Codex review: the RFC claims the fixtures prove each guard fires, but the
binding-pattern guards (events + services), the service no-prose branch,
and the empty-@param/@returns-description branches had no focused tests.
Add the five missing cases; every violation branch in the generator now
has a matching fixture.
gen-cordis-catalog now hard-errors (aggregated, not fail-fast) when an
event lacks description prose or a payload @param, or a public service
method lacks JSDoc, a @param per parameter, a @returns on a non-void
result, or an explicit return type annotation. The this receiver and the
trailing waterfall next are exempt on events (mode machinery owned by
@mode); a stale @param naming no real parameter errors, mirroring the
@mode contradiction check. parseJsDoc now ends prose at the first block
tag (standard JSDoc semantics), so the tags never change the rendered
catalog — only Source: line pointers moved.
Fills the ~139 gaps found across the 15 surface files, extends the spec
with negative-path fixtures for every new guard plus the exemptions,
records the decision as an implemented process RFC, and extends the
AGENTS.md typed-events bullet with the authoring rule. Runs inside
verify-cordis-catalog -> doc-sync, so CI and pre-push enforce it with
zero new wiring.
Master's docs-overhaul stack (#142-#144) rewrote AGENTS.md into the
slim budget-gated form and repointed the review skill's citations.
Resolutions:
- AGENTS.md: master's rewrite wins; the no-hardcoded-tunables
convention is re-added as one terse bullet in the new style, after
'Explicit > implicit at package seams'. Within the verify-doc-budgets
ceiling, so no displacement or raise needed.
- dsh-code-review SKILL.md: master's repointed citations win; the
hardcoded-tunables reviewer check and the Conventions keyword are
re-applied on top.
- packages/README.md: master replaced the hand-maintained dependency
list (which carried this branch's chars-per-token wording) with a
pointer to the generated module graph — master's side taken whole;
the estimator wording lives on in the compact package READMEs.
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.
Exact-size ceilings turned every two-word wording fix into a gate
event. The policy amends to: a ceiling sits at least 5% above the
doc's current size (pre-rewrite) and keeps that margin when ratcheted
to target — routine edits pass, real growth still trips the gate.
Amended together in all four policy homes (docs/AGENTS.md § Budgets,
the doc-tiers RFC, the gate script's module comment, the skill's
ratchet rule) plus the manifest values, so prose and mechanics stay
consistent.
stdio-chat.spec.ts imports ContentBlock/StreamChunk types from
@deepseek-ai/dsh-llm; the manifest entry lived on the folded package
and did not move with the tests. Declared peer+dev like the module's
other harness deps (the src consumes dsh-llm vocabulary through the
session events it renders).
docs/testing.md's real-API tier bullet now names the provider-specific
key gating (EXA_API_KEY, PERPLEXITY_API_KEY, ...): each suite
self-skips on its own key, so a DEEPSEEK_API_KEY-only run has not
exercised the provider smokes.
- session.md § TurnEndReasonMap now carries the full precedence rule
the old architecture.md stated and the loop implements (loop.ts's
stepFinishReason carry-forward): max-tokens wins over completed only;
disposed/aborted/error take precedence. The architecture.md pointer
was otherwise citing an incomplete home.
- The cookbook feature-map compaction row no longer reads as if a
model-facing /compact tool ships: a manual trigger invokes the same
ctx.compact routine; the consumer tool is deferred per the
compaction RFC.
The stdio app's module doc and its unit suite's doc carried the claim
the review already corrected elsewhere: an inject-less app boots past a
collapsed export shape, so the smoke proves the tree runs while the
unwrapExports assertion in the unit suite pins the shape.
vitest.config.ts coverage comments (excessive-tests welcome -> docs/testing.md;
v8-ignore reason rule -> the quality-gates RFC) and the acp bridge.spec
resource-ownership comment -> docs/testing.md. Postmortem 0001's summary now
names packages/AGENTS.md as the export-shape rule's home. Repo-wide sweep from
the root (all file types, vendor/lib excluded) shows every remaining AGENTS.md
citation resolves to a rule that exists where cited.
The packages/README.md group table still described support/ as holding
the stdio UI; and three prose sites credited the keyless smokes with
guarding the app export shape, which a bundle without inject cannot do
(a stray default boots rather than crashes) — the shape is pinned by
the stdio-agent unit suite's explicit unwrapExports assertion; the
smokes prove the composed tree boots.
The definitive sweep (audit every AGENTS.md mention in packages/, docs/,
examples/, scripts/) found thirteen more citations of relocated policy
and two citations of rules that never existed as quoted:
- with-key policy comments (web deepseek/perplexity e2e headers) ->
docs/testing.md; real-impl-over-mock comments (acp harness, load,
stream-update specs) -> docs/testing.md; defensive-pattern quotes
(acp index.ts x3, stream-update) -> docs/defensive-patterns.md.
- md-tier repoints: real-api-e2e RFC, tool-schema-catalog RFC,
postmortem 0001 guardrail row, adding-a-package cookbook,
drop-bash-output-spill-files RFC, acp-subagent-backend RFC phrasing.
- Two false attributions dropped in favor of self-contained reasoning:
tool-todo's 'don't validate scenarios that can't happen' and the
bash-stdin-env RFC's 'Don't add features beyond what the task
requires' (neither rule ever existed under those names).
- Citations of the two 'not golden truth' doctrines stay: those bullets
survive verbatim in the root conventions.
Note: packages/support/ui-stdio readline TTY spec flakes under full
coverage on a heavily loaded box (passes standalone and passed the
same tree's coverage run minutes earlier); untouched by this stack.
The readline UI lives inside @deepseek-ai/dsh-stdio-agent as the
in-package stdio-chat module; the packages/support/ui-stdio package is
gone. The app's front-door cluster always includes this UI and nothing
else composes it, so the boundary bought manifest/tsconfig/module-graph/
README/publint surface for a helper that is not independently
swappable — and a product app no longer depends on a support package
documented as not-product-surface.
createStdioChat, the StdioRuntime test seam, and both unit suites moved
verbatim (imports rewired to the module path); the named
name/inject/Config/apply export shape stays, being the contract the
app's ctx.plugin mount consumes. Coverage stays per-file 100%; the
built-bin smoke under plain node and both keyless Loader-path smokes
prove the published artifact and the demos end-to-end.
Implements docs/rfc/implemented/simplification/2026-07-04-fold-stdio-ui-helper.md
(moved from proposed/ and amended to the shipped shape).
- Restore the universal JSDoc rule the rewrite dropped (module doc
comment + semantic JSDoc on every export), in root AGENTS.md § Type
safety and documentation — the generated-catalog RFC cites it as the
rule the generator enforces at the source.
- Repoint the six remaining citations of moved content that the
section-name grep missed (rule-title quotes and prose references):
agent-loop agent.ts, acp index.ts, acp turns.spec.ts, the Exa e2e
header, the real-api-e2e RFC, the doc-sync-enforcement RFC amendment,
and rfc/implemented/AGENTS.md's section-name casing.
- Fix two docs/testing.md overstatements: the unit tier also runs
examples/*/tests specs, and keyless-by-nature examples have no
with-key smoke.
- Displacement trims keep root AGENTS.md at 1,498/1,500.
Codex review found three gaps between the documented contract and what
the gate enforced, each proven by a failing probe before this fix:
- a generated region must sit directly under its own '## {Lifecycle}'
heading (the last H2 above the begin marker), so a drifted heading
can no longer leave the tables filed under the wrong section;
- the lifecycle set is closed like the class set: an unknown directory
under docs/rfc/ is a structure error, not an invisible subtree;
- an index-shaped table row linking into a lifecycle folder OUTSIDE the
generated regions is an error (prose links to RFCs stay legal), so
'listed exactly once' cannot be violated by a hand-added row.
knip: the walker/renderer internals (the closed sets, the heading and
marker helpers, the per-lifecycle renderer) have no importer beyond
this module — the public surface is rfcRoot, walkRfcTree, and
spliceReadme, the three names the generator CLI and the gate consume.
docs/rfc/README.md's per-lifecycle tables are now generated between
gen-rfc-index marker comments from each RFC's path (lifecycle/class),
H1 title (optional 'RFC: ' prefix stripped), and filename date, sorted
by date then filename — the one docs region every proposal wave edits
and every concurrent branch conflicts on becomes derived state.
scripts/rfc-index.ts owns the shared walker (closed lifecycle/class
sets, structure rules, parseable-H1 requirement) and the renderer;
gen-rfc-index.ts is the writer CLI; verify-rfc-classification.ts keeps
the structure check and asserts the committed regions byte-match a
fresh render (freshness subsumes the index-completeness check, since a
generated-from-disk table is definitionally complete and correctly
headed). A malformed H1 is a hard error in both directions, so the H1
is now load-bearing as the title source — the one nonconforming H1
(a status suffix duplicating the path) is normalized.
Implements docs/rfc/implemented/process/2026-07-04-generate-rfc-index-tables.md
(moved from proposed/ and amended to the shipped mechanics); the
classification RFC's verify-only stance carries the supersession
cross-link per implemented/AGENTS.md.
The gate joined the doc-sync chain in this PR; the root file's two
enumerations of that chain must name it in the same change. The frozen
ceiling re-records the exact new count (8134).
architecture.md is the behavior map: layering, service map, seam
pattern, and the loop — everything else defers to its owning tier.
- Seam narrations compress to two-to-four sentences plus links to the
RFC and type-catalog homes that carry the detail (turn-end variant
semantics -> session.md, derivation mapping -> session.md, StreamChunk
conventions -> llm-streaming.md + source).
- The MVP feature-to-mechanism checklist moves de-statused into the
extension cookbook as 'The feature -> mechanism map' — mechanisms
only, no implementation-status bolding to rot; the microkernel RFC's
proof-obligation pointer follows it.
- The layering diagram describes layers by family instead of
enumerating packages (the stale 'future plugins: hooks, compaction'
row is gone); the dependency rule defers to packages/README.md.
- The loop pseudocode, the three externally-cited anchors (the
vocabulary, event taxonomy, waterfall semantics), and the filename
are unchanged.
- Budget ratchet: docs/architecture.md 3897 -> 1800 (now 1,797 words);
the doc-tiers RFC's deferred list prunes the item this ships.
- The manifest now freezes each budgeted doc at its exact current wc -w
count, matching the documented policy (a ceiling starts at the doc's
current size); the headroom that contradicted the freeze claim is
gone. Post-rewrite ratchets may still land at new-size-plus-headroom,
per the skill's ratchet rule — that is a different moment than the
initial freeze.
- --list now renders MISS/BAD rows for missing files and malformed
ceilings instead of silently dropping them (gate mode already failed
correctly; the report mode no longer under-reports).