CB-604: reject an unrecognized profile kind at config load #105

Closed
agent wants to merge 0 commits from worker/cb604-1445f8-24 into main
Member

Fixes #102.

A profile's kind: was lower-cased and compared against only the single string "opencode" — anything else, including a typo like kind: opencod, silently fell into the claude-code adapter bucket. With argv: also unset, the argv default only special-cases the exact string "claude-code", so the launch command fell back to List.of(kind) — the daemon then tried to run a program literally named opencod.

Fix: added BridgedConfig.rejectUnknownKind(yaml), called from load() alongside the other raw-YAML reject* checks (same pattern as rejectNegativeMaxLoad). Any profile whose kind: is non-blank and not one of the known kinds (claude-code, opencode, case-insensitive) now throws IllegalStateException at config load, naming the profile, the bad value, and the accepted set.

Preserved: absent/blank kind: still defaults to claude-code; mixed-case (OpenCode) still resolves to the opencode adapter; no adapter/CompositePeerLauncher wiring touched.

Tests added (BridgedConfigTest): rejection naming the value + accepted set, blank-kind still defaults, plus the two pre-existing tests already covering absent-kind-defaults and mixed-case-normalizes.

Criterion 5 audit — same shape (lower-cased in a compact constructor, compared against exactly one string, no membership check against a known set) found in two other places, NOT fixed here per the ticket:

  • BridgedConfig.Auth.mode (lower-cased at construction, compared only against MODE_TOKEN in tokenMode()) — an unrecognized auth.mode (e.g. a typo of token) silently behaves as loopback-trust with no error, as long as the bind is loopback.
  • BridgedConfig.Profile.placement (the per-profile tab/pane field, lower-cased at construction, compared only against "tab" in tabPlacement()) — an unrecognized value (e.g. placement: tba) silently falls back to legacy pane placement with no error.

Note the top-level placement: field (fixed/round-robin/weighted) is a different field and IS already validated — PlacementPolicies.fromName throws on an unknown name (lazily, on first spawn attempt, not at config load).

mvn -f bridged/pom.xml clean install, unpiped: Tests run: 832, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.

Fixes #102. A profile's `kind:` was lower-cased and compared against only the single string `"opencode"` — anything else, including a typo like `kind: opencod`, silently fell into the claude-code adapter bucket. With `argv:` also unset, the argv default only special-cases the exact string `"claude-code"`, so the launch command fell back to `List.of(kind)` — the daemon then tried to run a program literally named `opencod`. **Fix**: added `BridgedConfig.rejectUnknownKind(yaml)`, called from `load()` alongside the other raw-YAML `reject*` checks (same pattern as `rejectNegativeMaxLoad`). Any profile whose `kind:` is non-blank and not one of the known kinds (`claude-code`, `opencode`, case-insensitive) now throws `IllegalStateException` at config load, naming the profile, the bad value, and the accepted set. Preserved: absent/blank `kind:` still defaults to `claude-code`; mixed-case (`OpenCode`) still resolves to the opencode adapter; no adapter/CompositePeerLauncher wiring touched. **Tests added** (`BridgedConfigTest`): rejection naming the value + accepted set, blank-kind still defaults, plus the two pre-existing tests already covering absent-kind-defaults and mixed-case-normalizes. **Criterion 5 audit** — same shape (lower-cased in a compact constructor, compared against exactly one string, no membership check against a known set) found in two other places, NOT fixed here per the ticket: - `BridgedConfig.Auth.mode` (lower-cased at construction, compared only against `MODE_TOKEN` in `tokenMode()`) — an unrecognized `auth.mode` (e.g. a typo of `token`) silently behaves as `loopback-trust` with no error, as long as the bind is loopback. - `BridgedConfig.Profile.placement` (the per-profile `tab`/`pane` field, lower-cased at construction, compared only against `"tab"` in `tabPlacement()`) — an unrecognized value (e.g. `placement: tba`) silently falls back to legacy pane placement with no error. Note the top-level `placement:` field (`fixed`/`round-robin`/`weighted`) is a different field and IS already validated — `PlacementPolicies.fromName` throws on an unknown name (lazily, on first spawn attempt, not at config load). `mvn -f bridged/pom.xml clean install`, unpiped: `Tests run: 832, Failures: 0, Errors: 0, Skipped: 0` — BUILD SUCCESS.
agent added 1 commit 2026-08-16 18:37:43 +02:00
CB-604: reject an unrecognized profile kind at config load
CI / contract (pull_request) Successful in 53s
CI / build (pull_request) Successful in 1m10s
fe46311266
Owner

Merged locally into main and pushed as f5deaaf. Gitea cannot mark a locally merged PR as merged, so I am closing it by hand — merged, not rejected.

Verification is on #102.

Merged locally into `main` and pushed as `f5deaaf`. Gitea cannot mark a locally merged PR as merged, so I am closing it by hand — **merged, not rejected**. Verification is on #102.
ltms closed this pull request 2026-08-16 18:42:37 +02:00
Some checks are pending
CI / contract (pull_request) Successful in 53s
CI / build (pull_request) Successful in 1m10s

Pull request closed

Sign in to join this conversation.