Two pieces of dsh-acp surface were unreachable from any shipped config:
- AcpConfig.agentName/agentVersion: the app package hands the bridge only
{ model, systemPrompt }, so no leaf cordis.yml could set them; they were
settable only by direct-mounting the bridge (a unit test). Hardcode
agentInfo at the initialize site and delete the fields, their schema
defaults, the ?? fallbacks, and the TODO(double-default) whose subject
vanishes. The handshake wire value is unchanged (all snapshot initialize
lines byte-identical).
- The toolKindFor name heuristic special-cased bash*/read*/write/edit*
names in the generic-fallback path, violating the bridge's own design
rule ("the bridge never special-cases tool names"). Every first-party
tool ships its kind via presentCall; the fallback now renders the
neutral kind 'other'. The fallback is reachable when a presentCall
throws OR when model args fail the tool schema (defineTool's presentCall
wrapper returns undefined on violations) — the latter shows up in one
committed golden (hook-codex-posttool-block: three bash calls missing
the required description), whose kind cells flip execute->other. That
3-line golden refresh is the whole transcript delta.
The empty-arguments branch of parseToolArguments lost its only exercise
with the deleted heuristic test; it is live behavior (JSON.parse('')
throws, so the guard is what renders a zero-arg call as rawInput {}), so
it gets a dedicated pin instead of deletion.
RFC moved to docs/rfc/implemented/simplification/ and amended to shipped
reality: fallback reachability includes schema-invalid args, and the
golden churn is exactly the three kind cells (the original zero-churn
claim held only for the branding half).
4.0 KiB
RFC: Trim unreachable ACP bridge surface — the branding knobs and the kind-sniffing fallback
Status: implemented (accepted 2026-07-04)
Problem
Two pieces of dsh-acp surface were unreachable from any shipped configuration:
AcpConfig.agentName/agentVersion(packages/ui/acp/src/index.ts). The shipped app package hands the bridge only{ model, systemPrompt }(packages/ui/acp-agent/src/index.ts), so no leafcordis.yml— the only production config surface — could set the knobs at all; they were settable solely by direct-mounting the bridge, which only a unit test did. Every snapshot golden — the hook-matrix scenarios included — pins the schema defaults (deepseek-harness-acp/0.0.1). The pair also carried a liveTODO(double-default): the literals existed twice (schema.default(...)plus??fallbacks), with the TODO asking to pick one home.- The
toolKindForname heuristic (same file) special-casedbash*/read*/write/edit*tool names in the generic-fallback path. Since the render-intent union, every first-party tool those arms matched ships its ownpresentCallcarrying its kind, and the presenter-less production tools (subagent,subagent_fork) fell through tootheranyway. The arms were production-reachable only when a tool declined to present its own call — apresentCallthat THROWS (the containment fallback), or model arguments that fail the tool's schema sodefineTool'spresentCallwrapper returnsundefined(e.g. abashcall missing the requireddescription) — and the bridge's own module doc states the design rule the heuristic violated: "the bridge never special-cases tool names".
Decision
agentInfo is hardcoded at the initialize site ({ name: 'deepseek-harness-acp', version: '0.0.1' }); the two config fields, their schema defaults, the ?? fallbacks, and the TODO(double-default) (whose subject vanished with them) are gone, along with the knob half of the direct-mount config test, the two config rows in packages/ui/acp/README.md, and the packages/ui/acp/acp-feature-support.md cells that described the knobs and the name inference. The emitted handshake wire value is unchanged — zero golden churn on the branding half. toolKindFor is replaced by the constant 'other' at both fallback sites (the presenter fallback and nullToolPresenter), and the heuristic is deleted with its test rows. The fixed handshake identity stays pinned by the bridge's initialize unit test and by every snapshot golden. On the fallback half the transcript delta shows up in exactly one committed golden: hook-codex-posttool-block, whose recorded model omits the required description on three bash calls, so those cards take the declined-to-present fallback and carry kind: 'other' — the honest neutral card for a call the tool would not vouch for.
Why not keep them?
agentInfo is client-visible branding a deployment will eventually want configurable — but a knob no shipped config can reach is not configurability, it is drift surface (the double-default TODO was its symptom), and the honest re-add must include the dsh-acp-agent plumb-through that does not exist either; both arrive together with the deployment that needs them. For the heuristic: a hypothetical third-party presenter-less tool named read_docs loses an inferred read icon — but inferring kinds from unknown plugins' names is exactly the special-casing the render-intent design rejected. The only shipped paths the heuristic reached were the declined-to-present fallbacks (a throwing presentCall, or schema-invalid model args); rendering kind other there makes the client show the raw input instead of a masquerading first-party card — strictly better diagnostics for a broken presenter or a malformed call.
Risks
None beyond the fallback rendering trade described above — degenerate paths whose neutral card is more diagnosable than an inferred first-party one.