Skip to content

feat(acp): add OpenCode harness - #6777

Open
knowlet wants to merge 7 commits into
kirodotdev:mainfrom
knowlet:feat/add-opencode-acp-support
Open

feat(acp): add OpenCode harness#6777
knowlet wants to merge 7 commits into
kirodotdev:mainfrom
knowlet:feat/add-opencode-acp-support

Conversation

@knowlet

@knowlet knowlet commented Aug 29, 2026

Copy link
Copy Markdown

Problem / Motivation

Kiro Crew is KiroACP-only (agent.provider = acp, harness selected via agent.acp_backend), but the only selectable harnesses in the public build were kiro (default) and kas. Users without a signed-in kiro-cli are blocked by the full-screen KiroPrerequisiteGate (“Set up Kiro”) and cannot reach Developer → Agent Backend, so there is no way to switch to an alternative harness. The dashboard’s GET /api/models also unconditionally spawns kiro chat --list-models, so an OpenCode session’s provider-qualified model IDs (opencode/big-pickle, sakana/fugu) never appear in the picker.

Issue #6622 tracks the orthogonal governance question (“what may this deployment select”) that the registry from #6593 (“what can this build serve”) left open. Adding a third selectable harness makes that governance scope necessary and provides a concrete harness to bound.

Why it matters

  • UX: A first-run user who wants OpenCode (or any non-Kiro harness) currently sees only “Sign in to Kiro” with no escape hatch; the gate hides the backend switcher that would unblock them.
  • Model isolation: OpenCode advertises its catalog via ACP configOptions (provider-qualified IDs). Falling through to the Kiro catalog shows Kiro model IDs that opencode acp will reject (AcpModelUnavailable) and hides the models the backend can actually run.
  • Fleet governance: Every other governable surface has a SCOPE_CATALOG row; agent_backend had none, so an enterprise cannot say “only run the harness we qualified” (Add an agent_backend governance scope to bound which ACP harness a fleet may select #6622).

If left undone, OpenCode users must maintain a Kiro login they never use, and fleet operators cannot bound the harness set.

What changed (motivation → approach → change)

Motivation → Keep Kiro as the unconditional first-class harness; any new harness must adapt to existing seams, not generalize the Kiro path (harness-parity H5–H13). A harness that cannot land without changing the Kiro construction path must not land.

Approach → Add OpenCode as an AcpProvider backend at the acp_backend seam (not a second agent.provider value), keep all capability membership positive/opt-in, and make the dashboard backend-aware at the same registry that feeds the PATCH allowlist and config schema. Bypass Kiro-specific gates only when acp_backend == "opencode" (positive check, never not is_kiro).

Change →

  • Transport (feat(acp): add opencode transport): ACP_BACKEND_OPENCODE = "opencode" in acp_backends.py (leaf, re-exported by acp/types.py), BASELINE_SELECTABLE_BACKENDS += opencode, _resolve_opencode_bin (OPENCODE_BIN / mise which / ~/.opencode/bin / PATH), PROTOCOL_VERSION_OPENCODE = 1, per-session AcpClient (excluded from ACP_BACKENDS_SESSION_SHARING/STEER/INTERNAL_SANDBOX/ACP_RUNTIME/KIRO_IDENTITY_STORE), provider_label = "opencode" (fix(session): read agent specs through the hardened, size-capped gate #5423-style hardened materialized_agent_spec).
  • MCP (feat(acp): integrate opencode mcp tools): Portable projection via managed_session_servers (only MANAGED_MCP_SERVER_NAMES with whole @server exposure, pooled-stub replacement), sanitized wire-key sanitize(server)+_+sanitize(tool) with fail-closed mcp_identity_trusted, rawInput accepted.
  • Model/config (feat(dashboard): expose opencode acp backend + feat(acp): polish ...): AgentBackendTab.tsx 4th row backed by selectable_backend_values(), en.json + tests; GET /api/modelsacp_backend == "opencode"_advertised_opencode_models (ACP configOptions) + cold CLI fallback opencode models (1M cap, ^[^\s/]+/[^\s]+$, dedup, auto sentinel), never spawns kiro-cli for OpenCode; GET /api/sessions/usage and GET /api/kiro-prerequisite bypass via selected_backend_uses_kiro_identity() (ACP_BACKENDS_KIRO_IDENTITY_STORE).
  • Gate (fix(dashboard): allow opencode to bypass kiro setup gate): KiroPrerequisiteGate queries acp_backend; opencode renders children directly, otherwise shows Or use OpenCode → Continue with OpenCode (patchConfig('agent.acp_backend','opencode'), opencode.ai/docs link, opencode auth login copy block, private-error redaction); backend api_kiro_prerequisite_status mirrors with _opencode_ready_snapshot + asyncio.to_thread(KiroCrewConfig.load).

Tests

  • Backend unittest_acp_backend_opencode.py (16 cases): spawn is opencode acp --cwd, protocol 1, configOptions model capture, model switch + AcpModelUnavailable on unadvertised id, load without modes, MCP projection keeps only fully exposed managed servers, pooled-stub replacement, rawInput permission recovery, collision/unknown/overlap fail-closed, _advertised_opencode_models isolation; test_acp_backend_kas predicate exactly one holds now includes opencode.
  • Runtimetest_acp_runtime.py::test_every_session_request_builder_consults_pooled_servers updated to assert backend-aware helper still reaches pooled path; 299 acp-runtime tests green.
  • Dashboard gatestest_agent_backend_editable (9), test_harness_parity (27), KiroPrerequisiteGate.test.tsx (adds switches to OpenCode + keeps visible on failure + bypass when opencode), AgentBackendTab.test.tsx (4th backend offers all four + reflects persisted OpenCode + saves public OpenCode).
  • APItest_api_models_entitlement / retry still green; HARNESS_BASE_REF=origin/main python scripts/check_harness_parity.py ✓, scripts/docs-lint.sh ✓, black ✓.

Manual verification

  • opencode 1.18.23 + SAKANA_API_KEY on macOS gateway with KIROCREW_HOME=$(mktemp -d): AcpClient(backend=opencode).ensure_ready()ses_*, available_models 10x sakana/* + opencode/*, stream_events("hi")Hi/end_turn, ls -la tool-call → tool_resultempty dir response.
  • Gateway KiroPrerequisiteGate bypass: with acp_backend="" shows “Set up Kiro” + new “Or use OpenCode” card; clicking Continue with OpenCode patches config and gate disappears; with acp_backend="opencode" gate never appears even when kiro-cli unauthenticated, GET /api/kiro-prerequisite returns ready: true (SHA-pinned probe not spawned).

Screenshots / video

OpenCode session running as opencode/big-pickle (live test on this branch, sakana/fugu provider available):

OpenCode demo

Full dashboard context

Same prompt via the Kiro path still uses kiro-cli; footer now correctly shows the active harness’s model (opencode/big-pickle).

Related Issues

Related to #6622 — Adds the third selectable harness that the future agent_backend governance scope will bound. No new SCOPE_CATALOG row yet; the allow-vs-exclusively / H1-H3 / installed_context() recursion discussion from #6622 remains open. The helper selected_backend_uses_kiro_identity() introduced here is the resolution the future scope can reuse at provider_factory, PATCH /api/config/kirocrew, and GET /api/config/schema.

Checklist

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...) — N/A — this feature is intentionally split into 5 logical phases (feat(acp): add opencode transport, feat(acp): integrate opencode mcp tools, feat(dashboard): expose opencode acp backend, fix(dashboard): allow opencode to bypass kiro setup gate, feat(acp): polish opencode model fallback and unified kiro gate) per the task spec “Conventional Commit after each logical phase”. Squashing would hide the harness-parity boundaries.
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated ( README.md, docs/system-specs/modules/acp-client.md, docs/system-specs/modules/providers.md, docs/architecture/mcp.md, harness-parity.md )
  • No secrets, credentials, or internal references in the diff

Contribution License Agreement

- add native OpenCode ACP spawn, resume, and model handling
- preserve Kiro-only capability and sandbox boundaries
- pin backend selection and protocol behavior with tests
- project only fully exposed managed MCP servers into OpenCode sessions
- use portable ACP local-server fields and pooled stub replacement
- recover trusted MCP identity from sanitized server prefix with fail-closed collisions
- accept rawInput from OpenCode permission payloads for display and deny checks
- route session/new and session/load through backend-aware server helper
- update MCP and ACP client specs alongside behavior
- add OpenCode to AgentBackendTab picker with server-derived enablement
- route /api/models to advertised OpenCode configOptions when acp_backend is opencode
- never fall through to kiro catalog for opencode model namespaces
- document backend selection and model routing in README
- frontend gate bypasses entirely when acp_backend is opencode and shows switch button for kiro-blocked users
- backend /api/kiro-prerequisite returns ready when opencode is selected so CLI and non-owner checks also pass
- route /api/models to advertised OpenCode session first, then cold CLI fallback via opencode models with strict id validation and size cap
- centralize Kiro-identity checks behind selected_backend_uses_kiro_identity so usage/kiro-prerequisite and sessions bypass cleanly for OpenCode
- expose acp_backend on providers via positive identity, let _advertised_opencode_models filter on harness id
- harden KiroPrerequisiteGate switch: async invalidation, external doc link and private-error redaction
@knowlet
knowlet requested a review from a team August 29, 2026 12:12
@knowlet
knowlet requested a review from a team as a code owner August 29, 2026 12:12
@knowlet
knowlet requested a review from smeyffret August 29, 2026 12:12
@github-actions github-actions Bot added fork Pull request from a fork (external contributor) readiness: action required A blocking check or review needs attention labels Aug 29, 2026
@github-actions github-actions Bot added the merge conflict Branch has merge conflicts with its base — author must resolve before merge label Aug 29, 2026
@bolichen97
bolichen97 enabled auto-merge August 29, 2026 23:49
@NicholasRBowers NicholasRBowers added the needs-pr-triage PR scanner: awaiting automated triage label Sep 1, 2026
@chenmingwei23

Copy link
Copy Markdown
Contributor

Kiro Crew [operator: chenmingwei23#de330d0c]: This PR has been inactive for 7+ days. I reviewed the blockers but they require your input:

  • Baseline-backend contract collision. The branch is CONFLICTING with main; the rebase conflicts on 6 files, and the central one is a design decision, not mechanical base drift. main now ships BASELINE_SELECTABLE_BACKENDS = {kiro, claude, kas} and deliberately made ACP_BACKEND_CLAUDE a live, selectable public harness (documented rationale plus a test asserting it). This branch was written against the opposite premise: it removes claude and adds opencode, giving {kiro, kas, opencode}. Resolving the conflict requires deciding the merged contract -- should the baseline be the union {kiro, claude, kas, opencode}, or does adding OpenCode intentionally interact with main's CLAUDE-selectability decision? src/kiro_crew/acp/client.py carries the same collision: main reworked the _is_claude seam into a live public path, while this branch reworked the same seam for independently spawned adapted backends.
  • Governance scope. The description ties this to open questions Add an agent_backend governance scope to bound which ACP harness a fleet may select #6622 (what a deployment may select) and feat(config): one registry for selectable ACP backends instead of three literals #6593 (what a build can serve). Whether the agent_backend SCOPE_CATALOG row should land here or wait on those is a maintainer call.

Once you have rebased onto current main and settled the baseline-backend contract, the pipeline will re-assess on its next cycle. If you would prefer no automation on this PR, add the pr-no-autofix label.

@chenmingwei23 chenmingwei23 added needs-author-decision PR blocked on author input and removed needs-pr-triage PR scanner: awaiting automated triage labels Sep 1, 2026

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description / code mismatch

Four claims in the Description are contradicted by the code on f04a43160: the harness-parity approach it promises is inverted in the diff, the parity-gate pass it reports cannot hold against that same diff, and the OpenCode setup link it describes references an i18n key that exists in no catalog.

1. Kiro-specific gates are bypassed with not is_kiro_backend, the exact form the Description promises never to use

The Description says

Bypass Kiro-specific gates only when acp_backend == "opencode" (positive check, never not is_kiro).

The code does — four added lines express harness identity negatively:

  • src/kiro_crew/providers/acp.py:1484not is_kiro_backend
  • src/kiro_crew/providers/acp.py:1208not is_claude_backend
  • src/kiro_crew/acp/client.py:2444 — a negative identity test the branch's own widened rule matches
  • src/kiro_crew/dashboard/handlers/agents.py:1081!= ACP_BACKEND_OPENCODE

Risk — H5 is the invariant this change is most directly governed by (AGENTS.md, Harness parity: "Identity is positive... Never a bare string literal, an inequality, or a negation"), and it fails toward the permissive answer: not is_kiro_backend and != ACP_BACKEND_OPENCODE silently hand a fifth harness whatever branch they guard, with nothing going red until an operator who never opted into that harness pays for it. scripts/check_harness_parity.py does pin all four — the negative-identity rule catches the three in providers/acp.py and acp/client.py, and the negative-constant rule (!=\s*ACP_BACKEND_[A-Z_]+) catches agents.py:1081. All four are added lines under SCAN_ROOTS = ("src/kiro_crew/",) with no harness-ok suppression and no exempt= entry, so the diff-scoped gate reports four H5 violations (see finding 2). The harness-parity.md H6 row this PR edits cites test_acp_backend_opencode.py::TestOpenCodeProviderBoundaries, which pins H6's capability-set exclusions and is not a waiver for H5.

providers/acp.py:1484 additionally changes existing KAS behaviour that the Description does not disclose: cleanup_session no longer deletes ~/.kiro/sessions/cli/<sid>.json* for the KAS backend, which previously reached that code.

Required change — rewrite all four as positive tests or named-set membership: self.is_kiro_backend, provider.acp_backend == ACP_BACKEND_OPENCODE, self._is_opencode. For providers/acp.py:1208, the positive restoration of the early-return guard is if self.is_acp_runtime_backend or self.is_opencode_backend: return, or invert the block onto if self.is_claude_backend: — not a bare negation of either. If the KAS cleanup skip is intended, state it in the Description and in docs/system-specs/modules/harness-parity.md.

2. The reported check_harness_parity.py pass cannot hold — the gate matches four added lines

The Description says

HARNESS_BASE_REF=origin/main python scripts/check_harness_parity.py

The code does — the gate matches the four lines listed in finding 1. Three of them fail under main's unmodified rules: dashboard/handlers/agents.py:1081 hits the untouched negative-constant rule, and providers/acp.py:1208 and providers/acp.py:1484 hit main's existing is_(?:kiro|claude|kas)_backend alternation. Only src/kiro_crew/acp/client.py:2444 depends on this branch's own widening of _HARNESS_LITERAL and negative-identity to include opencode — so the branch adds a line that its own tightening then catches.

Risk — the Harness Parity Gate job (.github/workflows/ci.yml:405-429, which exports HARNESS_BASE_REF from the PR's base.sha, runs --test, then the enforcing invocation) did not execute on this head SHA; the check-runs present are limited to the fork workflow guards, Fork PR Description, and readiness signals, which is a consequence of the fork plus the dirty merge-ref state rather than anything the author did. That leaves the reported ✓ as the only evidence a reviewer has for the invariant most central to this change, and it does not hold.

Required change — fix the four lines per finding 1, then re-run HARNESS_BASE_REF=origin/main python3 scripts/check_harness_parity.py and update the Tests section with the real result.

3. The OpenCode setup link has no catalog key, so it renders a raw dotted path and the PR's own new test cannot pass

The Description says

KiroPrerequisiteGate queries acp_backend; opencode renders children directly, otherwise shows Or use OpenCode → Continue with OpenCode (patchConfig('agent.acp_backend','opencode'), opencode.ai/docs link, opencode auth login copy block, private-error redaction)

The code doeswebsite/src/components/KiroPrerequisiteGate.tsx:1016 calls i18nT('components.kiroPrerequisiteGate.open_opencode_setup'), and that key is defined in no catalog, including en.json. Separately, the diff to this component is purely additive — the switchBackendMutation plus the card — and contains no acp_backend query and no early return children; the opencode bypass is served only by the backend returning ready: true, not by the component. The Description's third claimed new test, bypass when opencode, is absent from the test diff, which adds only switches to OpenCode and keeps first-run setup visible.

Risk — three concrete failures. (1) The opencode.ai/docs link renders as the literal text components.kiroPrerequisiteGate.open_opencode_setup on the first-run setup screen. (2) The new test switches to OpenCode from first-run setup and renders the dashboard opens with await screen.findByRole('link', { name: 'Open OpenCode setup' }), and website/integration/setup.ts — which is the setupFiles entry for the entire vitest project (website/vite.config.ts:591), not just integration tests — calls initI18n('en') against the real catalog, so that query never resolves. (3) frontend-lint runs npm run i18n:check (.github/workflows/ci.yml:1420), whose runner website/scripts/i18n-check.mjs spawns check-i18n-keys.mjs; its key-refs row is declared scope: 'repo', enforce: 'hard-zero' at website/scripts/lib/i18n-gate-table.mjs:111, and a t() naming a key that does not exist fails the step.

Required change — add open_opencode_setup under components.kiroPrerequisiteGate with the intended copy, then run cd website && npm run i18n:check and the vitest file. Either implement the component-side acp_backend query and early return children the Description describes, or correct the Description to say the bypass is backend-only, and either add the bypass when opencode test or drop it from the Tests section.

4. New English catalog keys with none of the shipped target catalogs updated

The Description says

AgentBackendTab.tsx 4th row backed by selectable_backend_values(), en.json + tests

The code does — six keys are added to the English catalog only: five under the hunk @@ -4349,7 +4349,12 @@ at website/src/i18n/locales/en.json:4353-4357 (line 4352 is the modified your_crew_is_almost_ready), plus "opencode": "OpenCode" at website/src/i18n/locales/en.json:6354. No target catalog is touched. A seventh key, components.kiroPrerequisiteGate.open_opencode_setup, is referenced but added to no catalog at all (finding 3).

Risk — this contradicts the checklist item "[x] Existing tests pass and new tests added for new functionality": frontend-test reds on the catalog-parity cases, which cover 12 non-en codes rather than 11, because en-XA is itself a SUPPORTED_LANGUAGES entry (devOnly) and the parity loop includes it. The pseudolocale check (gen-pseudolocale.mjs --check, another hard zero) derives en-XA.json from en, which is unchanged here, so it fails as well.

Required change — add the new keys, translated, to every target catalog; place hand-authored strings in en.manual.json rather than the generated en.json; regenerate en-XA.json; then run cd website && npm run i18n:check && npm run test.

@bolichen97

Copy link
Copy Markdown
Collaborator

Audit note — three open PRs are building the same ACP-harness seam

This is a consolidation request, not a duplicate finding: #5349, #6307 and #6777
all add a non-kiro-cli ACP harness at the same seams, and they disagree with each other in
ways that mean only one design can land. Each of the three also carries substantial work
the other two do not, so none of them should simply be closed.

What the audit verified by reading all three merge-base diffs:

  • feat: add Codex ACP backend #5349feat: add staged acp adapter admission #6307 — Verified on both cached heads that these implement one capability -- drive the operator-installed codex-acp adapter as a harness chosen at agent.acp_backend, with a dashboard picker -- at the same seams with mutually exclusive contracts. Both add ACP_BACKEND_CODEX and PROVIDER_LABEL_CODEX to acp/types.py, then disagree on ACP_BACKENDS_SELECTABLE (each pinned by its own test); both rewrite DefaultProviderRegistry.create_factory with incompatible bodies (feat: add Codex ACP backend #5349 an if cfg.agent.acp_backend == ACP_BACKEND_CODEX branch, feat: add staged acp adapter admission #6307 _create_adapter_dispatch_factory on Dialect.KIRO/SPEC); both add SessionManager.acp_backend with contradictory semantics (restart-only snapshot that writes cfg.agent.acp_backend back, vs a live read plus live_harness); both add the same unlanded kiro-readiness bypass so a foreign-adapter operator is not told to sign into kiro-cli; both add codex adapter/sign-in rows to doctor; and both ship a harness selector carrying the same 'new chats only' disclosure (aiBackendPanel.* vs acpBackend.*). Since the earlier rounds ran, main landed the plumbing itself: feat(config): one registry for selectable ACP backends instead of three literals #6593 added acp_backends.py (BASELINE_SELECTABLE_BACKENDS, register_selectable_backend, one selectability gate) and feat(acp): add a dormant Codex ACP backend seam and name the harness-onboarding sequence #7813 added the dormant codex seam (_is_codex, PROTOCOL_VERSION_CODEX, _resolve_codex_acp_bin, a _spawn branch, _codex_session_mcp_servers, plus ACP_BACKENDS_EFFORT_VIA_CONFIG_OPTION / _MODEL_VIA_CONFIG_OPTION / _KIRO_SLASH_COMMANDS replacing the is_claude_backend negations), with codex known but withheld from the baseline. That landed shape is feat: add staged acp adapter admission #6307's -- in-place dispatch, membership sets, one gate, a create_factory documented to register nothing -- and it contradicts each of feat: add Codex ACP backend #5349's three signature choices: the create_factory special case (its standing, non-overridable BLOCK-MERGE), the CodexAcpClient spawn-path fork, and the restart-only snapshot (main computes _bg_runtime_backends() per call). Neither PR closes either documented admission blocker: feat: add Codex ACP backend #5349 has no tool gate or read routing at all, and neither touches agent_sdk/backend_install.py, the exact gap landed main names as the reason codex is not selectable. So only one design can land and feat: add staged acp adapter admission #6307 is the base -- but it is stale against main's landed seam and still carries an unaddressed CHANGES_REQUESTED whose item 3 asks for Codex in the first admitted set, which is what feat: add Codex ACP backend #5349's remainder feeds.

  • feat: add staged acp adapter admission #6307feat(acp): add OpenCode harness #6777 — Both PRs are still OPEN (the issue/PR reference check), so neither can have superseded the other. Reading the two merge-base diffs directly, I confirmed nine collisions inside the SAME functions, not merely the same files: build_permission_event's two rawInput fallback chains are byte-identical additions on both sides; api_sessions_usage gains the identical return web.json_response({"usage": {"available": False}}) at the same position ahead of reject_if_kiro_unverified; api_kiro_prerequisite_status gains the same asdict(PrerequisiteStatus(...)) + legacy_idle_operation() pre-probe bypass; reject_if_kiro_unverified is scoped off the same pre-existing ACP_BACKENDS_KIRO_IDENTITY_STORE set by two mutually exclusive shapes (inline live-slot resolution vs. a new selected_backend_uses_kiro_identity() helper plus a keyword on the function); AcpClient._spawn gains an OpenCode arm and protocol 1 twice by incompatible schemes (PROTOCOL_VERSION_CLAUDE renamed to PROTOCOL_VERSION_SPEC and mapped per-Dialect vs. a new PROTOCOL_VERSION_OPENCODE and a third elif); LLMProvider gains one new property under two names (backend vs acp_backend) with the same None default and the same rationale, and AcpProvider.is_opencode_backend is added twice with identical bodies; and the managed-MCP projection onto an adapted harness is implemented twice, in acp/spec_servers.py and mcp_gateway/session_servers.py, both reduced to (name, command, args, env) with the same user-server-exclusion rationale. test/test_harness_parity.py carries directly opposed edits. Only one design can own each of those seams, so a consolidation decision is owed. the first adjudication's nomination of feat: add staged acp adapter admission #6307 as survivor is nevertheless wrong: feat: add staged acp adapter admission #6307 pins ACP_BACKENDS_SELECTABLE == frozenset({KIRO, KAS}) with an equality assertion and its own docs row reads | OpenCode | opencode | opencode auth login | Described, not selectable |, and selectable_ids() returns that frozen set, so "opencode" can never be persisted and every non-Kiro branch it adds (models, usage, prerequisite, readiness) is unreachable code at its HEAD. Current state cuts under both: acp_backends.py landed between the two merge bases (absent at e48ea42, present at 588ae92), main now ships BASELINE_SELECTABLE_BACKENDS = {kiro, claude, kas} with claude deliberately selectable, and both branches rewrite that same line in opposite directions - feat: add staged acp adapter admission #6307 to {kiro, kas}, feat(acp): add OpenCode harness #6777 to {kiro, kas, opencode} - which is the one decision the consolidation has to make.

Why this needs a decision rather than a merge order

The three collide inside the same functions, not merely in the same files, and each pins its
own answer with its own test — so whichever lands second does not conflict textually so much as
contradict a test the first one added. In particular the ACP_BACKENDS_SELECTABLE / selectable-set
question is answered three different ways.

Two things must not be lost whichever design wins

From #5349, carry before consolidating: (1) website/src/pages/settings/AiBackendPanel.tsx plus website/src/test/AiBackendPanel.test.tsx -- the Settings -> System -> AI Backend picker with per-backend descriptions and the restart/new-chat disclosure (aiBackendPanel.restart_new_chats_description), together with its command-palette wiring in website/src/components/commandPalette/settingsRegistry.gen.ts and settingsTabLabel.ts; this is closer to the maintainer's 'the provider switch has its own page, keep consistent with the internal version' than #6307's preview-gated Developer -> ACP Adapters tab (AcpBackendCard.tsx). (2) src/kiro_crew/dashboard/kiro_readiness.py::running_backend_requires_kiro_prerequisite -- keep its fail-closed rule (an absent or unknown backend still demands kiro readiness) as the behaviour #6307's live resolver must preserve. (3) src/kiro_crew/cli_doctor.py's actionable codex rows ('Codex ACP adapter not found', Fix: npm i -g @agentclientprotocol/codex-acp), folded into acp/doctor.py::_report_adapter, and reused as the basis for the missing agent_sdk/backend_install.py codex probe that landed main names as codex's selectability blocker. (4) From test/test_acp_backend_codex.py, the design-independent cases only: codex advertised-model gating, session/new MCP descriptor wiring, session resume, and the auth-required / readiness boundary. Do NOT carry CodexAcpClient, CodexAcpProvider / create_codex_provider_factory, the create_factory codex branch, the ACP_BACKENDS_SELECTABLE edit, CODEX_EFFORT_CONFIG_ID = 'reasoning_effort', or the restart-only SessionManager.acp_backend snapshot -- each is superseded by, or contradicted by, landed main.

One security-relevant note

Nothing needs harvesting for a closure - neither PR is closed. For the consolidation, two items must not be lost whichever design wins. From #6307: src/kiro_crew/acp/opencode.py's ensure_routed_settings() with PERMISSION_ASK / PERMISSION_BYPASS_VALUES, which writes permission: "ask" into the session work_dir (opencode.json or .opencode/opencode.json, never ~/.config/opencode) and reads it back, because OpenCode's own default is permissive so an unseeded work_dir emits no session/request_permission and privileged tool calls never reach Crew's PreToolUse gate - I grepped #6777's whole diff and it has no counterpart (0 hits for opencode.json), so OpenCode must not become selectable without this. From #6777: AcpClient.opencode_mcp_identity / remember_opencode_mcp_servers, which reverse OpenCode's sanitize(server)+''+sanitize(tool) wire key with fail-closed prefix-collision handling (#6307's parser handles only the mcp___ shape); website/src/components/KiroPrerequisiteGate.tsx's switchBackendMutation escape hatch plus its five en.json keys and 52 test lines, the only first-run path a kiro-cli-less operator can reach; the AgentBackendTab.tsx fourth row; _cold_opencode_models (opencode models, 1 MB cap, ^[^\s/]+/[^\s]+$, dedup) so the picker fills before any session exists; agent.py's MANAGED_MCP_SERVER_NAMES + materialized_agent_spec capped reader; the opencode addition to scripts/check_harness_parity.py's HARNESS_LITERAL; and the live evidence in the PR body (opencode 1.18.23, real ses* session, stream_events -> end_turn) that is precisely what maintainer iamwhatever's CHANGES_REQUESTED point 3 asked for before any adapter is admitted.

Suggested next step: a maintainer picks the seam design (the registry-dispatch shape vs. the
per-backend branch shape), then the other two rebase onto it as harness additions rather than
as competing seams — which is also what the harness-parity rule in AGENTS.md asks for
("an added harness ADAPTS, it does not widen").


From a repository-wide duplicate/overlap audit of every pull request open against main (2026-09-02, 330 PRs, one reviewer per PR). Each PR was read as its full merge-base diff plus its description and every comment and review, then compared against each candidate PR's own diff and against origin/main at 1a765b88ceb7. This PR is not being closed — the note is informational. If the reading is wrong, please correct the reasoning rather than just the conclusion.

@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This 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

  • PR #5349 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #5349: MERGE_DISCUSSION. Adding a second non-Kiro harness at the same seams in a different shape; the shared scaffolding (readiness scoping, usage endpoint, managed-MCP projection, protocol dispatch, harness-parity tests) must be owned once. Files: src/kiro_crew/dashboard/kiro_readiness.py, src/kiro_crew/dashboard/handlers/sessions.py, src/kiro_crew/mcp_gateway/session_servers.py, src/kiro_crew/acp/client.py, test/test_harness_parity.py.
  • This PR is OVERLAPPING with PR #6307. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #6777: MERGE_DISCUSSION. Both are open, so neither supersedes the other, and they collide inside the same functions with contradictory contracts (selectability, permission-event parsing, readiness scoping, the ABC property name, the managed-MCP projection module). PR #6307 is broader (registry-driven staged admission across five adapters plus a routing verdict) and deliberately withholds OpenCode selectability; PR #6777 ships OpenCode selectable with material work PR #6307 lacks (the sanitized _ identity reversal with collision fail-closed, the first-run escape hatch, the cold opencode models picker). A maintainer has to pick the seam design before either lands; PR #6777's OpenCode-specific pieces should then be re-landed on top of it, and its permission-routi… Files: src/kiro_crew/acp_backends.py, src/kiro_crew/acp/_dispatch.py, src/kiro_crew/dashboard/kiro_readiness.py, src/kiro_crew/dashboard/handlers/kiro_prerequisite.py (+3 more). The two independent directions used different labels; the matrix conservatively retains OVERLAPPING for coordination.
  • This PR is OVERLAPPING with PR #7963. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #6777: MERGE_DISCUSSION. Textual collision on the baseline-selectable statement, plus a policy dependency: PR #7963 defines what a harness must demonstrate before it becomes selectable, and PR #6777 admits OpenCode with no routing verdict at all. Files: src/kiro_crew/acp_backends.py, src/kiro_crew/acp_tool_gate.py.
  • This PR is OVERLAPPING with PR #8255. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #6777: MERGE_DISCUSSION. PR #8255 rewrites the selection seam PR #6777 extends. If it lands first, OpenCode becomes an operator/bundled descriptor rather than an id plus branches, and most of PR #6777's provider and client conditionals no longer have a place to live. Files: src/kiro_crew/acp/types.py, scripts/check_harness_parity.py.
  • This PR is OVERLAPPING with PR #8257. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #6777: MERGE_DISCUSSION. Not a competing design so much as a style the landed seam has already moved to: PR #6777's (self._is_kiro or self._is_opencode) guards should become membership in the capability sets main already ships (ACP_BACKENDS_MODEL_VIA_CONFIG_OPTION and friends) that PR #8257 extends further. Files: src/kiro_crew/acp/client.py.
  • This PR is OVERLAPPING with PR #8319. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #6777: MERGE_DISCUSSION. Shared file and shared membership-set convention, nothing shared in behaviour; at most a trivial merge in the same region. Files: src/kiro_crew/acp_backends.py. The two independent directions used different labels; the matrix conservatively retains OVERLAPPING for coordination.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

@bolichen97

Copy link
Copy Markdown
Collaborator

@knowlet Thanks for this, and sorry for the long wait. OpenCode is still absent from main, so the feature itself is uncovered. What has landed since you opened this is the generalized owner of nearly every seam this PR builds for itself: the per-session MCP array is now a capability set plus src/kiro_crew/acp/session_mcp.py and a cached AcpClient._resolve_session_mcp_servers; configOptions model synthesis landed in src/kiro_crew/acp/client.py via #9165; src/kiro_crew/acp/_dispatch.py resolves rawInput/input/params by first present key; Kiro-identity scoping is now the uses_kiro_identity_store provider property; per-backend install probes live in src/kiro_crew/agent_sdk/backend_install.py. #7963 also raised the bar, since acp_tool_gate.routing_verdict requires a harness's tool calls to be routed before it may be selectable.

Still unique to this PR: the sanitized <server>_<tool> identity reversal with fail-closed collision handling, the managed-MCP projection in src/kiro_crew/mcp_gateway/session_servers.py, _cold_opencode_models and _advertised_opencode_models, the first-run gate escape hatch, and the Agent Backend row.

Please rebase and narrow to that remaining scope. Two blockers regardless: components.kiroPrerequisiteGate.open_opencode_setup is referenced but added to no catalog, and not self.is_kiro_backend in src/kiro_crew/providers/acp.py plus != ACP_BACKEND_OPENCODE in src/kiro_crew/dashboard/handlers/agents.py are negative-identity forms the harness-parity check flags. Your BASELINE_SELECTABLE_BACKENDS = {kiro, kas, opencode} would also drop main's deliberate claude and codex entries.

#9013 (@atomsbaza) registers the same backend on the landed registry but keeps it known-and-not-selectable, citing a live probe showing OpenCode's ACP surface exposes no permission mode. I suggest landing that registration first, then rebasing this PR's OpenCode-specific work on top. Could you two settle selectability here?

Posted from the 2026-09-08 open-PR relationship audit (read-only, one auditor per PR); reply here if any of this is wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fork Pull request from a fork (external contributor) merge conflict Branch has merge conflicts with its base — author must resolve before merge needs-author-decision PR blocked on author input readiness: action required A blocking check or review needs attention

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants