The convergence pass found four summary-level sites still describing
the estimator as fixed char/4: packages/README.md (twice), the compact
group and interface READMEs, and compact-basic's package.json
description. All now say chars-per-token with the charsPerToken
default, matching the authoritative package README/module doc/RFC.
A Codex review pass on the draft caught four real gaps and two solid
suggestions; all addressed except one pushed back on the merits:
- hooks-claude/hooks-codex: stderrSummaryMaxChars was the one new knob
with NO range validation — a negative/NaN cap would silently
misbehave inside slice(). Both bridges now assert a positive integer
at the TOP of apply() (before the config-file parse's early return,
so a bad value fails the load loudly), with rejection tests.
- tool-fs: the read caps count lines/chars/bytes, so positive-FINITE
was too loose (a fractional readLimit would flow into windowing
arithmetic and the schema description). All four now require a
positive integer, matching tool-web's cap.
- Doc drift the gates cannot catch: tool-web's README tools table
still named WEB_SEARCH_MAX_RESULTS as the mechanism; compact-basic's
README/module doc and the compaction-capability-seam RFC still
described estimation as fixed char/4 rather than the charsPerToken
default.
- subagent-acp: the dispose graces were tested only at the
startAcpRun level, so a regression that stopped threading plugin
config into AcpRunSpec would have survived. A provider-path test now
drives the trap-escalation scenario through ctx.subagents.start with
small config graces and bounds dispose at 4s.
Pushed back on: converting compact-basic's charsPerToken to a
schemastery field. The package's whole config is deliberately
hand-rolled (resolveConfig, every threshold REQUIRED with no default —
a documented design posture); one schemastery field beside it would be
incoherent. The knob is cordis.yml-reachable, defaulted, and validated,
which is what the convention requires; migrating the package to
schemastery wholesale is pre-existing config-surface hygiene out of
this change's scope.
The audit swept every packages/*/* plugin for the new AGENTS.md
convention (no hardcoded tunables in plugins) and exposes each finding
as a defaulted, validated Config field. Defaults are the previously
hardcoded values throughout, so no deployment or golden changes.
- tool-fs (had NO Config): readLimit, readMaxLineLength, readMaxBytes,
readStreamMinSize. The caps thread through ReadToolCaps/ReadWindow —
read-render already documented that the consumer applies the caps, so
they become explicit per-request fields.
- tool-web: searchMaxResults (WEB_SEARCH_MAX_RESULTS stays as the
schemastery default). Also fixes the stale GREP_LIMIT references in
search.ts and the web-capability-seam RFC (no such constant exists).
- bash-local: graceMs (SIGTERM->SIGKILL escalation grace). The
RunInternals.graceMs test seam is gone: graceMs is now a required
SpawnSpec field filled from config, so tests exercise the real
config path and the defaults live in exactly one place.
- subagent-acp: disposeEofGraceMs / disposeGraceMs. The AcpRunSpec
fields become required for the same one-defaulting-layer reason.
- session-persistence-sqlite: journalMode ('wal' default; the
rollback-journal modes serve filesystems where WAL's shared-memory
files do not work, e.g. network mounts).
- hooks-claude + hooks-codex: stderrSummaryMaxChars for the persisted
hook/result stderr summary. The duplicated summarize() helpers merge
into hook-protocol's summarizeStderr(stderr, maxChars), beside the
HookResultRecord field it feeds, with the bound parameterized the
same way runHook's defaultTimeoutMs already is.
- compact-basic: charsPerToken for the token estimator (default 4, the
English-text heuristic; CJK-heavy deployments need ~1-2 or compaction
fires far too late). Also corrects the BasicCompactService class doc,
which claimed defaults the required-field config never had.
- fs-local: deletes the dead STREAM_MIN_SIZE constant and the dead
FsIoInternals.streamMinSize seam — the read-routing bound lives in
the consumer (tool-fs), where it is now config. This is item 1 of
the proposed prune-write-only-fs-surface RFC, annotated accordingly.
Every new field gets range validation (following the existing
assertPositiveFinite pattern), a README row, and tests covering the
configured behavior, the schema default, and load-time rejection.
A number or string that two reasonable deployments could want set
differently — a timeout, grace period, output cap, result-count limit,
model name, base URL — belongs on the plugin's schemastery Config with
the shipped value as its default, not in a bare literal or module
constant. A DEFAULT_* constant or a test-only injection seam is not
configurability: the test is whether a cordis.yml deployment can change
the value without a code edit. Protocol/wire constants, semantic
constants, values pinned by an external spec, and security invariants
stay hardcoded.
The convention lands in AGENTS.md § Conventions (the authoritative
source the review skill cites); dsh-code-review gains the matching
reviewer-only check, since no mechanical gate can detect a hardcoded
tunable.
Maintainer decision: the subagent seam's deferred capability vocabulary
(outputSchema/structured, toolFilter, sendMessage/resume) is
intentionally reserved — the seam advertises the full intended contract
ahead of its implementations so providers and consumers grow into a
stable shape. Moved to rejected/ with the rationale on the status line;
the consumer-evidence analysis stays as the record of what is currently
unimplemented.
The fold-stdio RFC claimed the two e2e doc comments and packages/README
were the only non-runtime references; the package name also appears in
the generated module graph, sibling READMEs, and tsconfig references —
rescope the sentence to the runtime-importer census (the load-bearing
claim) and fold the mechanical references into the update list. The web
RFC's status-caller census now says 'the web packages' own tests'
(the seam's tests use the methods too, not only provider tests).
Deduplicate the parallel sweep from codex/exhaustive-simplification-rfcs
(merged in the parent commit) against the twelve RFCs already here, on
the merits per item:
Ported (rewritten to this set's evidence standard):
- fold-stdio-ui-helper — verified: dsh-stdio-agent is the only runtime
importer; the fold resolves the documented product-app-depends-on-
support-package tension. The false acp-agent analogy is replaced with
the real distinction (protocol product surface vs one app's front-door
scaffolding).
- generate-rfc-index-tables — verified: the classification RFC records
rejecting generation; the index tables are the repo's only
systematically conflicting docs region. Process framing made timeless.
Consolidated into existing owners:
- web searchStatus()/fetchStatus()/WebCapabilityStatus (verified: zero
production callers; the tool-web README and architecture.md claims are
drift) → drop-web-providers-change-event, renamed
drop-unconsumed-web-observation-surface.
- hook/result.durationMs (unread, nondeterministic, normalizer-scrubbed)
and the double-defaulted defaultTimeoutMs knob →
tighten-hook-protocol-contract.
- the exercised-but-unadvertised exec.arguments mutation path (a
tool-bash integration shim rewrites through it) → a sanction-or-seal
note in the pre-tool-input-rewrite proposal.
- the dormant-guard critique of subagent depth machinery → recorded in
prune-unimplemented-subagent-vocabulary as the considered-and-rejected
alternative, with the keep sharpened (uncapped-today acknowledged;
wiring the cap is the completion, not deletion). getProvider()/list()
and lastAssistantMessage recorded as examined-and-kept (bash-revert
precedent; observe-enrich recorded keep).
Not ported (with reasons):
- tools/change + system-prompt/change removal — recorded keeps in the
adapter-change RFC, unengaged by the sweep; no new facts.
- LlmService.models() — flagged by both surveys, but two lines with a
plausible consumer: TODO-or-drive-by territory per the RFC bar, not
a proposal.
- SchemaProp.default RFC — already XXX(unused-default)-tagged; the RFC
bar excludes TODO-tracked provisional cleanups.
- PreToolDecision 'ask' removal — FIXME(permissions)-anchored deferral
with the permission system as its named consumer.
- Codex bridge Config.model, merged systemMessages — wire-faithful
tested surface / README-documented deferral.
Their in-code TODO notes (acp-test-harness, hook-snapshot-noise,
catalog-type-links) and the stale hook-prompt-block name fixes ride the
merge unchanged.
Rewrite the hook-protocol RFC's process-relative wording (freshly-landed
/ days-old / week-old) as timeless evidence anchored to the recorded
RFCs, and sweep the same class from the steering, replay-config,
subagent-vocabulary, and vocabulary RFCs. Narrow the fs RFC's acceptance
criterion: replaceAll survives on the request spec and version on other
outcome types by design — name the exact removed surfaces instead of
claiming the spellings vanish.
The branch now bases on the hooks stack (PR #138's head), so every
'in-flight' reference to stack content became a current-state fact.
Audited all nine RFCs + three supplements claim-by-claim against the
merged tree (none invalidated; several strengthened):
- prune-dead-core-spine-surface: describe the landed tools/pre-execute →
dispatch → tools/post-execute pipeline — listeners return Decisions,
the registry builds every result and snapshots it to protect callId,
and a second (mutation-guard) test pins the field; drop the resolved
wait-for-interception hedge; scope the additionalContext ferry out.
- prune-producerless-vocabulary-variants: the ui-stdio fixture migrated
off the continuation trigger (llm-replay is the sole writer now); the
stack's own additions (rejected, prompt/blocked, hook/invoked+result)
all arrived with producers — the admission policy demonstrated live.
- prune-unimplemented-subagent-vocabulary: enrichment landed as
lastAssistantMessage only (agentType was dropped in the stack's own
review — the same judgment this RFC extends); the seam RFC now names
tools/pre-execute deny, which exists, sharpening the re-add path.
- drop-inert-request-knobs / drop-image-content-block /
trim-acp-bridge-unreachable-surface: current-state rewordings (shipped
bridges set no request fields; only compact-basic has explicit image
arms; 13 hook goldens also pin agentInfo).
- generic-long-running-tool-runtime census: second production seam
consumer (hook-protocol runHook: resolve+run, stdin/env, foreground
only — background machinery stays single-consumer); scrub-duplication
blast radius.
- discover-package-inventory: identical 54-entry tsconfig reference
sets; the comparesLog scenario knob (fixture-derivable, like recorded).
- unify-agent-and-session-id: third divergence site (in-process subagent
children mint two UUIDs), the hooks bridge id-lookups, and ui-stdio's
labelBySession map as a consumer that deletes under unification.
New RFCs from the post-stack survey:
- remove-agent-steering-mirror: the last mirror-of-durable event; zero
production listeners; both retention RFCs deferred its fate, and the
'no durable twin' rationale is contradicted by the adjacent append.
- tighten-hook-protocol-contract: producer-less 'native' dialect,
parsed-and-discarded suppressOutput, and hook/result semantics
(truncation + decision-string) defined twice in the bridges instead
of the lib that owns the event.
- single-source-acp-replay-config: cordis.yml/cordis.snapshot.yml differ
by exactly one plugin entry, with no gate on the forced symmetry.
The core-spine RFC's acceptance criterion still asserted all three
surfaces 'appear only in this RFC', contradicting the corrected scope
(runLoop/Inbox stay as package-internal symbols; only the public
re-exports go). Also add the deepseek README image-skip row and the
compact-basic [image]-placeholder row to the image RFC's removal set.
Two overstated evidence claims, reworded to the precise fact:
- prune-dead-core-spine-surface: runLoop has no importer OUTSIDE the
package (its own agent.ts drives the loop with it); the dead surface
is the public re-export, not the function.
- generic-long-running-tool-runtime census: BashTask.done has no
consumer through the public seam, but is production-load-bearing
inside dsh-bash-local (disposal awaits it); only the public
completion surface is single-consumer.
Also fold the reviewer-located doc sites into the removal sets so the
implementing PRs need no re-discovery: the llm/pi-ai/compact-basic
README rows and the adding-an-llm-adapter cookbook line (prefill/image),
the content-block-vocabulary RFC's has-a-home consequence lines
(cache/prefill/image), the tools.md paste + type-equiv manifest row +
tools README row (callId), and the session-surface RFC's
full-rebuild-after-replacement sentence (invalidate).