feat(acp): select claude models from the provider's advertised list - #8257
Conversation
Design Review (Fable 5) — 🟡 CONCERNSDesign-level review of Design-Verdict: CONCERNS Sound root-cause fix, but the "one-edit opt-in" claim is half-true and the spec this PR obsoletes ships unchanged. Watch
Suggestions
[DESIGN-REVIEWED] 57a5a18 |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All counts are run; I have what I need. Final review follows. First-Principles-Verdict: CONCERNS The fix is real and cause-level, but the "one-edit opt-in" story the two new capability sets sell is contradicted by the diff's own spawn path. What this change shipsIntent: FIX — picking Opus 4.8 sometimes silently ran at a 200K window instead of 1M.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 57a5a18 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
748f47a to
ed88510
Compare
|
**rebutted** — span=0dfda262228b (`src/kiro_crew/acp/client.py` — adapter persistence on the Kiro construction path)
The awaited persist is already gated: on The remaining objection is to the presence of the membership
Removing the branch entirely would require extracting the shared new-session/resume block into a member-only override — a redesign wider than the problem, for zero behavioral change. Per the two-question rule this is legitimate-but-disproportional, so the code stays as-is.
|
ed88510 to
ca45ac3
Compare
|
**fixed** — span=e713faf8c271 (`src/kiro_crew/model_registry.py` — cold-cache fallback comment contradicted the dedup)
The docstring claimed the cold-cache fallback returns "the same list shipped before this cache existed, so behaviour is unchanged", which contradicts the
Fixed in |
Unify model selection so the context window follows the model the user
picked, not a stale registry guess — and make the warm-pool re-apply
provider-agnostic so any future provider inherits the fix.
Problem
- Selecting Opus 4.8 sometimes left the session on the 200K window instead
of 1M. Two distinct causes, both silent:
(A) the settings.local.json allowlist carried both the `[1m]` id and its
200K base sibling; the claude-agent-acp adapter dedups by base name and
could resolve to the 200K spelling.
(B) the warm pool seeds settings.local.json at spawn with the pool default
model; on claim, set_model switched the wire model but never re-seeded
the file, so the stale spawn-time window stuck.
What changed
- model_registry: add `_dedup_window_siblings`, dropping a base-window id when
a 1M sibling with the same advertised key is present (order-preserving,
also drops exact dupes). `seed_available_models` routes both the advertised
(warm) list and the static (cold) fallback through it.
- acp_backends: introduce two opt-in capability frozensets —
`ACP_BACKENDS_ADVERTISED_MODEL_SELECTION` (feed the advertised cache + fold
the wire id at spawn and on set_model) and `ACP_BACKENDS_SEED_LOCAL_SETTINGS`
(re-seed settings.local.json on a warm claim) — plus
`model_registry_namespace(backend)` mapping every known backend to its
registry index key. Claude is the sole member today; a future provider opts
in with one edit, never a `not is_claude` inference (harness-parity H5/H6).
- acp/client: `set_model` folds the bare id onto the advertised spelling and
re-seeds the settings file (both gated on membership), `_capture_available_models`,
the `_spawn` fold, and `_write_claude_local_settings` all read the capability
sets + `_model_registry_namespace` instead of `_is_claude`/"claude_code".
- acp/types re-exports the new names for existing importers.
Tests
- model_registry: dedup drops the base-window sibling of a 1M id, dedups the
advertised list too, preserves order, drops exact dupes, keeps distinct base
models; cold-cache fallback compares against the deduped list.
- acp client: set_model folds the bare id onto the advertised spelling,
re-seeds settings on claude, and a non-member backend (codex) neither folds
nor re-seeds.
- capability-sets leaf test pins the two new members and the namespace map for
every known backend.
ca45ac3 to
57a5a18
Compare
chenmingwei23
left a comment
There was a problem hiding this comment.
Approved: PR Readiness green on this head, all check-runs pass on best-per-name.
Open PR relationship auditThis is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion. Relationship findings
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
Problem / Motivation
Selecting Opus 4.8 sometimes left a session pinned to the 200K context window instead of the model's 1M window — silently, with no UX signal. Two independent, intermittent causes:
settings.local.jsonallowlist carried both the versioned...claude-opus-4-8[1m]id and its bare 200K base sibling...claude-opus-4-8. The claude-agent-acp adapter dedupsavailableModelsby base name and could resolve to the 200K spelling.settings.local.jsonat spawn with the pool's default model. On claim,set_modelswitched the model on the wire but never re-seeded the file, so the spawn-time window stuck.Why it matters
The context window is the single most consequential per-session setting. A silent halving to 200K truncates long sessions with no indication the selected model's full window was lost — the user picks 4.8 (1M) and unknowingly runs at a fifth of the capacity.
What changed (motivation → approach → change)
Root cause was that model selection was reconstructed from the static registry rather than reconciled with what the provider actually advertises, and the warm-pool re-apply was claude-special-cased at the call site.
model_registry— add_dedup_window_siblings: drop a base-window id when a 1M sibling with the same advertised key is present (order-preserving; also drops exact dupes).seed_available_modelsroutes both the advertised (warm) list and the static (cold) fallback through it, so the seeded allowlist can no longer carry both spellings (fixes A).acp_backends— introduce two opt-in capability frozensets,ACP_BACKENDS_ADVERTISED_MODEL_SELECTION(feed the advertised cache + fold the wire id at spawn and onset_model) andACP_BACKENDS_SEED_LOCAL_SETTINGS(re-seedsettings.local.jsonon a warm claim, fixing B), plusmodel_registry_namespace(backend)mapping every known backend to its registry index key. Claude is the sole member today; a future provider opts in with one edit rather than anot is_claudeinference (harness-parity H5/H6).acp/client—set_modelfolds the bare id onto the advertised spelling and re-seeds the settings file (both gated on membership);_capture_available_models, the_spawnfold, and_write_claude_local_settingsread the capability sets +_model_registry_namespaceinstead of_is_claude/"claude_code".acp/types— re-export the new names for existing importers.Net effect for non-claude backends is zero behavior change (the added init-path call early-returns unless the membership-gated capture flagged a change).
Tests
test_model_registry— dedup drops the base-window sibling of a 1M id, dedups the advertised list too, preserves order, drops exact dupes, keeps distinct base models; cold-cache fallback compares against the deduped list.test_acp_client_more_coverage—set_modelfolds the bare id onto the advertised spelling, re-seeds settings on claude, and a non-member backend (codex) neither folds nor re-seeds.test_acp_capability_sets_leaf— pins the two new members and themodel_registry_namespacemap for every known backend, so opting a harness in stays a visible, deliberate edit.test_acp_session_mcp— updated the settings-seed assertion to compare against the dedupedseed_available_models(the source the seed now uses), locking in that the written allowlist no longer carries the 200K base sibling.Manual verification
N/A — unit coverage sufficient. The seed/fold/re-seed paths are asserted directly (settings file contents, wire id folding, membership gating); no integration or external service is involved.
Related Issues
N/A — no tracked issue; found and fixed while unifying provider-advertised model selection.
Pattern harvest
Pattern: a silent capability downgrade from a stored-vs-served spelling gap (a versioned
[1m]id and its bare base sibling both present, dedup-by-base picking the weaker one) combined with a spawn-time seed never re-applied on mutation. Rule candidate (review-prompt / agents-md): a per-session settings artifact seeded at spawn must be re-seeded on any wire-level mutation of the same field, and an allowlist must not carry two spellings that a downstream dedup can collapse to the weaker one.Checklist