Define the in-file RFC contract in docs/rfc/README.md § The file format: the header block (`# RFC: <title>` plus a dateless Status enum cross-checked against the lifecycle folder), the per-lifecycle body skeleton (a Problem opener everywhere; Proposal/Alternatives considered/ Acceptance criteria/Risks in proposed/; present-tense Decision/ Consequences with proposal-era headings banned in implemented/; the frozen proposal shape in rejected/), and a mandatory Alternatives considered section with a date-fenced grandfather comment for pre-format RFCs whose alternatives are not reconstructible from the record. Enforce it with a new doc-sync gate, scripts/verify-rfc-format.ts, and normalize all 112 RFCs to it: ~15 Status-line spellings collapse to the enum, 29 Context openers become Problem, the 39 legacy-format XXX debt markers are resolved and banned from reappearing, proposal-era sections in implemented RFCs are rewritten to shipped reality (including the web/fs/subagent seam RFCs' migration plans and test checklists, closing the doc-tiers deferred-work item on the web seam), every RFC gains an Alternatives considered section or the grandfather comment, and the bilingual pair is re-mirrored and re-recorded. Move the generated index tables out of README.md into a fully generated docs/rfc/INDEX.md — gen-rfc-index now writes the whole file, and verify-rfc-classification checks its freshness and rejects index-shaped rows in the curated README — which makes room for the format contract to live in the README front door instead of a separate FORMAT.md. The decision record, and the first RFC written in the new format, is docs/rfc/implemented/process/2026-07-05-uniform-rfc-format.md.
4.2 KiB
RFC: Prune write-only fields and a dead routing knob from the fs seam
Status: implemented
Problem
The fs seam split moved read routing and policy out of the backend into dsh-tool-fs and dsh-fs-policy. Four pieces of surface kept the pre-split shape — populated on every call, read by nobody:
STREAM_MIN_SIZE+FsIoInternals.streamMinSizeindsh-fs-local— removed ahead of this change by the no-hardcoded-tunables audit, which made the routing bounddsh-tool-fs'sreadStreamMinSizeconfig; recorded here as part of the full prune. Originally (packages/fs/fs-local/src/fsio.ts, re-exported frompackages/fs/fs-local/src/index.ts): zero readers anywhere, including fs-local's own source and tests. The backend has no read routing —readWholeText/streamWholeTextare separate primitives the caller chooses between — and the real routing constant lives in the consumer (packages/fs/tool-fs/src/read.ts, compared againstinfo.size). Two mirrors of the 10 MiB fact; the backend's was dead, and the knob's JSDoc claimed a "read routing" override that did not exist.FsTarget.inputPath(packages/fs/fs/src/types.ts): every backend and every test fake had to fabricate a "diagnostics only" value with zero production readers — the policy plugin and every error message usetargetKey/displayPath. ThelistDirproducer exposed the semantic wobble: directory children got the bare entry name, which was nobody's "input".FsEditOutcome.replacements+.replaceAll(packages/fs/fs/src/types.ts):replacementshad zero production readers (the single-match policy itself stays — it is enforced by theFS_AMBIGUOUS_EDIT/FS_EDIT_NOT_FOUNDthrows inside the backend, whose error message keeps the internal count);replaceAllwas read only byformatEditOutputinpackages/fs/tool-fs/src/edit.ts— as an echo of thereplace_allargument the tool already holds. Shrunk,FsEditOutcomeis{ version, before, after }, parallel toFsWriteOutcome's genuinely backend-discovered fields.FileReadOutcome.limit+.version(packages/fs/tool-fs/src/read-render.ts): populated by the read tool, butformatReadOutputrendersoffset/lines/totalLines/truncatedByBytesonly, and thefs/observedemit usesinfo.versiondirectly rather than an outcome copy.
Decision
Delete the fs-local constant, its re-export, and the streamMinSize knob (the remaining FsIoInternals knobs are genuinely used by the atomic-write tests); drop inputPath from FsTarget; shrink FsEditOutcome to { version, before, after } and pass replaceAll to formatEditOutput from the parsed args; drop limit/version from FileReadOutcome. The filesystem.md pastes, packages/fs/fs/README.md, and the test fakes that had to fabricate the removed fields shrink with the types.
Alternatives considered
Why not keep them?
A future permission/containment layer might want the pre-resolution path for error text — but it would want the request, which every call site still holds. "N occurrences replaced" might become model-facing text — a behavior change to design when wanted, and the backend-internal count survives for its error message. A read footer might display limit — everything the footer shows already derives from lines/totalLines. Meanwhile every current and future backend (remote, native) would have to fabricate wire fields nobody consumes, and every test fake would have to satisfy them.
Verification
The removed surfaces are gone — STREAM_MIN_SIZE/streamMinSize in dsh-fs-local, FsTarget.inputPath, FsEditOutcome.replacements/.replaceAll, and FileReadOutcome.limit/.version — while the request-side replaceAll (FsEditRequest) and the version fields on the other outcome types are untouched; the test fakes shrank with the types. formatEditOutput's emitted text is unchanged for both replace_all branches, so no snapshot golden churned.
Consequences
Backends gain no new obligations; they shed four fields nobody consumed. The fs discovery work (glob/grep tools) touches the same dsh-fs type files — a textual, not design, overlap that reconciles mechanically.