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.
- ADR-005 decision 3 amended (the gap is closed upstream: alkvault
0.2.0's Zeroizing<String> return on every path incl. the error one;
the adapter wrap dropped — the double wrap would be dead posture);
the Negative consequence amended; the vault-surface note updated.
- mapping.md: the forwarded-tcpip row rides the gate shape (typed
call-plane miss, residual race re-scoped); derivation-contract
rule 5 amended (the PTY flattening constraint retired, alktty
0.8.2 — no contract change); the exit_signal row + OQ anchors
updated.
- open-questions.md: OQ-07 + OQ-10 amended with the upstream
closures; the two closed watch items struck with pointers.
- client.md: the OQ-07 anchor updated.
The wave-3 gate read the landed server mappings against
mapping.md/ADR-006/ADR-003/auth.md line by line: session/pty/shell/pipe
(translation bridge + the derivation contract rules 1-6), git exec
(full grammar, no-oracle validation, collapsed denial byte order),
route-1 bridge direct rows, the full -R listen flow + forwarded-tcpip
data leg, per-build decline matrices, identity observed at every seam,
bounded bookkeeping, and the uniform safe tracing vocabulary. All four
decomposition-time interpretations confirmed.
Fixed inline (this gate):
- install_forwarded_surface pre-checks the tunnel/forwarded op —
alkcall's register silently replaces an existing entry, so a second
adapter over the same shared seam registry silently stole the first
adapter's arrival routes; now a failed construction
(AdapterError::ListenRegistration), pinned by a new wire row
- git take drops a stored pty entry (combined-build stale state)
- doc hygiene: channel_cap release ordering, the session-row scoping
of the handler Drop, the cancel-order recording, the post-cancel
arrival caveat, the exit-chunk ADR lineage cross-ref, the stale
lib.rs wave-state doc, the superseded dispatch test name, and
mapping.md's listen-resource wording (address verbatim + tcp port)
Booked for wave 4: the git row's status-before-close honored by
scheduling not construction (needs a seam/shape change), the
channel-not-held wire test rows, the tty coverage minors (cancel path,
Closed/Break shape, stdout write deadline), and a defensive
ListenRouter token-collision pre-check.
Verified: cargo test (default) + --all-features (173 rows, 13
binaries), clippy --all-targets -D warnings ×2 (default +
--all-features), fmt --check, cargo doc --no-deps (0 warnings),
taskgraph validate (26 tasks).
The tunnel branch's other half is served for real against published
alktunnels 0.4.1 / alkcall 0.10.1, with POC-3 §C.4 as the template:
- tcpip_forward/streamlocal_forward (+ both cancel forms) map the
vanilla listen request family-ward through TunnelListener::open on
the held ChannelClient (bounded inline dial — the global request's
reply + the &mut u32 bound-port out-param are the hook's); the
wire bind endpoint rides as the resource string verbatim (OpenSSH
client-side normalization parity). A reply without the minted
listen token/bound report declines and reaps its fabric listener.
- the door serves the fabric's inbound tunnel/forwarded opens: the
serve config gains TunnelListenSeam (the seam-config second
course-correction — the shared ServingConfig registry the assembly
wired at from_connection_with_serving); alkssh registers the
forwarded openable (ChannelCore over a clone of the held client's
manager) at SshAdapter::new — a failed registration is a failed
construction (AdapterError::ListenRegistration).
- the forwarded-tcpip data leg: token correlation through the shared
ListenRouter → the in-crate per-session channel cap at the accept
surface (the acceptor vocabulary) → Handle::
channel_open_forwarded_tcpip streaming the listen request's own
args + the actually-bound port (sshd parity) with
TunnelForwardedParams.peer as the originator → pump_bidi(ssh,
fabric). Correlation-miss → teardown-at-birth, never a half-open
SSH channel. Unix arrivals ride forwarded-streamlocal@openssh.com.
- bounded bookkeeping: the per-session listener map packet-loop-only
with a dedicated max_concurrent_channels-ceiling counter; the
route map bounded by construction (mints at accepted listens,
removed at cancels + session teardown — the handler's Drop).
- the harness grew the client-side accept hook
(server_channel_open_forwarded_tcpip/_streamlocal,
acceptor-driven) + a hand-rolled tracing subscriber; 18 contract
rows + both real-tool smokes (ssh -L, ssh -R + curl) green in
tests/tunnel_mapping.rs; the wave-2 stub listen rows migrated per
build (unfeatured / seam-unconfigured).
Verified: cargo test across default/git/session/sftp/tunnel and all
combos + --all-features; clippy -D warnings (default, all-features,
combos); cargo fmt --check; cargo doc --no-deps clean;
taskgraph validate (26 tasks).
The biggest single-row surface of the spine: pty/shell/pipe-exec serve
through the published alktty seam (drive_session_pre_negotiated) over a
translation bridge, re-landing POC-3 stage D as contract:
- src/tty_map.rs (session feature): the bridge (SSH data → Chunk::stdin
through a bounded mpsc + framing task; SSH EOF → the zero-length
stdin sentinel — verified against alktty's pump semantics; stdout →
data; stderr → extended_data(1); exit chunk → the derivation
contract), the locally-built negotiation (OQ-08: shell argv verbatim,
wrapper ++ [cmd], cwd/env session-side, ttymodes → the reserved {}
modes JSON), and the spawned serving path.
- serve_tty_row: driven chunks→SSH inline so the exit report is the
path's final act (status before close, ADR-006 decision 4); two
cancel signals (per-channel watch fired by channel_close, connection
teardown fired by SessionHandler::Drop) abort the alktty session
task — the ADR-056 kill-on-drop contract is the cancelled session's
cleanup.
- Derivation contract rules 1–6 in derive_exit + the crate-local
signum→name table: −N → exit-signal(name), −1 → exit-status(255),
core_dumped never, unmapped negatives → 255, the PTY-path rule-5
fidelity limit documented; wire rows pin the shapes.
- Negotiation-error frames (alktty's 0x00-length-prefixed error
framing) → negotiation-failed serving decline + exit-status(255) +
close — the one could-not-start claim this door makes (nothing ran).
- Control rows: window_change/signal → the per-channel BridgeSurface
(bounded mpsc, try_send, never awaited on the packet loop); signal
names verbatim (no decline shape — fire-and-forget); pre-serve
resizes update the stored pty params (final size wins).
- Unset-declines (OQ-08) keep the git row's serving-path shape
(failure then close, spawned): shell-unconfigured /
wrapper-unconfigured / seam-unconfigured; pipe-less shell SERVES
pipe-mode (the recorded vanilla-sshd posture); env_request decline
unchanged (OQ-09).
- tests: the hand-rolled TtyBackend shim + ownership witness doubles;
serving rows through the harness, the unset-field decline matrix,
derivation rows on the wire, negotiation-failure rows, the identity
ride asserted live at the seam, the kill witness; the real-tool
smoke — OpenSSH -tt against a real PTY via dev-dep alktty local
(test graph only), skip-with-reason posture. Wave-2 stub rows
migrated to the new decline reasons (dispatch_core, observe).
Verification: cargo test (113) / --features session (149) / git (142) /
tunnel (115) / --all-features (161) — all green; cargo clippy
--all-targets -- -D warnings ×5 feature configs; cargo fmt --check;
cargo doc --no-deps ×3 builds clean; taskgraph validate.
- OQ-10 is not resolved: the mapping-contract derivation details (name
table, core-dumped disposition, −1 disposition) are working
dispositions on mapping.md's exit rows until formalized there / as an
ADR-006 amendment. The 1a40bfb commit prematurely closed them and left
ADR-006's reference (OQ-10 open) stale.
- Premise correction integrated into the seed, not argued after the
fact; the portable-pty 0.9.0 source now cited as verified
(From<std::process::ExitStatus> maps signal-terminated → code 1 +
locale-dependent strsignal string; the number is unrecoverable).
- mapping.md: exit_signal row and the Open Questions entry updated to
open OQ-10 with the working-draft framing.
Verification: cargo test (2/2 targets ok), cargo clippy --all-targets -- -D warnings, cargo fmt --check (docs-only change)
- OQ-10 resolved: alkssh derives SSH exit-signal from the alktty exit
chunk's negative code (−N = signal N, pipe path) server-side; −1
(alktty's could-not-determine sentinel) → exit-status(255), never a
signal claim; core_dumped never asserted in v1; no alktty wire change
- new verified backend finding recorded: portable-pty 0.9 reduces
PTY-mode signal termination to a plain exit code 1 (signal number
unrecoverable through its trait surface) — faithful exit-signal on
the PTY row is an alktty/portable-pty upstream ask, not an alkssh one
- research docs (poc-3 §D.2, pocs.md, mapping-table rows) restated
accurately instead of carrying inline correction scaffolding
Verification: cargo doc --no-deps earlier this session (docs-only change)
- OQ-10's seed overstated alktty's exit-chunk design as a 'signal-identity
gap': the negative code is the documented signal encoding (alktty ADR-004,
ported from alknet ADR-055; src/local/pipe.rs::exit_code_from — Unix
ExitStatus::signal() → -sig), a tty/pty-grounded convention
- 'alktty widens the chunk' demoted from co-equal candidate to gated
exception: it would widen ControlMessage::Exit's one-way parsed shape,
the backend-wide exit_code API, and the alknet ADR-055 lineage — requires
an alktty ADR with explicit tty/pty-grounded justification, never an
alkssh convenience ask
- OQ-10 re-scoped to the real remaining detail: −1 (wait failure vs SIGHUP)
collision, core-dumped disposition, non-Unix semantics, name derivation
- correction notes added in place: poc-3 §D.2, pocs.md, mapping-table.md
(both rows); mapping.md exit_signal row updated
Verification: cargo doc --no-deps (docs-only change)