Renames the written-usage rule into practice across the contract surface:
docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files,
taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the
.opencode/agents/ prompts. Every replacement is a sense-preserving
rewrite (colons, semicolons, commas, parentheses, sentence splits, varied),
never a mechanical swap; no spaced hyphens were substituted. Enum fields,
frontmatter, tables, wire data, and cited identifiers survive verbatim;
en dashes inside numeric ranges keep their meaning.
Verification: taskgraph validate (43 tasks), repo grep clean (single
exempt hit: the verification command's own search pattern in AGENTS.md),
cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
The wave-3 gate's booked finding 1: the git row's exit-report order
was honored by scheduling, not by construction — into_stream()'s
ChannelCloseOnDrop spawned a CHANNEL_CLOSE send racing the
exit_status_request send for a queue position on russh's bounded
message queue (event_buffer_size, default 10); a full buffer at the
serve tail could silently no-op the report.
- the seam now rides a split-halves duplex built from russh 0.65's
public API (its ChannelRx/ChannelTx are private): the reader is
git_map's ChannelHalveReader (owned read half, the private rx's
observation semantics repinned, no Drop path ever messages the
wire) and the writer the write half's ChannelTx via
make_writer_ext (poll_shutdown still sends EOF; the Rust 2024
capture phantom erased through a trait object)
- the report's send is now the sole close-contracting sender,
ordered ahead of the explicit close by sequential awaited sends on
the one handle — saturated buffer or not
- new wire row backlogged_seam_tail_still_reports_exit_status_before_close:
a volume flood freezes the armed client loop (the new harness
DataGate + the client data hook russh awaits inline) so the server's
flush pends and the bounded message queue saturates at the serve
tail; the gate's release drives the tail through the unwinding
contention — the row pins exit-status(0) before close under the
saturation and falsifies the old shape deterministically (15/15 red
under a temporary into_stream() revert, 10/10 green with the fix)
- doubles gain a race-safe per-repo serve gate (install_gate, the
double-check pattern over list_refs); harness gains
drive_with_data_gate (the drive* bodies factored into
mount_and_connect, existing signatures unchanged)
- all existing git wire rows unchanged and green (wire order
identical when the race does not fire); spawn-and-return intact;
tty rows untouched; event_buffer_size stays russh's default
(pinned in the row)
Verification: cargo test (default), --all-features, and six mixed
per-feature builds green; clippy x2 -D warnings; fmt -check;
cargo doc --no-deps 0 warnings; taskgraph validate (35 tasks).
Eight implementation tasks + the gate under tasks/implementation/,
folding the wave-3 gate's four booked findings in as dedicated tasks
(the gate's own disposition — wave-4 material, not wave-3 backports).
Hardening set (server-side, independent of the client chain,
scheduled first as cheap remediation):
- w4-git-exit-close: booked item 1 — the git row's status-before-close
by construction (the seam/shape decided + recorded in-task; the
saturated-buffer scenario pinned on the wire)
- w4-channel-not-held-rows: booked item 2 — the guard's wire rows on
git/tty pipe/shell, per build (test-only)
- w4-tty-coverage: booked item 3 — the channel_close-fired cancel
path + the Closed/Break no-exit-chunk rows + the write_all
no-deadline known-limit record
- w4-router-collision: booked item 4 — the ListenRouter
token-collision fail-closed pre-check
Client chain (sequential logistically — one client Handler impl):
- w4-dial: the russh client stack + the connection handle + the
client Handler skeleton at fail-closed defaults, over an in-process
fake-sshd server double
- w4-op-rows: exec/pty, direct-tcpip, git exec over the shared
grammar, the sftp client-side decline — the POC-3 §D.3 mechanics
- w4-reverse-listen: the -R leg — forward requests + the acceptor
registry + late-install (the wave's highest-risk task)
- w4-lifecycle: the OQ-12 primitives (cancellable handles, clean
disconnect, no reconnect policy)
review-wave-4 depends on all eight. The gate's item 5 (upstream asks)
stays booked with its owners — no task. Plan wave-4 section carries
the decomposition record + four flagged interpretations; wave table
status updated.
Verification: taskgraph validate ✓ (35 tasks)