Skip to content

refactor: move backend capability tables into agent_sdk (PR 3a) - #9381

Merged
chenmingwei23 merged 1 commit into
mainfrom
refactor/agent-sdk-capabilities
Sep 8, 2026
Merged

refactor: move backend capability tables into agent_sdk (PR 3a)#9381
chenmingwei23 merged 1 commit into
mainfrom
refactor/agent-sdk-capabilities

Conversation

@iamwhatever

Copy link
Copy Markdown
Collaborator

Problem / Motivation

Six places outside the agent-SDK boundary decide what to do by asking which
backend
a session is on, not what that backend can do:

  • config/loader.py compares agent.acp_backend == ACP_BACKEND_CLAUDE.
  • dashboard/chat_handlers.py and dashboard/handlers/agents.py read
    provider.is_claude_backend.
  • dashboard/chat_runner.py does it three times: the pinned-model verdict, the
    billing row's provider label, and the /compact branch.
  • knowledge/llm_pool.py reads AcpClient._is_claude — a private attribute
    of a class in another package.
  • subagent.py lazily imports providers.acp.is_claude_backend to pick which
    home tree to clean up.

The tables those checks are really about also sit outside the boundary:
acp_backends.py owns the backend ids, 16 ACP_BACKENDS_* capability sets, the
Routing enum and the two dispatch tables; acp_tool_gate.py owns the
PreToolUse routing verdict and the adapter credential mask.

Why it matters

An identity check has one failure mode and it always points the same way. Add a
fifth backend, edit nothing, and it silently takes whichever arm "not claude"
selects — an arm it never demonstrated it can serve. That is the failure the
harness-parity doc calls H6, and codex is the harness that just landed.

The _is_claude read is worse than the others. It is application code reaching
an underscore attribute of the ACP client, so the client cannot rename its own
private field without breaking the knowledge pool.

Leaving the tables outside also means the boundary is advertised rather than
real: the old import path still works, and a new consumer finds it first.

What changed (motivation → approach → change)

Each branch depends on a property of the backend. The property already had a
table. So the tables moved behind the boundary, and each branch now reads the
property.

acp_backends.py became agent_sdk/backends.py, and acp_tool_gate.py became
agent_sdk/tool_gate.py. Both old modules stay as pure re-export shims, so about
30 existing call sites needed no edit. The shims re-export the functions, not
copies, so the registry still has one _baseline/_selectable pair whichever
path imported it.

New agent_sdk/capabilities.py holds SessionCapabilities, a frozen record with
one field per question a consumer actually asks, plus capabilities_for(backend)
and capabilities_of(provider). AcpProvider.capabilities is where a live
session's record comes from. Every field translates a table that already existed,
so no membership changed and no backend changed arms.

flowchart LR
  subgraph Before
    C1[consumer]:::ctx -->|"is this the claude backend?"| P1[provider / client]:::removed
    C1 -->|"reads a set directly"| T1[acp_backends.py]:::removed
  end
  subgraph After
    C2[consumer]:::ctx -->|"what can this backend do?"| K[SessionCapabilities]:::added
    K --> T2[agent_sdk/backends.py]:::added
    S[acp_backends.py shim]:::changed -.->|re-export| T2
  end
  classDef added fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:2px
  classDef changed fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:2px
  classDef removed fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-dasharray:4 3
  classDef ctx fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E
  linkStyle 0,1 stroke:#DC2626,stroke-dasharray:4 3
  linkStyle 2,3 stroke:#16A34A,stroke-width:2px
Loading

🟩 added · 🟨 changed · 🟥 removed · 🟦 unchanged

A consumer now asks what the backend can do; the tables it asks about live behind
the boundary, and the old module path still answers.

The six sites and the field each one reads:

site question field
config/loader.py which namespace are this backend's model ids in? model_id_namespace
dashboard/chat_handlers.py same, on a live provider model_id_namespace
dashboard/handlers/agents.py must its own advertised list be read back? resolves_model_from_advertised_list
dashboard/chat_runner.py (pin verdict) same resolves_model_from_advertised_list
dashboard/chat_runner.py (billing row) which provider seam served the turn? provider_seam
dashboard/chat_runner.py (/compact) does compaction finish inside the turn? compacts_inline
knowledge/llm_pool.py which channel carries an effort change? effort_via_config_option
subagent.py which home tree holds the session files? provider_seam

One capability set is new. ACP_BACKENDS_INLINE_COMPACTION exists because the
/compact branch was the one question with no table behind it. Its membership is
exactly what the check it replaced answered, and it is a strict subset of
ACP_BACKENDS_COMPACT.

Three more things the move required:

  • Every ACP_BACKENDS_* set now has a recorded disposition in
    agent_sdk/backends.py's docstring — semantic question, pre-session registry
    query, or driver-internal — and a test fails if a set has no row.
  • scripts/check_harness_parity.py's VOCABULARY_PATH follows the vocabulary to
    its new home. Pointing it at the shim would have been the drift the H8 rule
    exists to prevent: a second place a definition could legally live.
  • The agent-SDK boundary baseline shrank, because two consumers stopped
    importing providers.acp at all: dashboard/chat_runner.py 5 → 4 and
    subagent.py 8 → 7. Nothing was added or raised.

RFC docs/request-for-change/rfc-crew-agent-sdk-boundary.md asked PR 3 to pick
one of three homes for Routing and the dispatch tables and record the choice.
This lands option 1 and records it, plus a "PR 3a LANDED" note saying what is
still PR 3's to do (the role protocols, SessionRequest.mcp_servers,
writes_own_transcripts, spawnable_multiplexed_selections()).

Zero behaviour change, and how that is held

Every routing verdict, permission config, membership answer and capability field
is pinned in test_agent_sdk_capabilities.py to a literal copied from a clean
main checkout
, for every backend id and for an unknown one:

id routing permission config enforced
"" (kiro) agent_spec ("", "") no
kas agent_spec ("", "") no
claude seeded_settings ("", "") no
codex session_config ("mode", "read-only") yes
unknown unverified ("", "") no
"kiro" (the policy wire name, not a backend id) unverified ("", "") no

capabilities_of keeps the calling convention the six predicates had. They were
isinstance(provider, AcpProvider) and provider.is_claude_backend, so a shape
that was not a provider answered False; it requires a real SessionCapabilities
instance, so a wrapper, an unstarted provider or a MagicMock(spec=...) lands on
the fail-closed default instead of claiming every capability at once.

One residue is preserved deliberately: provider_seam still labels a KAS turn
acp. Only the ACP layer's own PROVIDER_LABEL_* constants separate it, and
promoting that would change what every KAS turn records — a telemetry change, not
a refactor.

One identity read stays

dashboard/handlers/agents.py's api_models still compares against
ACP_BACKEND_CLAUDE. It asks whether the backend has a --list-models catalog to
shell out to, which is a pre-session question whose capability answer would have
to be decided for KAS and Codex rather than translated. It is pinned by
enclosing function name, so a seventh read cannot appear beside it.

Tests

New test/test_agent_sdk_capabilities.py (37 tests):

  • An AST scan of each of the six files: no attribute, name, or string literal
    naming a backend identity, with the one remaining read allowed by enclosing
    function and a second test that fails if that function stops reading one.
  • Each capability field is read only off capabilities_for(...),
    capabilities_of(...) or .capabilities, so one grep finds every consumer;
    plus every field has at least one consumer, and the field list cannot drift from
    the dataclass.
  • Both shims define nothing (only __all__), so the boundary has no second front
    door.
  • Per-backend capability rows, the routing/permission/known-membership table
    above, and the fail-closed answer for an unknown id.
  • Every ACP_BACKENDS_* set has a disposition row, and no row names a set or a
    field that does not exist.
  • ACP_BACKENDS_INLINE_COMPACTION is a strict subset of ACP_BACKENDS_COMPACT.
  • AcpWorker never passes acp_backend, which is why swapping _is_claude for
    the effort capability changed nothing; if a future pool selects a backend, this
    fails instead of inheriting an arm.

Updated tests, all for the same reason — a double that asserted an identity flag
now carries the real capability record for the backend it stands in for:
test_cc_models_endpoint, test_dashboard_chat, test_dashboard_chat_handlers_coverage,
test_acp_model_config_options, test_llm_pool, test_subagent_coverage,
test/metrics/test_turn_profile. The three that restore the private registry pair
(test_harness_parity, test_agent_backend_editable, test_agent_backend_governance)
now reach agent_sdk.backends, which defines it. test_acp_capability_sets_leaf
and test_agent_sdk_backend_identity follow the vocabulary to its new module and
additionally assert the shim is a shim.

test_subagent_coverage's _is_cc_provider tests gained the case that used to be
missing: a shape that is not a provider must answer False.

Manual verification

N/A — unit coverage sufficient. This is a module move plus predicate translation
with no new user-facing surface, and the no-behaviour-change claim is held by
literals copied from main rather than by inspection.

Local verification, for the record:

  • Every ratchet and lint gate in the resolved prepare-pr profile: green.
  • check_agent_sdk_boundary.py: green, baseline lowered by 2.
  • Full backend suite run and attributed against a clean origin/main
    worktree
    : 270 failures fail identically on the base (this host has no user
    namespaces, so the sandbox, pod, trash and session-storage suites are red
    regardless), and zero are caused by this diff. One that first looked
    diff-caused (test_security_conductor_skill_contract) was fixed on main in
    fix(test): security-conductor scripts guard checks for stubs, not absence #9362 and went green on rebase.
  • docs_lint: green. Its one report-only stale baseline entry
    (line-ref … acp/client.py:3313) reproduces on main and is not from this diff.

Related Issues

no linked issue: this is a step of an RFC that has no tracking issue; the design
of record is docs/request-for-change/rfc-crew-agent-sdk-boundary.md, PR 3.

Checklist

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable)
  • No secrets, credentials, or internal references in the diff

@iamwhatever
iamwhatever requested a review from a team as a code owner September 8, 2026 04:39
@iamwhatever
iamwhatever requested a review from Zedmor September 8, 2026 04:39
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

Intent: Move the ACP backend registry and the tool-gate routing verdict behind the agent-SDK boundary (RFC PR 3, option 1), and make the six consumers that still asked which backend a session is on ask what the backend can do instead — with every answer, for every backend id and for an unknown one, identical to main.

Not a goal: the rest of RFC PR 3 (the runtime_checkable role protocols, the SessionRequest.mcp_servers extension point, writes_own_transcripts + AgentSupervisor.cleanup_session, spawnable_multiplexed_selections()); moving the routing key off the backend id and onto each driver (PR 4); closing the api_models catalog question, whose capability answer has to be decided for KAS and Codex rather than translated; and any behaviour change at all, telemetry's KAS-labelled-acp residue included.

@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: PASS

Capability-over-identity is the right cure for H6; behavior equality is held by literals pinned from main, and shims keep the move fully reversible.

Suggestions

  • The RFC rejected option 2 because "a reachable old path is the one a new consumer finds" — the permanent, non-warning shims re-create a milder version of that. Consider a ratchet (like the boundary baseline) freezing the count of acp_backends/acp_tool_gate importers so the old paths only shrink toward PR 4's removal.

[DESIGN-REVIEWED] 4ae969c

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

First-Principles-Verdict: CONCERNS

The non-deprecated shims keep the old import path a new consumer finds first — the exact failure the PR's rationale rejected — plus two zero-consumer surfaces.

Not justified as shipped

  • Item 7 — zero consumers: all 8 production consumers import kiro_crew.agent_sdk.capabilities directly; 0 import the five names re-exported from agent_sdk/__init__.py (grepped from kiro_crew.agent_sdk import).
  • Item 8 — zero consumers: no production code reads SessionCapabilities.backend; the PR's own test_every_capability_field_has_a_consumer exempts it.
  • Item 9 — undeclared: adapter_expose_files swapped leaf.split("/") for PurePosixPath(leaf).parts inside a claimed pure move; behavior-identical for every current entry, but it is a rewrite riding in a "moved" file.

What this change ships

Intent: stop application code from branching on which backend a session runs, so a new harness cannot silently inherit an arm it never demonstrated — an ADDITION (structural refactor executing the repo's recorded RFC decision).

  1. Backend registry/capability tables moved to agent_sdk/backends.py; old acp_backends path stays alive as a shim — justified
  2. Tool-gate verdict moved to agent_sdk/tool_gate.py, same shim arrangement — justified
  3. New SessionCapabilities record; six identity checks (loader, chat_handlers, agents, chat_runner ×3, llm_pool, subagent) now read a capability field — justified
  4. AcpProvider.capabilities property resolves the live session's record — justified
  5. New ACP_BACKENDS_INLINE_COMPACTION set gates the /compact acknowledgment arm — justified
  6. Test doubles without a real capability record now fail closed instead of claiming membership — justified
  7. Five capability names also re-exported from agent_sdk/__init__.py — zero consumers
  8. SessionCapabilities.backend field, "for messages and logs" — zero consumers
  9. adapter_expose_files path construction rewritten during the move — undeclared, rides along
  10. Disposition rows for every ACP_BACKENDS_* set, test-enforced; parity checker and docs follow the move — justified

Watch

  • The motivation argues "a reachable old path is the one a new consumer finds, so the SDK would be an alternative rather than the way" — then ships shims where "importing from here is correct" and "nothing warns", with no removal named in the RFC's remaining-work list. Definitions have one home and identity tests pin non-divergence, but the finds-it-first failure is retained by design.
    Clears when: the ~30 call sites are migrated and both shims deleted, or shim removal is listed in the RFC's PR-3/PR-4 remaining scope.

Subtractions

  • Delete the from kiro_crew.agent_sdk.capabilities import (…) block and its five __all__ entries in src/kiro_crew/agent_sdk/__init__.py — 0 consumers reach them through the package path.
  • Drop the backend field from SessionCapabilities (agent_sdk/capabilities.py) — 0 readers; re-add it with the first log line that needs it.

[FIRST-PRINCIPLES-REVIEWED] 4ae969c

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 4ae969c

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a: <one-sentence reason>

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 4ae969c

Verdict parsed from the review's SHA-scoped output markers for commit 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 4ae969ca0c2b53920b47b53e0ac92b6e08908d7a: <one-sentence reason>

@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from bdc961e to 8c8ceef Compare September 8, 2026 04:58
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 8, 2026
@iamwhatever

iamwhatever commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author
span=9ec07ed2ded9

self-added: no
mechanism: TestTheKiroConstructionPathIsUnconditional — pins that every non-claude backend reaches to_acp_id and never to_provider_id, plus the namespace-table entry the branch keys on

  • capability dispatch changes the Kiro construction pathrebutted (not a defect), with the drift risk it named now pinned by test.

This is not a defect. The Kiro construction path is unchanged, and H13 counts
conditionals on that path rather than how one is spelled: acp_effective_model
had exactly one conditional before this PR (== ACP_BACKEND_CLAUDE) and has
exactly one after (model_id_namespace != MODEL_NAMESPACE_ACP), on the same
axis, with the same two arms. capabilities_for("") resolves
model_registry_namespace("") to "acp" (agent_sdk/backends.py), which IS
MODEL_NAMESPACE_ACP, so kiro takes to_acp_id(m) — the identical call the
hardcoded branch made. No adapter capability is consulted to decide what Kiro
does; a table states what namespace Kiro's own ids live in, which is a fact
about the model registry, not about an adapter.

The demanded fix — "preserve the explicit Kiro model-resolution path without
capability dispatch" — is to restore == ACP_BACKEND_CLAUDE. That is the
backend-identity check harness-parity H6 forbids and this PR exists to remove,
so satisfying H13 that way violates H6 at the same line. H13 is about not
adding a conditional in service of an adapter; it is not about which vocabulary
the one pre-existing conditional is written in.

One real thing was in the finding and is now closed. The kiro arm is reached
through a TABLE, so an edit to _MODEL_REGISTRY_NAMESPACE_BY_BACKEND could
move it where a hardcoded to_acp_id could not. Commit 8c8ceef adds
TestTheKiroConstructionPathIsUnconditional, which records which registry
translator acp_effective_model actually calls and asserts "", kas,
codex and an unknown id every reach to_acp_id and never to_provider_id,
that claude still reaches to_provider_id(m, "claude_code"), and that the
table entry itself stays acp. Mutation-verified: flipping kiro's entry to
"claude_code" reddens 2 of its 6 tests.

This ruling covers every instance of the same tradeoff — a branch on a
capability field where the pre-existing branch was on a backend id, wherever it
moves — because the invariant that matters is "no non-claude backend changes
arm", and that is now asserted at the call site rather than argued.

Pre-drafted override rationale for a maintainer, verified independently against the code
before posting (the finding is security-fenced, so a rebuttal alone cannot clear it):

/ai-review override gpt 8c8ceefc838eea6e31eb1e74960ea654163f880d: acp_effective_model has one conditional before and after this PR on the same axis; kiro's namespace resolves to "acp" so it takes the identical to_acp_id(m) arm, and TestTheKiroConstructionPathIsUnconditional now asserts that for every non-claude backend and mutation-verifies the table entry.

@iamwhatever

iamwhatever commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author
span=25513d154ea8

self-added: no
mechanism: TestAForeignProviderIsClassifiedThePreviousWay — pins a real non-AcpProvider LLMProvider subclass to the same answer origin/main's isinstance-gated predicates gave it

  • provider capability is absent from the ABCrebutted (not a defect), with the equivalence now pinned against a real LLMProvider subclass.

This is not a defect: falling through to the unknown record IS the behaviour on
origin/main, preserved exactly. Every one of the six predicates this PR
replaced was isinstance(provider, AcpProvider) and provider.is_claude_backend,
so a provider outside AcpProvider already answered the conservative default
and no call site ever asked the ABC. capabilities_of reproduces that by
requiring a real SessionCapabilities instance, which is why it is an
isinstance check and not a bare getattr: a MagicMock(spec=AcpProvider)
has a truthy .capabilities whose every attribute is also truthy, so a
getattr would let a foreign shape claim every capability at once — the
opposite of the misclassification this finding is about.

"Silently misclassified" is the direction that cannot happen. Every field of
UNKNOWN_BACKEND_CAPABILITIES withholds: provider_seam is PROVIDER_ACP,
model_id_namespace is the native acp, and all three booleans are False. A
foreign provider is therefore classified as the harness with the fewest granted
capabilities, which is the same answer is_claude_backend returned for it.

Commit 8c8ceef adds TestAForeignProviderIsClassifiedThePreviousWay,
which subclasses LLMProvider for real — implementing all six abstract methods
rather than passing a bare object — and asserts it lands on
UNKNOWN_BACKEND_CAPABILITIES, that every boolean field is withheld and both
string axes are native, and that SubagentManager._is_cc_provider still
answers False for it, which is the one consumer where a wrong answer walks the
wrong home tree. Mutation-verified three ways: granting a capability on the
default, flipping its provider_seam, and replacing the isinstance gate with
caps or UNKNOWN each redden it.

Declaring capabilities on LLMProvider is not refused on principle, it is
out of scope for a translation PR. providers/base.py is the module the RFC
retires, and its consumer migration is PR 5; adding a member now means PR 5
removes one it just added. There is also no reachable caller: the only src
subclasses are AcpProvider (which declares it) and AcpSessionProvider
(used as AcpProvider's internal client, never passed to capabilities_of,
whose call sites are AcpProvider-typed). This ruling covers every instance of
the same tradeoff — asking for an ABC declaration so a hypothetical foreign
provider cannot reach the fail-closed default — because the default is what the
previous code already gave that shape.

Pre-drafted override rationale for a maintainer, verified independently against the code
before posting (the finding is security-fenced, so a rebuttal alone cannot clear it):

/ai-review override gpt 8c8ceefc838eea6e31eb1e74960ea654163f880d: A foreign LLMProvider already answered the conservative default on origin/main (every replaced predicate was isinstance-gated on AcpProvider), UNKNOWN_BACKEND_CAPABILITIES withholds every capability, and TestAForeignProviderIsClassifiedThePreviousWay now pins that against a real LLMProvider subclass with three mutations verified.

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention labels Sep 8, 2026
@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from 8c8ceef to 9e66635 Compare September 8, 2026 06:00
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 8, 2026
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

Blocked on a pre-existing comment-history-baseline.json mismatch for chat_runner.py

Backend Lint & Type Check (3.12) fails at Check comments for change history:

::error file=src/kiro_crew/dashboard/chat_runner.py::history narration in comments
grew from 103 to 109. The baseline carries the existing lines; it does not license new ones.

This PR adds none of those six markers. Running the gate's own scanner
(check_comment_history.violations_in_source) over the origin/main blob and over this
branch's file gives the same count both sides:

src/kiro_crew/dashboard/chat_runner.py: base=109 head=109

Every reported difference is a line-number shift of the same marker, caused by this PR
deleting one import line. comment-history-baseline.json records 103, so
origin/main itself carries six markers the baseline does not. A clean origin/main
checkout passes the gate only because the verdict is scoped to files a change touches,
and nothing touches chat_runner.py there. This PR touches it, so the pre-existing
mismatch surfaces here.

What I already did

Every history marker this PR genuinely added is rewritten to present tense — the rule is
right, and the prose was wrong:

file markers removed
src/kiro_crew/agent_sdk/backends.py 4
src/kiro_crew/agent_sdk/capabilities.py 1
src/kiro_crew/agent_sdk/tool_gate.py 2
src/kiro_crew/acp_backends.py 1
test/test_agent_sdk_capabilities.py 7
test/metrics/test_turn_profile.py 1
test/test_subagent_coverage.py 1

I also ran check_comment_history.py --write-baseline as the gate asked, which pruned
the two entries the module move emptied (acp_backends.py 3 → 0 and acp_tool_gate.py
2 → 0) and lowered _total from 7606 to 7601.

Why I am not "fixing" the remaining six

The two available moves both look wrong to me, so this is a maintainer call:

  1. Delete six markers from chat_runner.py. They are (#2696 GPT round, blocking) /
    #2686 references inside the promise-only-turn recovery, and each records why a
    specific guard exists
    — which reviewer demanded it and which symptom it prevents.
    The rule targets comments narrating what code used to do; these narrate why a
    guard is there, which is the WHY code-style.md asks comments to carry. Removing
    them to satisfy a count would delete the most useful thing in that block, in a
    mechanism this PR does not otherwise touch.
  2. Raise the entry 103 → 109. The baseline's own header forbids it: "Do NOT add or
    raise an entry to make a red gate green."
    --write-baseline enforces that — it
    never adds or raises — so the tool cannot record this correction either.

That leaves a real gap in the ratchet worth naming: a PR that lands new markers and
runs --write-baseline ships with an entry that silently understates its file, and the
next person to touch that file inherits the red. That is what happened to
chat_runner.py.

What would unblock this

Either is fine by me; both are yours to choose:

  • Correct the chat_runner.py entry to the tree's real count (109) in its own commit, so
    the correction is reviewable as a baseline fix rather than hidden inside a refactor; or
  • tell me to pay the six markers down in chat_runner.py, and I will rewrite each into
    present tense while keeping the guard's reason (naming the symptom instead of the issue
    number).

Everything else on this PR is green or answered: GPT's round-1 blockers are gone (its
current stamp carries one advisory FINDING), Opus reports no findings, and both round-1
dispositions are posted with no disposition-rule violations.

@iamwhatever

Copy link
Copy Markdown
Collaborator Author
span=d02faec46615

self-added: yes
mechanism: none — comment-only correction to the docstring block this PR moved

  • "Consulted only for" contradicts capabilities_for(), which calls it for every backendfixed in 9e66635.

Legitimate, and the comment was wrong in the direction that matters: it told the next
reader the namespace table is only consulted for one capability set, while
capabilities_for() reads it for every backend on every call. A reader trusting the
old sentence would think a namespace entry is inert for a non-member and could change
one safely. Corrected to name both consumers, in present tense.

@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from 9e66635 to 7fab54b Compare September 8, 2026 06:09
@github-actions github-actions Bot added merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 8, 2026
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 8, 2026
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

Attribution for head 7fab54bd0447: both reds were inherited, not from this diff. Backend Lint & Type Check (3.12) hit the known 15-min mypy timeout; Backend Tests (Windows) (4) failed only on test_work_ledger::test_two_conductors_binding_one_worker_at_once_yield_exactly_one_binding, the same flake #9359 saw and passed on rerun. Head is now 10523da60 (rebased onto e7db5f5b8, which also carried main's own comment-history-baseline.json correction for chat_runner.py, so that gate is green locally); CI re-runs on the new head instead of a targeted rerun of the superseded one.

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Sep 8, 2026
@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from 10523da to 735d34c Compare September 8, 2026 16:08
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Sep 8, 2026
@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from 735d34c to 6009f26 Compare September 8, 2026 19:54
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge labels Sep 8, 2026
@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from 6009f26 to 0006b1b Compare September 8, 2026 20:11
@github-actions github-actions Bot added the merge conflict Branch has merge conflicts with its base — author must resolve before merge label Sep 8, 2026
Consolidates the ACP backend registry and the tool-gate routing verdict behind
the agent-SDK boundary, and replaces the six backend-identity checks that still
lived outside it with capability questions. RFC PR 3, option 1.

acp_backends.py -> agent_sdk/backends.py and acp_tool_gate.py ->
agent_sdk/tool_gate.py. Both top-level modules stay as pure re-export shims, so
no existing call site changed in the same commit as the move. New
agent_sdk/capabilities.py carries SessionCapabilities, one field per question a
consumer outside the boundary asks; every field translates a table that already
existed. scripts/check_harness_parity.py's VOCABULARY_PATH follows the
vocabulary to its new home.

No behaviour change: every routing verdict, permission config, membership answer
and capability field is pinned to a literal copied from a clean main checkout,
for every backend id and for an unknown one.
@iamwhatever
iamwhatever force-pushed the refactor/agent-sdk-capabilities branch from 0006b1b to 4ae969c Compare September 8, 2026 21:16
@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Sep 8, 2026
@chenmingwei23
chenmingwei23 merged commit 2cb7357 into main Sep 8, 2026
65 checks passed
@chenmingwei23
chenmingwei23 deleted the refactor/agent-sdk-capabilities branch September 8, 2026 22:50
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants