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.
fs/ - filesystem capability family
The filesystem stack: a provider seam (text IO + atomic mutation with an optional version guard), a local implementation, a policy gate plugin (observed-state + read-before-edit + version-guarded write/edit), and the model-facing file tools + executor. All product packages.
| Package | Role | ctx key |
|---|---|---|
fs/ |
Provider seam: text IO + atomic mutation primitives (optional version guard); owns the fs/* policy events |
ctx.fs |
fs-local/ |
Local-filesystem FileSystem implementation |
(registers ctx.fs) |
fs-policy/ |
Policy gate plugin: observed-state + read-before-edit + version-guarded write/edit, via the fs/* event gate |
(no service — fs/* listeners) |
tool-fs/ |
Model-facing read/write/edit tools AND the executor (reads via ctx.fs, owns read windowing, dispatches fs/*) |
(registers on ctx.tools) |
The interface lives at fs/fs/. A sandboxed, remote, or project-scoped filesystem backend can replace fs-local without touching the seam, the policy gate, or the model-facing tool schemas. The policy (fs-policy/) is a plugin that participates only through the fs/* event gate, not a service the tool injects — so dropping it gracefully loses the policy and leaves the unconstrained bare provider rather than breaking the tool. A deployment that loads tool-fs/ is expected to also load it.