Every packages/*/* README now carries a canonical '## Known Limitations and
Deferred Work' section: condensed, evidence-backed bullets for consumer-visible
gaps (unimplemented features, platform caveats, MVP cuts) and consciously
postponed work (TODO/FIXME/XXX markers, RFC deferrals still open). The ten
pre-existing ad-hoc variants ('What is NOT here (TODO)', 'Deferred',
'Limitations (MVP)', 'Known limitations (tracked TODOs)', ...) are normalized
into the canonical heading.
A new doc-sync gate, scripts/verify-readme-limitations.ts, enforces the shape:
exactly one limitations-like heading per package README, byte-equal to the
canonical h2, with at least one bullet; near-miss headings fail so variants
cannot creep back. Packages with genuinely nothing to declare (dsh-brand,
dsh-timeout, dsh-subagent-mock, dsh-app-boot) are whitelisted in the script and
must NOT carry the section; whitelist entries are validated against the scanned
package set so a rename fails loud.
Wired into the doc-sync chain (package.json) and the run-gates doc-sync leaf
set; the standing rule lands in packages/AGENTS.md and the adding-a-package
cookbook; decision record in
docs/rfc/implemented/process/2026-07-10-readme-known-limitations-gate.md
(RFC index regenerated).
Also fixes two stale '(deferred)' markers claiming dsh-compact-basic is
unimplemented (the dsh-compact seam README's package table and the seam's
module doc comment).
Drain idle injection flushes before agent teardown, snapshot approval and subagent provider inputs, and gate subagent lifecycle events on real child readiness. Align the RFCs and generated contracts with the hardened behavior.
formatSystemPromptSnapshot's insert-join ternary had its already-newline-
terminated arm unexercised (a delta whose insert ends in a blank line),
failing the per-file 100% branch gate on suite.ts (99.24%).
Review follow-up: the blanket 10s default correctly targets worker-thread
cold-start races (starting, first-script-line, async child-registration
messages — genuinely CPU-bound under CI contention), but four waits assert
something different — that the HOST reacted PROMPTLY to an event that
already happened (a settled result, an observed worker death). Those had no
cold-start left to wait on, so the generous default just widened the window
a real regression could hide in.
Verified by injecting a 6s delay into the settle-reap's abort call: the
un-overridden helper's test still passed in ~6s. The same mutation now fails
in ~1s with the explicit 1000ms override restored on all four sites (the
abort-on-settle test's own assertion, the two worker-death cancel/dispose
reap checks, and the dispose-ack-race check). The other 12 waits keep the
10s default — they run BEFORE a result is awaited, waiting on the worker to
actually start rather than on a host reaction. Doc comment corrected to
describe the split instead of claiming every wait is a cold-start race.
The spec's 16 vi.waitFor sites used the 1s default timeout to wait for
worker-thread startup and child registration — CPU-bound work that blows
past 1s on a contended runner. The CI coverage lane (4 vitest workers plus
suites that spawn real subprocesses) hit this 3 times across 4 recent PR
runs, each a different subset of the cancellation/worker-death tests, each
green on rerun.
Every wait now goes through a shared helper with a 10s bound, and the file
sets a 30s test timeout to make room for it. The one deliberately tight
wait keeps its 800ms bound through the helper's override — it proves the
host (not the wedged worker's later loop turn) delivered the cancel, so a
generous bound would erase what it tests. No behavior under test changed.
spec.onError is a caller-supplied callback boundary, but the flattening
catch invoked it unguarded: a throwing sink rejected the whole async result,
breaking the seam's "result never rejects on a child-level failure"
contract (and docs/defensive-patterns.md's contain-callback-exceptions
rule). The sink's own throw is now swallowed — named as such — while the
original child failure still settles as stopReason 'error'. Regression test
drives a spawn failure through a throwing sink and asserts result resolves.
Same defect as the codex backend's, fixed there on PR #240; this is the
symmetric fix for the already-merged ACP backend.
The three wedged-child scenarios busy-spin their worker for 1.5s while the
host waits for the start RPC; under a loaded 2-core CI runner (this branch
adds several parallel suites) the RPC lands after vi.waitFor default 1s,
failing the coverage lane three runs in a row at the same three sites. An
explicit 10s waitFor timeout (well inside each test own 15s budget) makes
the assertions load-tolerant without weakening them. Belongs upstream with
dsh-workflow-workerthread; carried here because it gates this PR.