Generic per-provider host config: -provider-bin/-provider-config maps replace claude-named config fields #78

Closed
opened 2026-07-09 21:45:54 +02:00 by dominik.polakovics · 1 comment

Context

internal/config carries claude-named host config: ClaudeBin/ClaudeConfig fields, -claude/-claude-config flags, LAB_CLAUDE_CONFIG env. Each new provider (#2 Codex, Gemini) would copy the pattern, growing config.go per provider and permanently violating the grep-clean-core spirit of #2's acceptance.

Decided in provider-seam grill (2026-07-09): one generic per-provider map surface; provider N+1 needs zero config.go changes.

Design

  • New generic surface in internal/config:
    • Repeatable flags -provider-bin id=path and -provider-config id=path, stored as map[string]string keyed by registered provider id.
    • Env forms LAB_PROVIDER_BIN_<ID> / LAB_PROVIDER_CONFIG_<ID> (id uppercased, dashes to underscores: claude-codeCLAUDE_CODE).
  • main hands each adapter its entries at construction; adapters keep their own defaults (PATH lookup for bin, ~/.claude.json-style default for config) when no entry exists.
  • Existing -claude, -claude-config, LAB_CLAUDE_CONFIG become documented deprecated aliases that write into the map's claude-code entries. Precedence: the explicit generic form wins over an alias; document it. The alias shim is the only provider-named remnant in config.go, marked deprecated.
  • Anything beyond bin + config-path stays adapter-owned (a provider reads its own extras); the generic surface does not grow per-provider knobs.

Acceptance

  • config.go is provider-name-free except the documented deprecated alias shim.
  • -help documents the generic flags; boot validation (existing pick/env precedence rules) applies to the map forms.
  • Tests: alias and generic forms both reach the adapter; generic wins over alias; unknown provider id in a flag is a boot error (catches typos).
  • Claude-code constructed via the map path end to end.
## Context `internal/config` carries claude-named host config: `ClaudeBin`/`ClaudeConfig` fields, `-claude`/`-claude-config` flags, `LAB_CLAUDE_CONFIG` env. Each new provider (#2 Codex, Gemini) would copy the pattern, growing config.go per provider and permanently violating the grep-clean-core spirit of #2's acceptance. Decided in provider-seam grill (2026-07-09): one generic per-provider map surface; provider N+1 needs zero config.go changes. ## Design - New generic surface in `internal/config`: - Repeatable flags `-provider-bin id=path` and `-provider-config id=path`, stored as `map[string]string` keyed by registered provider id. - Env forms `LAB_PROVIDER_BIN_<ID>` / `LAB_PROVIDER_CONFIG_<ID>` (id uppercased, dashes to underscores: `claude-code` → `CLAUDE_CODE`). - `main` hands each adapter its entries at construction; adapters keep their own defaults (PATH lookup for bin, `~/.claude.json`-style default for config) when no entry exists. - Existing `-claude`, `-claude-config`, `LAB_CLAUDE_CONFIG` become documented deprecated aliases that write into the map's `claude-code` entries. Precedence: the explicit generic form wins over an alias; document it. The alias shim is the only provider-named remnant in config.go, marked deprecated. - Anything beyond bin + config-path stays adapter-owned (a provider reads its own extras); the generic surface does not grow per-provider knobs. ## Acceptance - config.go is provider-name-free except the documented deprecated alias shim. - `-help` documents the generic flags; boot validation (existing pick/env precedence rules) applies to the map forms. - Tests: alias and generic forms both reach the adapter; generic wins over alias; unknown provider id in a flag is a boot error (catches typos). - Claude-code constructed via the map path end to end.
Author
Owner

This was generated by AI while landing a PR.

Landing audit — PR #83 (afk/78) → VERDICT: PASS

  • Conventions: title feat(config): … is Conventional Commits-clean; body carries a valid Closes #78.
  • Merge state: merges cleanly into origin/main (no conflicts). Branch was behind main; the merge pulls in unrelated changes with no overlap in the files this PR touches.
  • Diff review (correctness vs. issue #78 acceptance):
    • config.go is provider-name-free except the single marked aliasProviderID shim. ✓
    • Precedence verified in code: generic flag > generic env > alias flag > alias env; unknown provider id in a generic flag is a boot error listing the registered ids; duplicate id rejected. ✓
    • main.go degraded-boot guard home=="" && ProviderConfig[claude-code]=="" is semantically equivalent to the old ClaudeConfig=="" (HOME unset ∧ nothing supplied), including the HOME-unset-but-explicit-config edge — no regression. ✓
    • claudecode.New drops the two required-field checks and fills adapter-owned defaults (claude on PATH; LoginDir/.claude.json); LoginDir is still validated, so the join is safe. ✓
    • -help documents the generic flags with the aliases marked deprecated (pinned by TestUsageDocumentsGenericProviderFlags). ✓
    • nix/module.nix example updated to the generic --provider-bin claude-code=… form. ✓
  • Verification signal: CI required native check (ci.yml) gates the merge at forge level; the hermetic ci-nix.yml also triggers here (PR touches nix/module.nix). The lab token cannot read the combined status API, so I re-ran the Go gate locally on the merged tree: go build, go test, go vet, and gofmt -l all clean for internal/config, internal/provider/claudecode, cmd/lab (claudecode tests run against real git/tmux/prlimit).
  • Non-blocking nit: post-merge there will be two ADR-0034 files (0034-generic-per-provider-host-config.md here + 0034-per-provider-login-sessions.md landed on main after this PR was authored). The repo already tolerates duplicate ADR numbers (two 0033-* exist on main), so this is doc-hygiene only, not a blocker.

No blockers. Awaiting free-text merge confirmation.

> *This was generated by AI while landing a PR.* **Landing audit — PR #83 (`afk/78`) → VERDICT: PASS** - **Conventions:** title `feat(config): …` is Conventional Commits-clean; body carries a valid `Closes #78`. - **Merge state:** merges cleanly into `origin/main` (no conflicts). Branch was behind main; the merge pulls in unrelated changes with no overlap in the files this PR touches. - **Diff review (correctness vs. issue #78 acceptance):** - `config.go` is provider-name-free except the single marked `aliasProviderID` shim. ✓ - Precedence verified in code: generic flag > generic env > alias flag > alias env; unknown provider id in a generic flag is a boot error listing the registered ids; duplicate id rejected. ✓ - `main.go` degraded-boot guard `home=="" && ProviderConfig[claude-code]==""` is semantically equivalent to the old `ClaudeConfig==""` (HOME unset ∧ nothing supplied), including the HOME-unset-but-explicit-config edge — no regression. ✓ - `claudecode.New` drops the two required-field checks and fills adapter-owned defaults (`claude` on PATH; `LoginDir/.claude.json`); `LoginDir` is still validated, so the join is safe. ✓ - `-help` documents the generic flags with the aliases marked deprecated (pinned by `TestUsageDocumentsGenericProviderFlags`). ✓ - `nix/module.nix` example updated to the generic `--provider-bin claude-code=…` form. ✓ - **Verification signal:** CI required native check (`ci.yml`) gates the merge at forge level; the hermetic `ci-nix.yml` also triggers here (PR touches `nix/module.nix`). The lab token cannot read the combined status API, so I re-ran the Go gate locally on the merged tree: `go build`, `go test`, `go vet`, and `gofmt -l` all clean for `internal/config`, `internal/provider/claudecode`, `cmd/lab` (claudecode tests run against real git/tmux/prlimit). - **Non-blocking nit:** post-merge there will be two ADR-0034 files (`0034-generic-per-provider-host-config.md` here + `0034-per-provider-login-sessions.md` landed on main after this PR was authored). The repo already tolerates duplicate ADR numbers (two `0033-*` exist on main), so this is doc-hygiene only, not a blocker. No blockers. Awaiting free-text merge confirmation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab#78
No description provided.