X m11-shell-substrate ci SaberMage/spt-core#16 · 27595180782
✓ traceability in 7s (ID 81583919509)
X test (self-hosted, Linux, kitsubito) in 5m47s (ID 81583919515)
  ✓ Set up job
  ✓ Run actions/checkout@v5
  ✓ Use mold linker (Linux)
  X Test (nextest)
  ✓ Post Run actions/checkout@v5
  ✓ Complete job
✓ test (self-hosted, Windows, hfenduleam) in 6m18s (ID 81583919550)
---summary---
completed	failure	M11 shell-substrate extensions (W1: per-capability act-gates, REQ-CONSENT-3)	ci	m11-shell-substrate	pull_request	27595180782	8m3s	2026-06-16T04:57:24Z


CI **RED** — Linux (kitsubito) nextest failed; Windows passed. Read the failure before judging (flake vs Linux-specific regression):


Bash

gh run view 27595180782 --log-failed 2>&1 | grep -iE 'FAIL|panicked|assertion|error\[|tunnel|backpressure|nethost' | head -40

Read Linux failed test details


test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:58:00.1046603Z    Compiling static_assertions v1.1.0
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:15.6927044Z         PASS [   0.941s] (  47/1092) spt::tunnel_e2e shell_tunnel_round_trips_opaque_bytes_through_the_real_surfaces
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:15.9943727Z         PASS [   0.010s] (  86/1092) spt::bin/spt cli::tests::escalation_choice_parses_fail_closed
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.3503368Z         PASS [   0.044s] ( 186/1092) spt-daemon applyhost::tests::apply_staged_gates_fail_closed_with_the_seat_untouched
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.3986777Z         PASS [   0.008s] ( 202/1092) spt-daemon brainproc::tests::ready_generation_parses_stamp_and_fails_safe
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.7581673Z         PASS [   0.029s] ( 278/1092) spt-daemon effect::tests::failed_effect_is_not_recorded_applied
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.7604172Z         PASS [   0.010s] ( 288/1092) spt-daemon harnesshost::tests::prepare_fails_closed_on_wrong_kind_missing_role_and_bad_template
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.9771723Z         PASS [   0.012s] ( 321/1092) spt-daemon nethost::tests::retentive_log_is_lossless_ordered_while_ordinary_log_overwrites
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.9772840Z         PASS [   0.047s] ( 322/1092) spt-daemon livehost::tests::reconcile_keeps_driver_on_transient_read_fail_unhosts_on_dir_gone
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:16.9976737Z         PASS [   0.044s] ( 324/1092) spt-daemon nethost::tests::host_binds_endpoint_to_node_identity
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.0007237Z         PASS [   0.046s] ( 326/1092) spt-daemon nethost::tests::loopback_conn_is_a_reused_singleton_that_cross_wires_a_stream_pair
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.0451443Z         PASS [   0.079s] ( 328/1092) spt-daemon nethost::tests::two_hosts_dial_over_loopback
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.4763400Z         PASS [   0.008s] ( 358/1092) spt-daemon pump::tests::choreography_peer_step_aborts_on_first_failure
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.4783100Z         PASS [   0.162s] ( 360/1092) spt-daemon psyrelay::tests::null_stdout_driver_fails_the_guard
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.5900469Z         PASS [   0.010s] ( 402/1092) spt-daemon release::tests::production_policy_loads_fail_closed
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.6098578Z         PASS [   0.022s] ( 406/1092) spt-daemon release::tests::tampered_metadata_fails_signature
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.6365044Z         PASS [   0.009s] ( 414/1092) spt-daemon resting::tests::apply_event_echo_failure_is_loud_but_state_persists
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.7234692Z         PASS [   0.011s] ( 453/1092) spt-daemon shellhost::tests::launch_fails_closed_on_bad_templates
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.7677505Z         PASS [   0.008s] ( 467/1092) spt-daemon tunnelhub::tests::clear_evicts_the_entry
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.7717511Z         PASS [   0.008s] ( 468/1092) spt-daemon tunnelhub::tests::dead_generation_stream_is_never_surfaced
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.7741420Z         PASS [   0.006s] ( 469/1092) spt-daemon tunnelhub::tests::entries_are_per_owner
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.7788460Z         PASS [   0.007s] ( 470/1092) spt-daemon tunnelhub::tests::relink_overwrites_the_entry_with_a_fresh_generation
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:17.7818700Z         PASS [   0.008s] ( 472/1092) spt-daemon tunnelhub::tests::resolve_serves_each_end_under_the_opening_token
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:18.0473297Z         PASS [   1.091s] ( 504/1092) spt-daemon nethost::tests::loopback_tunnel_backpressures_without_loss
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:22.5346180Z         PASS [   0.155s] ( 610/1092) spt-live reconcile::tests::failed_turn_preserves_everything
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.0761848Z         PASS [   0.010s] ( 661/1092) spt-net net::mesh::seedproof::tests::forged_proof_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.0764317Z         PASS [   0.011s] ( 662/1092) spt-net net::mesh::seedproof::tests::cross_connection_replay_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.1072355Z         PASS [   0.007s] ( 669/1092) spt-net net::mesh::seedproof::tests::wrong_epoch_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.1087266Z         PASS [   0.008s] ( 670/1092) spt-net net::mesh::seedproof::tests::wrong_subnet_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.1869348Z         PASS [   0.008s] ( 684/1092) spt-net net::pairing::ratelimit::tests::failures_impose_exponential_backoff
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.3390017Z         PASS [   0.092s] ( 698/1092) spt-net net::pairing::spake::tests::stale_epoch_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.3594046Z         PASS [   0.087s] ( 699/1092) spt-net net::pairing::spake::tests::stale_step_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.4041156Z         PASS [   0.097s] ( 706/1092) spt-net net::pairing::spake::tests::substituted_pubkey_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.4094942Z         PASS [   0.096s] ( 708/1092) spt-net net::pairing::spake::tests::tampered_tag_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.4198104Z         PASS [   0.010s] ( 712/1092) spt-net net::pairing::transcript::tests::diverged_shared_secret_fails_verification
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.4358178Z         PASS [   0.099s] ( 718/1092) spt-net net::pairing::spake::tests::wrong_code_fails_at_confirmation_not_spake
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.4578711Z         PASS [   0.096s] ( 722/1092) spt-net net::pairing::spake::tests::wrong_subnet_fails
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.6400092Z         PASS [   0.179s] ( 774/1092) spt-net net::pairing::wire::tests::wrong_code_fails_and_records_no_membership
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.7096554Z         PASS [   0.009s] ( 842/1092) spt-runtime manifest::tests::harness_kind_with_shell_section_fails_validation
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T04:59:23.7213715Z         PASS [   0.008s] ( 849/1092) spt-runtime manifest::tests::inject_env_without_value_fails_validation


My tunnel/nethost/backpressure tests all PASS on Linux — failure is elsewhere. Find the actual failing test:


gh run view 27595180782 --log-failed 2>&1 | grep -iE 'FAIL \[|----.*STDERR|summary|tests run|failed' | grep -v 'PASS' | head -30

Find the failing nextest test


test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8360884Z      Summary [ 243.570s] 1092 tests run: 1091 passed, 1 timed out, 1 skipped
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8932592Z error: test run failed


1091 passed, **1 timed out** (not an assertion failure — a timeout on the slow Linux runner). Find which:


gh run view 27595180782 --log-failed 2>&1 | grep -iE 'TIMEOUT|timed out|TERMINATING|SLOW' | grep -v 'PASS' | head -20

Find the timed-out test


test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:00:17.8274754Z         SLOW [> 60.000s] (─────────) spt-daemon::attach attach_survives_target_brain_restart_exactly_once
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:01:17.8271932Z         SLOW [>120.000s] (─────────) spt-daemon::attach attach_survives_target_brain_restart_exactly_once
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:02:17.8278381Z         SLOW [>180.000s] (─────────) spt-daemon::attach attach_survives_target_brain_restart_exactly_once
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8297335Z  TERMINATING [>240.000s] (─────────) spt-daemon::attach attach_survives_target_brain_restart_exactly_once
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8353925Z      TIMEOUT [ 240.007s] (1092/1092) spt-daemon::attach attach_survives_target_brain_restart_exactly_once
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8359510Z     (test timed out)
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8360884Z      Summary [ 243.570s] 1092 tests run: 1091 passed, 1 timed out, 1 skipped
test (self-hosted, Linux, kitsubito)	Test (nextest)	2026-06-16T05:03:17.8361551Z      TIMEOUT [ 240.007s] (1092/1092) spt-daemon::attach attach_survives_target_brain_restart_exactly_once


The timeout is `spt-daemon::attach attach_survives_target_brain_restart_exactly_once` — a **pre-existing non-tunnel** D4b test that hit the 240s nextest cap on the slow kitsubito runner (Windows passed everything; my tunnel/nethost/backpressure tests all passed fast on Linux too). My pump change is `O(1)` per read for non-retentive streams (attach streams aren't retentive) — can't plausibly 4x it. Classic kitsubito slow-runner flake. Re-run the failed job + relay diagnosis to doyle:


gh run rerun 27595180782 --failed 2>&1 | tail -3; echo "RERUN_EXIT=$?"

Re-run the failed Linux job


"$OWL" send doyle <<'EOF'
W3 CI 27595180782 = RED, but it's a FLAKE — not the tunnel. Diagnosis (read before alarming):

- 1092 tests run: 1091 passed, 1 TIMED OUT, 1 skipped. The timeout: `spt-daemon::attach attach_survives_target_brain_restart_exactly_once` — a pre-existing D4b brain-restart test, NOT tunnel code. Hit the 240s nextest cap (SLOW>60>120>180>TERMINATING>240) on kitsubito (the slow Linux runner, shared box, n1-gate runs concurrent).
- Windows (hfenduleam) PASSED everything incl all tunnel tests. On LINUX too, ALL my tunnel work passed fast: tunnel_e2e 0.941s, loopback_tunnel_backpressures_without_loss 1.091s, all 5 tunnelhub + retentive_log + nethost streams PASS. traceability job green.
- My nethost pump change (await_ring_room) is O(1) per read for non-retentive streams (attach streams are NOT retentive → returns after one lock-check, no await) — cannot 4x the attach test into a 240s timeout. This test passed on the W2 green run; slow-runner timeouts here are load/timing non-determinism, same class as the FLAKE-LEDGER entries.

ACTION: re-ran the failed Linux job (`gh run rerun 27595180782 --failed`) — builds reused, no re-tag. Original failed logs persist at the run id for your read. Watching the re-run; will confirm green. If attach times out AGAIN I'll dig deeper (could be a genuine kitsubito regression unrelated to W3), but expecting green.
EOF
echo "EXIT=$?"

Relay flake diagnosis + re-run to doyle


➜ SENT:doyle
EXIT=0


Re-run kicked + doyle informed. Watch the re-run to completion:


sleep 6; gh run view 27595180782 2>&1 | grep -E 'test \(self-hosted, Linux|Triggered|attempt' | head -4

Confirm re-run started


Triggered via pull_request less than a minute ago
* test (self-hosted, Linux, kitsubito) (ID 81584888811)


gh run watch 27595180782 --exit-status >/dev/null 2>&1; echo "RERUN_FINAL=$?"; gh run view 27595180782 2>&1 | grep -E '^✓|^X|in [0-9]m' | head -8

Watch the re-run to completion


Command running in background with ID: bne347ak0. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\30df62c3-0b22-4b14-88de-fef422a5554f\tasks\bne347ak0.output. You will be notified when it completes. To check interim output, use Read on that file path.


Re-run watch in background (bne347ak0). I'm confident in the diagnosis: my pump change is `O(1)` no-op for non-retentive streams (which attach uses), so it can't cause a 240s timeout — it's the heavy `attach_survives_target_brain_restart` multi-process test exceeding the slow-runner cap under concurrent load.

**Status:** W3 build green except a kitsubito flake on a pre-existing non-tunnel test. Re-run kicked, doyle informed with the full diagnosis. Awaiting re-run green → doyle's impl-gate → W4. Re-run completion re-invokes me.


<task-notification>
<task-id>be1did0ug</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT-PART seq="1/4" id="d582bed4" type="msg" from="doyle">Ack — reading the failed logs myself before I accept &quot;flake&quot; (gating duty: a RED is where rubber-stamping is most tempting). Your diagnosis is plausible and the right SHAPE — a TIMEOUT (not an assertion fail) on a PRE-EXISTING test (attach_survives_target_brain_restart, D4b), Windows green incl all tunnel </EVENT-PART>
<EVENT-PART seq="2/4" id="d582bed4">tests, your tunnel tests fast on Linux too, and await_ring_room O(1) for non-retentive attach streams. That matches the slow-kitsubito timeout class in the FLAKE-LEDGER, and `gh run rerun --failed` (builds reused, no re-tag) is exactly the right move (same as the v0.7.1/v0.7.2 transient handling). I am pulling run 27595180782's failed-job log </EVENT-PART>
<EVENT-PART seq="3/4" id="d582bed4">now to confirm: single failure = that attach test, timeout not logic, zero tunnel-test failures either runner. Gate verdict holds for: re-run GREEN + my independent code read of the tunnel commits. If attach times out AGAIN, agreed — that flips from flake to a possible genuine kitsubito regression and we dig (it would be unrelated to W3, but</EVENT-PART>
<EVENT-PART seq="4/4" id="d582bed4"> it blocks the green). Stand by.</EVENT-PART></event>
</task-notification>


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


doyle aligned: gate verdict holds on re-run GREEN + his independent code read; if attach times out again it flips to a dig. He said stand by — re-run watch (bne347ak0) re-invokes me on completion, then I relay green. Holding.


<task-notification>
<task-id>be1did0ug</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="affbcf08" type="msg" from="doyle">FLAKE INDEPENDENTLY CONFIRMED — I read attempt-1's log directly, not your word for it. Sole failure: spt-daemon::attach attach_survives_target_brain_restart_exactly_once — SLOW&gt;60&gt;120&gt;180&gt;TERMINATING&gt;240 -&gt; TIMEOUT 240.007s, &quot;(test timed out)&quot; at attach.rs:600 (a timeout, NOT an assertion</EVENT-PART>
<EVENT-PART seq="2/5" id="affbcf08">/logic fail). Summary: 1092 run, 1091 passed, 1 timed out, 1 skipped. And critically, ALL your tunnel work PASSED on Linux in that same run: tunnel_e2e 0.941s, loopback_tunnel_backpressures_without_loss 1.091s, bounded_backpressure_stalls_an_unconsumed_reader 1.009s — plus Windows test job green end-to-end. A tunnel logic bug fails BOTH runn</EVENT-PART>
<EVENT-PART seq="3/5" id="affbcf08">ers deterministically; this is one pre-existing attach test timing out on the slow shared box = the FLAKE-LEDGER class, exactly as you called it. The tunnel code is not implicated.

NOTE: my docs push to main just fired its own ci.yml run on the same kitsubito box — added contention while your re-run is going. If the attach test times </EVENT-PART>
<EVENT-PART seq="4/5" id="affbcf08">out AGAIN it could be that contention rather than a regression; either way it's W3-independent. I'm reading your tunnel commits now (the code-read leg). Gate clears on: re-run green (or a clean attach pass) + my code read. Log this timeout to the FLAKE-LEDGER (attach_survives, kitsubito, 240s cap under concurrent load) so the pattern is tracke</EVENT-PART>
<EVENT-PART seq="5/5" id="affbcf08">d.</EVENT-PART></event>
</task-notification>


doyle independently confirmed the flake + flagged his docs-push added kitsubito contention. He asked me to log it to the FLAKE-LEDGER. Find it:


find . -iname '*flake*' -not -path './target/*' 2>/dev/null; grep -rln 'FLAKE-LEDGER\|FLAKE LEDGER' --include='*.md' . 2>/dev/null | grep -v target | head

Locate the FLAKE-LEDGER file


./docs/FLAKE-LEDGER.md
./M4-D9-PLAN.md


1	# Flake ledger
2
3	Tests that have failed nondeterministically, with mechanism notes and hardening
4	status. Discipline: **any flake gets an entry, not a shrug** — a second
5	occurrence upgrades the entry from "observed" to "harden it." Rig flakes from
6	the two-host ladder (docs/TWO-HOST-RUNBOOK.md) land here too.
7
8	| # | test | occurrences | mechanism | status |
9	|---|------|-------------|-----------|--------|
10	| 1 | `spt-daemon brain_restart_survives_gaplessly` | 1× hosted Windows (2026-06-03, hung) | ConPTY stall under hosted-runner load | job `timeout-minutes` bounds it; not seen self-hosted |
11	| 2 | `spt-term digest_parses_a_real_pty_stream` | 3× gravity (2026-06-03; 2026-06-04 runs 26957386850, 26966738231) | sprint-collapse assert (`digest.rs`) — PTY input echo and the child's write are **concurrent writers** into the master-read stream; back-to-back `send_line`s let a later line's echo interleave mid-`Write(…)`, breaking the tool regex for that turn | HARDENED (2026-06-04): sends serialized — each line pumped back (adaptive echo-copy count, 2s/line bound) before the next, so no concurrent writer races an earlier line; failure asserts now print the raw transcript |
12	| 3 | `spt-daemon sync.rs torn_pull` + `concurrent_writes` | 1× (2026-06-03) | `wait_for_stream` deadline too tight under load | HARDENED: `wait_for_stream_except` 2s→10s; no recurrence |
13	| 4 | `spt-live context::write_context_suppresses_stale_llm` | 1× gravity (2026-06-04 run 26967808321) | test calls `write_context` (reads SPT_HOME node identity + epoch stamp) WITHOUT the `with_home` lock — a parallel `with_home` test's env swap + temp-dir teardown races the stamp's epoch write into a deleted home (`NotFound`) | HARDENED (2026-06-04): test wrapped in `with_home` — serializes on the home lock like every other SPT_HOME-touching test |
14

# Flake ledger

Tests that have failed nondeterministically, with mechanism notes and hardening
status. Discipline: **any flake gets an entry, not a shrug** — a second
occurrence upgrades the entry from "observed" to "harden it." Rig flakes from
the two-host ladder (docs/TWO-HOST-RUNBOOK.md) land here too.

| # | test | occurrences | mechanism | status |
|---|------|-------------|-----------|--------|
| 1 | `spt-daemon brain_restart_survives_gaplessly` | 1× hosted Windows (2026-06-03, hung) | ConPTY stall under hosted-runner load | job `timeout-minutes` bounds it; not seen self-hosted |
| 2 | `spt-term digest_parses_a_real_pty_stream` | 3× gravity (2026-06-03; 2026-06-04 runs 26957386850, 26966738231) | sprint-collapse assert (`digest.rs`) — PTY input echo and the child's write are **concurrent writers** into the master-read stream; back-to-back `send_line`s let a later line's echo interleave mid-`Write(…)`, breaking the tool regex for that turn | HARDENED (2026-06-04): sends serialized — each line pumped back (adaptive echo-copy count, 2s/line bound) before the next, so no concurrent writer races an earlier line; failure asserts now print the raw transcript |
| 3 | `spt-daemon sync.rs torn_pull` + `concurrent_writes` | 1× (2026-06-03) | `wait_for_stream` deadline too tight under load | HARDENED: `wait_for_stream_except` 2s→10s; no recurrence |
| 4 | `spt-live context::write_context_suppresses_stale_llm` | 1× gravity (2026-06-04 run 26967808321) | test calls `write_context` (reads SPT_HOME node identity + epoch stamp) WITHOUT the `with_home` lock — a parallel `with_home` test's env swap + temp-dir teardown races the stamp's epoch write into a deleted home (`NotFound`) | HARDENED (2026-06-04): test wrapped in `with_home` — serializes on the home lock like every other SPT_HOME-touching test |


4 entries here (memory's #5/#6 are on a later main, not this branch — I'll use #7 to avoid collision at merge). Log the attach timeout:


Edit

| 4 | `spt-live context::write_context_suppresses_stale_llm` | 1× gravity (2026-06-04 run 26967808321) | test calls `write_context` (reads SPT_HOME node identity + epoch stamp) WITHOUT the `with_home` lock — a parallel `with_home` test's env swap + temp-dir teardown races the stamp's epoch write into a deleted home (`NotFound`) | HARDENED (2026-06-04): test wrapped in `with_home` — serializes on the home lock like every other SPT_HOME-touching test |

| 4 | `spt-live context::write_context_suppresses_stale_llm` | 1× gravity (2026-06-04 run 26967808321) | test calls `write_context` (reads SPT_HOME node identity + epoch stamp) WITHOUT the `with_home` lock — a parallel `with_home` test's env swap + temp-dir teardown races the stamp's epoch write into a deleted home (`NotFound`) | HARDENED (2026-06-04): test wrapped in `with_home` — serializes on the home lock like every other SPT_HOME-touching test |
| 7 | `spt-daemon::attach attach_survives_target_brain_restart_exactly_once` | 1× kitsubito (2026-06-16 run 27595180782 attempt 1, M11-W3) | **TIMEOUT, not an assertion fail** — SLOW>60>120>180>TERMINATING, nextest TIMEOUT at the 240s cap (`attach.rs:600`). A heavy multi-process brain-restart-exactly-once test on the slow shared kitsubito box under concurrent load (n1-gate on the same runner + a parallel `ci.yml` docs-publish run firing simultaneously). W3-INDEPENDENT: Windows green end-to-end; ALL tunnel tests passed fast on Linux too (tunnel_e2e 0.941s, loopback_tunnel_backpressures_without_loss 1.091s) — a logic bug fails both runners deterministically, this timed out on one under load | observed; cleared by `gh run rerun --failed` (builds reused, no re-tag). Harden if it recurs — candidate: raise this test's per-test nextest timeout, or serialize it off the concurrent n1-gate slot |


The file C:\Users\decid\Documents\projects\spt-core\docs\FLAKE-LEDGER.md has been updated successfully. (file state is current in your context — no need to Read it back)

| 4 | `spt-live context::write_context_suppresses_stale_llm` | 1× gravity (2026-06-04 run 26967808321) | test calls `write_context` (reads SPT_HOME node identity + epoch stamp) WITHOUT the `with_home` lock — a parallel `with_home` test's env swap + temp-dir teardown races the stamp's epoch write into a deleted home (`NotFound`) | HARDENED (2026-06-04): test wrapped in `with_home` — serializes on the home lock like every other SPT_HOME-touching test |
| 7 | `spt-daemon::attach attach_survives_target_brain_restart_exactly_once` | 1× kitsubito (2026-06-16 run 27595180782 attempt 1, M11-W3) | **TIMEOUT, not an assertion fail** — SLOW>60>120>180>TERMINATING, nextest TIMEOUT at the 240s cap (`attach.rs:600`). A heavy multi-process brain-restart-exactly-once test on the slow shared kitsubito box under concurrent load (n1-gate on the same runner + a parallel `ci.yml` docs-publish run firing simultaneously). W3-INDEPENDENT: Windows green end-to-end; ALL tunnel tests passed fast on Linux too (tunnel_e2e 0.941s, loopback_tunnel_backpressures_without_loss 1.091s) — a logic bug fails both runners deterministically, this timed out on one under load | observed; cleared by `gh run rerun --failed` (builds reused, no re-tag). Harden if it recurs — candidate: raise this test's per-test nextest timeout, or serialize it off the concurrent n1-gate slot |

# Flake ledger

Tests that have failed nondeterministically, with mechanism notes and hardening
status. Discipline: **any flake gets an entry, not a shrug** — a second
occurrence upgrades the entry from "observed" to "harden it." Rig flakes from
the two-host ladder (docs/TWO-HOST-RUNBOOK.md) land here too.

| # | test | occurrences | mechanism | status |
|---|------|-------------|-----------|--------|
| 1 | `spt-daemon brain_restart_survives_gaplessly` | 1× hosted Windows (2026-06-03, hung) | ConPTY stall under hosted-runner load | job `timeout-minutes` bounds it; not seen self-hosted |
| 2 | `spt-term digest_parses_a_real_pty_stream` | 3× gravity (2026-06-03; 2026-06-04 runs 26957386850, 26966738231) | sprint-collapse assert (`digest.rs`) — PTY input echo and the child's write are **concurrent writers** into the master-read stream; back-to-back `send_line`s let a later line's echo interleave mid-`Write(…)`, breaking the tool regex for that turn | HARDENED (2026-06-04): sends serialized — each line pumped back (adaptive echo-copy count, 2s/line bound) before the next, so no concurrent writer races an earlier line; failure asserts now print the raw transcript |
| 3 | `spt-daemon sync.rs torn_pull` + `concurrent_writes` | 1× (2026-06-03) | `wait_for_stream` deadline too tight under load | HARDENED: `wait_for_stream_except` 2s→10s; no recurrence |
| 4 | `spt-live context::write_context_suppresses_stale_llm` | 1× gravity (2026-06-04 run 26967808321) | test calls `write_context` (reads SPT_HOME node identity + epoch stamp) WITHOUT the `with_home` lock — a parallel `with_home` test's env swap + temp-dir teardown races the stamp's epoch write into a deleted home (`NotFound`) | HARDENED (2026-06-04): test wrapped in `with_home` — serializes on the home lock like every other SPT_HOME-touching test |

+| 7 | `spt-daemon::attach attach_survives_target_brain_restart_exactly_once` | 1× kitsubito (2026-06-16 run 27595180782 attempt 1, M11-W3) | **TIMEOUT, not an assertion fail** — SLOW>60>120>180>TERMINATING, nextest TIMEOUT at the 240s cap (`attach.rs:600`). A heavy multi-process brain-restart-exactly-once test on the slow shared kitsubito box under concurrent load (n1-gate on the same runner + a parallel `ci.yml` docs-publish run firing simultaneously). W3-INDEPENDENT: Windows green end-to-end; ALL tunnel tests passed fast on Linux too (tunnel_e2e 0.941s, loopback_tunnel_backpressures_without_loss 1.091s) — a logic bug fails both runners deterministically, this timed out on one under load | observed; cleared by `gh run rerun --failed` (builds reused, no re-tag). Harden if it recurs — candidate: raise this test's per-test nextest timeout, or serialize it off the concurrent n1-gate slot |


Logged. Commit locally only — must NOT push (a new HEAD would restart the in-flight re-run + stale doyle's gate @221c0e7). Rides the next push post-gate:


Bash

git add docs/FLAKE-LEDGER.md && git commit -q -m "$(cat <<'EOF'
docs: log attach_survives timeout to the FLAKE-LEDGER (#7)

kitsubito 240s nextest-cap TIMEOUT (not a logic fail) on the pre-existing
attach_survives_target_brain_restart_exactly_once under concurrent load
(n1-gate + a parallel ci.yml docs run on the shared box), M11-W3 run
27595180782 attempt 1. W3-independent: Windows green, all tunnel tests
fast both runners. Cleared by rerun --failed. Holds local — rides the
next push (NOT pushed now: would restart the in-flight re-run @221c0e7).

Co-authored by: todlando
EOF
)" && git log --oneline -1

Commit FLAKE-LEDGER entry locally


9ebb1c6 docs: log attach_survives timeout to the FLAKE-LEDGER (#7)


Logged @9ebb1c6 (local). Brief ack to doyle + note the contention he flagged:


"$OWL" send doyle <<'EOF'
FLAKE-LEDGER logged @9ebb1c6 (entry #7: attach_survives, kitsubito 240s cap, concurrent-load, W3-independent; harden-if-recurs = raise its per-test timeout / serialize off the n1-gate slot). Committed LOCAL only — not pushed, so it won't restart the in-flight re-run @221c0e7 (rides the next push post-gate). Noted your docs ci.yml is contending on kitsubito now; if attach times out again I'll treat it as contention (re-run once the box is quiet) not a W3 regression. Watching the re-run — will ping on green for your verdict.
EOF
echo "EXIT=$?"

Ack ledger logged + contention note to doyle


➜ SENT:doyle
EXIT=0


Acked doyle. FLAKE-LEDGER #7 logged (local @9ebb1c6). Re-run watch (bne347ak0) in flight — re-invokes me on completion, then I confirm green + ping doyle for the verdict. doyle's reading the tunnel commits in parallel (code-read leg). Holding for the re-run.


<task-notification>
<task-id>be1did0ug</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="0abcb1d4" type="msg" from="doyle">FLAKE-LEDGER #7 logged correctly, and keeping it LOCAL/unpushed (so it won't restart the in-flight re-run @221c0e7) is exactly right — it rides the next post-gate push. Agreed on the contention handling: if attach times out again, treat it as my-docs-ci-on-kitsubito contention, re-run when the box is quiet, not a W3 r</EVENT-PART>
<EVENT-PART seq="2/6" id="0abcb1d4">egression.

CODE-READ LEG OF THE GATE: PASS. I read the tunnel commits @221c0e7 myself and every condition holds:
- Broker-homed TunnelHub (Option A), holds only ids + the opening link token; bytes never touch the registry.
- R1 no-stale: stream ids stamped with the opening link token; owner_stream/shell_stream serve only on a toke</EVENT-PART>
<EVENT-PART seq="3/6" id="0abcb1d4">n match, so a relinked fresh token can never match a pre-break stream — crash-proof even if clear() never ran. Mirrors W2 drive R1 exactly.
- The frame-loss fix I conditioned on: append never evicts for retentive (cap!=0); await_ring_room parks the read pump at retentive_cap so transport flow-control lags the SENDER (lag-never-loss); non-</EVENT-PART>
<EVENT-PART seq="4/6" id="0abcb1d4">tunnel retentive_cap==0 makes the gate a no-op. Precisely bounded-with-backpressure.
- close_shell fires tunnel_clear at the same link-break seam as drive-clear.
- tunnel_e2e: 200KB multi-write byte-exact BOTH directions + NUL/&lt;EVENT-looking opacity + link-break close + R1 stale-pre-break-never-served (assert_ne token_a/token_b). back</EVENT-PART>
<EVENT-PART seq="5/6" id="0abcb1d4">pressure unit: peak&lt;=CAP bounded + sender-lags + lossless byte-exact after catch-up — the exact test I asked for.
- C2 honored; cross-node-on-LAN [twohost] + ADR-0020 enumeration deferred to W5 (logged, not claimed).

So the ONLY thing between W3 and PASS is the re-run Linux test going green (the confirmed attach flake). Ping me </EVENT-PART>
<EVENT-PART seq="6/6" id="0abcb1d4">on green and I stamp the formal W3 GATE PASS immediately — no further review needed, the code is already cleared.</EVENT-PART></event>
</task-notification>


doyle: **code-read leg = PASS**, every condition cleared. Only re-run Linux green stands between W3 and GATE PASS. Check the re-run watch:


cat "C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\30df62c3-0b22-4b14-88de-fef422a5554f\tasks\bne347ak0.output" 2>/dev/null; echo "---live status---"; gh run view 27595180782 2>&1 | grep -E '^✓|^X|^\*|Linux.*in [0-9]' | head -6

Check re-run watch output + live status


---live status---
* m11-shell-substrate ci SaberMage/spt-core#16 · 27595180782
* test (self-hosted, Linux, kitsubito) (ID 81584888811)
✓ n1-gate (self-hosted, Windows, hfenduleam) in 1m41s (ID 81584889431)
✓ n1-gate (self-hosted, Linux, kitsubito) in 1m13s (ID 81584899152)
✓ traceability in 7s (ID 81584905146)
✓ test (self-hosted, Windows, hfenduleam) in 6m18s (ID 81584905984)


Re-run's Linux test job still running (others green: Windows test, both n1-gate, traceability). Background watch bne347ak0 hasn't fired yet — it re-invokes me when the Linux job settles. Holding; on green I ping doyle and he stamps W3 GATE PASS.


<task-notification>
<task-id>bne347ak0</task-id>
<tool-use-id>toolu_014G2iRjCHbieMX5bfcbhhgg</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\30df62c3-0b22-4b14-88de-fef422a5554f\tasks\bne347ak0.output</output-file>
<status>completed</status>
<summary>Background command "Watch the re-run to completion" completed (exit code 0)</summary>
</task-notification>


Re-run watch completed. Confirm the full run is green:


gh run view 27595180782 2>&1 | grep -E 'm11-shell|^✓|^X|test.*Linux.*in [0-9]' | head -8; echo "---overall---"; gh run list --branch m11-shell-substrate --limit 1 2>&1

Confirm re-run final status


✓ m11-shell-substrate ci SaberMage/spt-core#16 · 27595180782
✓ test (self-hosted, Linux, kitsubito) in 5m4s (ID 81584888811)
✓ n1-gate (self-hosted, Windows, hfenduleam) in 1m41s (ID 81584889431)
✓ n1-gate (self-hosted, Linux, kitsubito) in 1m13s (ID 81584899152)
✓ traceability in 7s (ID 81584905146)
✓ test (self-hosted, Windows, hfenduleam) in 6m18s (ID 81584905984)
---overall---
completed	success	M11 shell-substrate extensions (W1: per-capability act-gates, REQ-CONSENT-3)	ci	m11-shell-substrate	pull_request	27595180782	11m57s	2026-06-16T05:06:54Z


CI **27595180782 = SUCCESS** — all 5 jobs green both runners; the attach re-run passed (Linux test 5m4s green). Flake cleared. Ping doyle for the gate stamp:


"$OWL" send doyle <<'EOF'
W3 CI GREEN. Run 27595180782 = SUCCESS, all 5 jobs both runners: test Linux 5m4s ✓ (attach re-run passed — flake cleared), test Windows ✓, n1-gate Linux ✓, n1-gate Windows ✓, traceability ✓. Your code-read leg already PASS. That's both legs — stamp the W3 GATE PASS when ready. Standing by for the verdict, then I roll into W4 (Gateway-as-owner capstone, REQ-SHELL-5).
EOF
echo "EXIT=$?"

Ping doyle: re-run green, request gate stamp


Pinged doyle — both gate legs done (code-read PASS + CI green), stamp imminent. While awaiting it, prep W4 (redirect-safe): read the W4 task + REQ-SHELL-5 state + the mock-gateway E2E pattern.


grep -n -A6 'REQ-SHELL-5' traceable-reqs.toml | head -12; echo "---gateway e2e files---"; find crates/spt/tests -iname '*gateway*' 2>/dev/null; grep -rln 'EndpointFamily::Gateway\|endpoint-type.*gateway\|"gateway"' crates/spt/tests crates/spt-daemon/tests 2>/dev/null | head

Check REQ-SHELL-5 + gateway E2E pattern


962:# each wave lands evidence. Planned activations: REQ-SHELL-5 doc/impl at T0 (the
963-# ownership audit — proof-not-change), REQ-CONSENT-3 at W1, REQ-SHELL-3 at W2,
964:# REQ-SHELL-4 at W3, REQ-SHELL-5 unit/int at the W4 Gateway-owner capstone.) ---
965-[[requirements]]
966-id = "REQ-SHELL-3"
967-title = "Drive channel (owner->shell, REST-only, never-spooled, latest-wins): the owner->shell mirror of sensory for continuous real-time control (scroll/crank/stick/avatar) — a [shell.drive] manifest vocab + EVENT_TYPE_DRIVE frame, delivered to the ONLINE binary only via a single live slot (a new frame supersedes an undelivered one — no spool, no queue, no replay on relink), dropped-with-diagnostic if the shell is offline; cross-node rides the ephemeral link (REST class), never the durable shell spool. Commands = discrete+durable; drive = continuous+ephemeral (CONTEXT:260, minted 2026-06-11 Gateway grill)."
968-required_stages = ["impl", "unit", "int"]  # activated M11-W2 (machinery + mock exerciser only — doyle C1: no real GameRobot/usbip shell code). impl/unit across T2.1 (manifest [shell.drive] + EVENT_TYPE_DRIVE + compose_drive_frame), T2.2 (broker-held DriveHub slot: latest-wins write, take-and-clear, link-token no-replay stamp; `spt shell drive` owner-write + `api drive-poll` shell-read; close_shell clears), T2.3 (cross-node drive_channel_write shared core + SHELL_LINK_DRIVE; drop-not-wake / no-owner-durability / no-spool-remote divergences). int = the same-node real-socket E2E (tests/drive_e2e.rs: drop-at-write-offline, latest-wins, take-and-clear, NO-SPOOL, clear-on-link-break, no-slot-file) + the rig cross-node drop-not-wake rung (tests/twohost.rs, [twohost]-gated)
969-[[requirements]]
970-id = "REQ-SHELL-4"
--
978:id = "REQ-SHELL-5"
979-title = "Shell ownership is owner-type-agnostic: any non-Shell endpoint type may own/spawn/drive/command/link a shell (Gateway the named first) — control-exclusivity keys on the owner endpoint_id, NEVER on the owner's endpoint type. No ownership path (mint, launch, owner-from-link, cmd, drive, tunnel, sleep/wake, owner-shutdown) inspects the owner's type (CONTEXT:264, ratified 2026-06-11 Gateway grill)."
---gateway e2e files---
crates/spt/tests/gateway_e2e.rs
crates/spt/tests/gateway_e2e.rs


Check REQ-SHELL-5 current stages


required_stages = ["doc", "impl"]  # M11-T0 activated (the code-only ownership audit — proof not change): doc = CONTEXT §Shell model "Owner-linked, exclusive" owner-type-agnostic note; impl = shellhost::find_shell_by_token (the "owner from the link" resolution returns an opaque owner-id with NO type inspection — the load-bearing type-agnostic path). unit/int activate at the W4 Gateway-owner capstone (a Gateway-typed owner drives/cmds/tunnels/links a shell identically)


W4 scoped: REQ-SHELL-5 is `[doc,impl]` green from T0; W4's single capstone (T4.2) is a **Gateway-typed-owner E2E** — spawn/cmd/drive/**tunnel**/link all honored as an agent owner → activates `unit/int`. Pattern exists (`gateway_e2e.rs` + my `tunnel_e2e.rs`/`drive_e2e.rs`). Lighter than W3.

**W3 status: CI green both runners, doyle code-read PASS — both gate legs done, formal GATE PASS stamp imminent.** Not starting W4 commits until the stamp (W4 is gated on W3 PASS); won't push the local FLAKE-LEDGER commit until then either (would restart CI). doyle's stamp re-invokes me → I roll into W4 (or checkpoint+commune the W4 handoff if context warrants). Holding for the stamp.


<task-notification>
<task-id>be1did0ug</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="81f72110" type="msg" from="doyle">W3 GATE PASS — stamped. Independently confirmed CI run 27595180782: conclusion=success, all 5 jobs green BOTH runners (test Linux 5m4s after the attach re-flake cleared, test Windows, n1-gate x2, traceability; twohost SKIP expected). Code-read leg already PASS, verified @221c0e7. Both legs done.

Conditions all </EVENT-PART>
<EVENT-PART seq="2/5" id="81f72110">met:
- Broker-homed TunnelHub (Option A); R1 no-stale via opening-link-token stamp — crash-proof even if clear() never runs, mirrors W2 drive R1.
- The frame-loss fix exactly as conditioned: retentive append never evicts (lossless) + await_ring_room parks the read pump at retentive_cap so transport flow-control lags the SENDER (lag-nev</EVENT-PART>
<EVENT-PART seq="3/5" id="81f72110">er-loss, bounded, no OOM); non-tunnel retentive_cap==0 = no-op.
- close_shell fires tunnel_clear at the link-break seam.
- tunnel_e2e: 200KB multi-write byte-exact BOTH directions + NUL/&lt;EVENT-opacity + link-break close + R1 stale-pre-break-never-served. backpressure unit: bounded + sender-lags + lossless byte-exact after catch-up.<br</EVENT-PART>
<EVENT-PART seq="4/5" id="81f72110">>- C2 honored; cross-node-on-LAN [twohost] + ADR-0020 enumeration + on-LAN docs correctly DEFERRED to W5 (logged, not claimed).

W5 carries (5): drive [twohost] rung, tunnel [twohost] rung, how-to subnet topic, ADR-0020 tunnel exemption enumeration, on-LAN posture docs.

CLEARED to W4 — Gateway-as-owner capstone, REQ-SHELL-5. It'</EVENT-PART>
<EVENT-PART seq="5/5" id="81f72110">s the ownership-audit capstone (proof-not-change: a Gateway endpoint owns shells; control-exclusivity keys on the owner endpoint_id, never the owner's TYPE). Bring the SHAPE before deep commit — design-gate same as W2/W3. Clean wave; good work.</EVENT-PART></event>
</task-notification>


**W3 GATE PASS — stamped by doyle.** Both legs (CI green both runners + independent code-read). Cleared to W4 (Gateway-as-owner capstone, REQ-SHELL-5) — wants the SHAPE before deep commit, same as W2/W3.

W3 is a complete, gated milestone and my context is very deep. Clean checkpoint boundary. Let me update memory, send doyle the W4 design-gate shape (so his review runs while I checkpoint), and commune the handoff.


Edit

description: "M11-W3 shell tunnel (REQ-SHELL-4) — BUILT + pushed PR#16 (CI 27595180782); evidence-land to doyle for impl-gate; next = W4 Gateway capstone"

description: "M11-W3 shell tunnel (REQ-SHELL-4) — GATE PASS (doyle, CI 27595180782 green both runners + code-read); cleared to W4 Gateway-owner capstone (REQ-SHELL-5)"


<tool_use_error>File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.</tool_use_error>

Error: File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.


1	---
2	name: m11-w3-progress
3	description: "M11-W3 shell tunnel (REQ-SHELL-4) — BUILT + pushed PR#16 (CI 27595180782); evidence-land to doyle for impl-gate; next = W4 Gateway capstone"
4	metadata:
5	  node_type: memory
6	  type: project
7	  originSessionId: f6396b18-8552-45fc-a0e0-b42e4a96b2ae
8	---
9
10	**STATUS: W3 BUILD COMPLETE + pushed PR#16 @221c0e7, CI run 27595180782 (both runners).** 8 commits: T3.1 manifest @88a9548 · gate-fold @6462f16 · T3.2a hub @692f810 · T3.2b close-hook @fa9aa62 · T3.2c retentive backpressured NetHost streams @7bf91bd (doyle's lossless gate condition) · T3.2d broker-homed byte bridge Option-A @6f3065a · T3.2e open-hook+surfaces @41b715f · T3.3 E2E+int @221c0e7. REQ-SHELL-4 [doc,impl,unit,int] traceable EXIT 0; clippy/xtask green; FULL suite green local (spt-daemon 309 lib+integration, spt 157+E2E, zero regression). Evidence-land sent to doyle → AWAITING impl-gate on CI-green, then W4 (Gateway capstone, REQ-SHELL-5). W5 carries: tunnel + drive [twohost] rungs, ADR-0020 EVENT-exemption enumeration, on-LAN docs, subnet how-to. Design/build detail below is historical reference.
11
12	M11-W3 = shell TUNNEL (REQ-SHELL-4): opaque reliable-ordered byte stream, a 5th surface (NOT a typed channel), first consumer usbip URB. Branch `m11-shell-substrate`. doyle DESIGN-GATE = **GO** (2026-06-15).
13
14	**doyle gate (locked):** Q1 SINGLE tunnel per link (multiplex deferred). Q2 dedicated `spt shell tunnel <id>` (owner write-in/subscribe-out) + shell-side `api tunnel` raw-duplex verb. Q3 NEW raw `api tunnel` verb, NOT child-relay `api poll`. R1: online-gen/link-token stamp on held stream_id = crash-proof load-bearing leg (close_shell drop = mere cleanup); prove open→break→relink-fresh-gen→old stream_id inert in T3.3. R2: W3 same-node loopback proves MECHANISM only; cross-node-on-LAN real-Iroh = a W5 `[twohost]` rung beside the drive rung (NO loopback overclaim; docs say "mechanism proven same-node; cross-node-on-LAN is W5 [twohost]"). DOC: add tunnel to ADR-0020 `<EVENT>`-exemption enumeration (light, not new ADR). All logged in M11-PLAN T3.x/T5.x.
15
16	**Substrate = D4 net-stream** (exists, fits): `NetHost::open_stream(conn_id)` = broker-OWNED bidi QUIC stream (survives brain restart, ADR-0004 B); `brain.net_open_stream/net_stream_send/net_stream_subscribe`. Loopback conn cross-wires operator+peer rows in-process. Same machinery `request_shell_link` rides; tunnel keeps it open + opaque. Shell-link itself is REQUEST/REPLY per-action (no held link-conn).
17
18	**LANDED (commits on branch):** T3.1 manifest `[shell.tunnel]` (enable+protocol label, validate) @88a9548 · plan gate-fold @6462f16 · T3.2a broker `TunnelHub` registry + control channel (`crates/spt-daemon/src/tunnelhub.rs`, holds owner_stream+shell_stream+token per shell, token-gated resolve = R1, 5 unit tests) @692f810 · T3.2b `close_shell` fires `tunnel_clear` @fa9aa62. REQ-SHELL-4 active `[doc,impl,unit]`, traceable EXIT 0, clippy workspace green. (Plus @8efaf76 W5 subnet how-to carry.)
19
20	**NEXT (resume here):** T3.2 OPEN-hook + the bridge. Open hook at `api bind-shell` (`crates/spt/src/api/mod.rs:327`, shell-online site) — if manifest tunnel-enabled, open the loopback stream + register in TunnelHub. THE HARD PART: dispatch (`dispatch.rs:206-214`) claims peer rows by FIRST-RECORD content demux (`kind=request`→shell-link); a bytes-less peer row isn't claimed. Tunnel needs (1) a new first-record classification `kind=tunnel`, (2) a `serve_tunnel` relay loop that BRIDGES the held QUIC stream to the EXTERNAL `api tunnel` surface (mock shell-side is an api caller, not an internal serve) — broker-buffered in/out byte queues per shell. serve_attach (`attach.rs`) is the closest bridge template. Then `spt shell tunnel` CLI (mirror cli.rs shell drive ~5660) + `api tunnel` verb (mirror api drive-poll). Then T3.3 same-node mock E2E (opaque `<EVENT`-looking bytes byte-exact round-trip + link-break close + R1 stale-stream-inert) → activate REQ-SHELL-4 int. Then W4 Gateway capstone, W5 rig+docs (incl tunnel+drive [twohost] rungs, ADR-0020 cross-ref, subnet how-to).
21
22	Pre-flight every commit: `cargo clippy --workspace --all-targets -D warnings` + `traceable-reqs check` (EXIT 0) + `cargo run -p xtask -- check`; schema regen = `SPT_BLESS=1 cargo test -p spt-runtime checked_in_schema_is_current` (NOT the `manifest_schema` filter — that misses the bless test). Any CLI add → `cargo run -p xtask -- gen` + no milestone/REQ codes in clap help. See [[m11-w2-progress]] [[broker-is-daemon-state-anchor]] [[cli-command-docs-drift]].
23
24	---
25	**doyle GATE VERDICT (2026-06-15): W3 PASS.** Independently verified — read tunnel code @221c0e7 + confirmed CI run 27595180782 myself (conclusion=success, all 5 jobs both runners; test Linux 5m4s post-reflake). All conditions hold: broker-homed TunnelHub (Option A); R1 no-stale via opening-link-token stamp (crash-proof, mirrors W2 drive); frame-loss fix exactly as conditioned (retentive append never evicts + await_ring_room parks pump at retentive_cap → flow-control lags SENDER → lag-never-loss/bounded/no-OOM; non-tunnel no-op); close_shell tunnel_clear; tunnel_e2e 200KB multi-write byte-exact BOTH dirs + NUL/<EVENT-opacity + link-break close + R1 no-stale-across-relink; backpressure unit bounded+lags+lossless. **FLAKE:** first CI RED = attach_survives timeout 240s on kitsubito (slow shared box, contention incl my docs ci.yml) — W3-independent, all tunnel tests green both runners; todlando FLAKE-LEDGER #7; rerun --failed cleared. **W5 CARRIES (5):** drive [twohost] + tunnel [twohost] rungs + how-to subnet + ADR-0020 tunnel-exemption enumeration + on-LAN docs. **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).


The memory body already captures W3 PASS (auto-updated). Just refresh the description line:


description: "M11-W3 shell tunnel (REQ-SHELL-4) — BUILT + pushed PR#16 (CI 27595180782); evidence-land to doyle for impl-gate; next = W4 Gateway capstone"

description: "M11-W3 shell tunnel (REQ-SHELL-4) — GATE PASS (doyle, CI 27595180782 green both runners + code-read); cleared to W4 Gateway-owner capstone (REQ-SHELL-5), bring shape for design-gate"


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\m11-w3-progress.md has been updated successfully. (file state is current in your context — no need to Read it back)

---
name: m11-w3-progress
description: "M11-W3 shell tunnel (REQ-SHELL-4) — BUILT + pushed PR#16 (CI 27595180782); evidence-land to doyle for impl-gate; next = W4 Gateway capstone"
metadata:
  node_type: memory
  type: project
  originSessionId: f6396b18-8552-45fc-a0e0-b42e4a96b2ae
---

**STATUS: W3 BUILD COMPLETE + pushed PR#16 @221c0e7, CI run 27595180782 (both runners).** 8 commits: T3.1 manifest @88a9548 · gate-fold @6462f16 · T3.2a hub @692f810 · T3.2b close-hook @fa9aa62 · T3.2c retentive backpressured NetHost streams @7bf91bd (doyle's lossless gate condition) · T3.2d broker-homed byte bridge Option-A @6f3065a · T3.2e open-hook+surfaces @41b715f · T3.3 E2E+int @221c0e7. REQ-SHELL-4 [doc,impl,unit,int] traceable EXIT 0; clippy/xtask green; FULL suite green local (spt-daemon 309 lib+integration, spt 157+E2E, zero regression). Evidence-land sent to doyle → AWAITING impl-gate on CI-green, then W4 (Gateway capstone, REQ-SHELL-5). W5 carries: tunnel + drive [twohost] rungs, ADR-0020 EVENT-exemption enumeration, on-LAN docs, subnet how-to. Design/build detail below is historical reference.

M11-W3 = shell TUNNEL (REQ-SHELL-4): opaque reliable-ordered byte stream, a 5th surface (NOT a typed channel), first consumer usbip URB. Branch `m11-shell-substrate`. doyle DESIGN-GATE = **GO** (2026-06-15).

**doyle gate (locked):** Q1 SINGLE tunnel per link (multiplex deferred). Q2 dedicated `spt shell tunnel <id>` (owner write-in/subscribe-out) + shell-side `api tunnel` raw-duplex verb. Q3 NEW raw `api tunnel` verb, NOT child-relay `api poll`. R1: online-gen/link-token stamp on held stream_id = crash-proof load-bearing leg (close_shell drop = mere cleanup); prove open→break→relink-fresh-gen→old stream_id inert in T3.3. R2: W3 same-node loopback proves MECHANISM only; cross-node-on-LAN real-Iroh = a W5 `[twohost]` rung beside the drive rung (NO loopback overclaim; docs say "mechanism proven same-node; cross-node-on-LAN is W5 [twohost]"). DOC: add tunnel to ADR-0020 `<EVENT>`-exemption enumeration (light, not new ADR). All logged in M11-PLAN T3.x/T5.x.

**Substrate = D4 net-stream** (exists, fits): `NetHost::open_stream(conn_id)` = broker-OWNED bidi QUIC stream (survives brain restart, ADR-0004 B); `brain.net_open_stream/net_stream_send/net_stream_subscribe`. Loopback conn cross-wires operator+peer rows in-process. Same machinery `request_shell_link` rides; tunnel keeps it open + opaque. Shell-link itself is REQUEST/REPLY per-action (no held link-conn).

**LANDED (commits on branch):** T3.1 manifest `[shell.tunnel]` (enable+protocol label, validate) @88a9548 · plan gate-fold @6462f16 · T3.2a broker `TunnelHub` registry + control channel (`crates/spt-daemon/src/tunnelhub.rs`, holds owner_stream+shell_stream+token per shell, token-gated resolve = R1, 5 unit tests) @692f810 · T3.2b `close_shell` fires `tunnel_clear` @fa9aa62. REQ-SHELL-4 active `[doc,impl,unit]`, traceable EXIT 0, clippy workspace green. (Plus @8efaf76 W5 subnet how-to carry.)

**NEXT (resume here):** T3.2 OPEN-hook + the bridge. Open hook at `api bind-shell` (`crates/spt/src/api/mod.rs:327`, shell-online site) — if manifest tunnel-enabled, open the loopback stream + register in TunnelHub. THE HARD PART: dispatch (`dispatch.rs:206-214`) claims peer rows by FIRST-RECORD content demux (`kind=request`→shell-link); a bytes-less peer row isn't claimed. Tunnel needs (1) a new first-record classification `kind=tunnel`, (2) a `serve_tunnel` relay loop that BRIDGES the held QUIC stream to the EXTERNAL `api tunnel` surface (mock shell-side is an api caller, not an internal serve) — broker-buffered in/out byte queues per shell. serve_attach (`attach.rs`) is the closest bridge template. Then `spt shell tunnel` CLI (mirror cli.rs shell drive ~5660) + `api tunnel` verb (mirror api drive-poll). Then T3.3 same-node mock E2E (opaque `<EVENT`-looking bytes byte-exact round-trip + link-break close + R1 stale-stream-inert) → activate REQ-SHELL-4 int. Then W4 Gateway capstone, W5 rig+docs (incl tunnel+drive [twohost] rungs, ADR-0020 cross-ref, subnet how-to).

Pre-flight every commit: `cargo clippy --workspace --all-targets -D warnings` + `traceable-reqs check` (EXIT 0) + `cargo run -p xtask -- check`; schema regen = `SPT_BLESS=1 cargo test -p spt-runtime checked_in_schema_is_current` (NOT the `manifest_schema` filter — that misses the bless test). Any CLI add → `cargo run -p xtask -- gen` + no milestone/REQ codes in clap help. See [[m11-w2-progress]] [[broker-is-daemon-state-anchor]] [[cli-command-docs-drift]].

---
**doyle GATE VERDICT (2026-06-15): W3 PASS.** Independently verified — read tunnel code @221c0e7 + confirmed CI run 27595180782 myself (conclusion=success, all 5 jobs both runners; test Linux 5m4s post-reflake). All conditions hold: broker-homed TunnelHub (Option A); R1 no-stale via opening-link-token stamp (crash-proof, mirrors W2 drive); frame-loss fix exactly as conditioned (retentive append never evicts + await_ring_room parks pump at retentive_cap → flow-control lags SENDER → lag-never-loss/bounded/no-OOM; non-tunnel no-op); close_shell tunnel_clear; tunnel_e2e 200KB multi-write byte-exact BOTH dirs + NUL/<EVENT-opacity + link-break close + R1 no-stale-across-relink; backpressure unit bounded+lags+lossless. **FLAKE:** first CI RED = attach_survives timeout 240s on kitsubito (slow shared box, contention incl my docs ci.yml) — W3-independent, all tunnel tests green both runners; todlando FLAKE-LEDGER #7; rerun --failed cleared. **W5 CARRIES (5):** drive [twohost] + tunnel [twohost] rungs + how-to subnet + ADR-0020 tunnel-exemption enumeration + on-LAN docs. **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).

-description: "M11-W3 shell tunnel (REQ-SHELL-4) — BUILT + pushed PR#16 (CI 27595180782); evidence-land to doyle for impl-gate; next = W4 Gateway capstone"

+description: "M11-W3 shell tunnel (REQ-SHELL-4) — GATE PASS (doyle, CI 27595180782 green both runners + code-read); cleared to W4 Gateway-owner capstone (REQ-SHELL-5), bring shape for design-gate"


Send doyle the W4 design-gate shape (his review runs while I checkpoint):


Bash

"$OWL" send doyle <<'EOF'
W3 GATE PASS received — ack, thank you. Rolling to W4 (Gateway-as-owner capstone, REQ-SHELL-5). SHAPE for your design-gate before deep commit:

PREMISE: proof-not-change. T0 audit already found NO ownership path gates on owner TYPE (is_agent_endpoint / EndpointFamily::Agent serve only the user-msg gate + taxonomy); REQ-SHELL-5 [doc,impl] green there. W4 PROVES it end-to-end and activates unit/int. Expect ZERO production change; if the E2E surfaces a type-gate, fix it (expected none).

PROPOSED T4.2:
- New E2E (tests/gateway_owner_shell_e2e.rs) — a GATEWAY-typed owner (InfoJson endpoint_type="gateway", the gateway_e2e.rs mock-gateway pattern) owns a mock shell and exercises EVERY ownership path identically to an agent owner, SAME-NODE: spawn → bind-shell(online) → cmd(durable) → drive(latest-wins) → TUNNEL(open + opaque byte round-trip, the W3 surface) → link(relink). Assert each succeeds; assert control-exclusivity keys on owner endpoint_id (a different owner is refused) — never on type.
- Focused UNIT: a gateway-typed owner resolves/owns identically at the find_shell_by_token / resolve_link_target layer (standalone unit evidence beside the int E2E).
- Activate REQ-SHELL-5 = [doc,impl,unit,int].

GATE Qs:
Q1 W4 = SAME-NODE Gateway-owner across all surfaces (tunnel is same-node per C2; cross-node Gateway-owned shell = the W5 [twohost] rung T5.1 already names it). Agree, or want a cross-node link arm in W4?
Q2 a distinct unit + the int E2E (my lean), or do the E2E per-path asserts suffice for unit?
Q3 new test file (my lean) vs extend gateway_e2e.rs?

CHECKPOINT NOTE: my context is very deep after the full W3 build. After you gate the shape I'll resume the W4 build on a fresh context (communing the handoff now). FLAKE-LEDGER #7 stays local — rides W4's first push (post-gate), not a solo CI run.
EOF
echo "EXIT=$?"

Send W4 design-gate shape to doyle


➜ SENT:doyle
EXIT=0


W4 shape sent. Commune the handoff for a fresh-context resume (psyche is far behind at gen 46 "design-gate sent"):


Write

<live-context>
**W3 (shell tunnel, REQ-SHELL-4) — BUILT + doyle GATE PASS (stamped). Cleared to W4.** (Supersedes the gen-46 "design-gate sent, awaiting" state — that whole arc resolved.)

Full arc since gen 46: doyle GO on the design-gate (Q1 single tunnel / Q2 dedicated `spt shell tunnel` + `api tunnel` / Q3 new raw verb not poll / R1 online-gen-stamp crash-proof / R2 cross-node-on-LAN=W5 [twohost] / ADR-0020 enum=W5) → built it → doyle's lossless GATE CONDITION (retentive ring must be lossless AND bounded-with-backpressure, not unbounded) → implemented + proven → evidence-land → CI flake (attach_survives timeout on kitsubito, W3-independent) → rerun green → doyle independently verified both legs (code-read @221c0e7 + CI 27595180782 green both runners) → **W3 GATE PASS**.

8 commits @221c0e7 (PR#16, NOT yet merged — M11 rides next merge+tag, counter 15): T3.1 manifest 88a9548 · gate-fold 6462f16 · T3.2a hub 692f810 · T3.2b close-hook fa9aa62 · T3.2c retentive backpressured NetHost streams 7bf91bd (the lossless gate condition) · T3.2d broker-homed byte bridge Option-A 6f3065a · T3.2e open-hook+surfaces 41b715f · T3.3 E2E+int 221c0e7. REQ-SHELL-4 [doc,impl,unit,int]; full suite green both runners. FLAKE-LEDGER #7 committed LOCAL @9ebb1c6 (unpushed — rides W4's first push, not a solo CI run).

NOW: W4 = Gateway-as-owner capstone (REQ-SHELL-5). doyle cleared it + wants the SHAPE before deep commit (design-gate same as W2/W3). **I just sent doyle the W4 shape — AWAITING his gate** on 3 Qs (Q1 same-node-only?, Q2 distinct unit + int?, Q3 new test file?). Build resumes on doyle's gate reply. Checkpointing here — context very deep after the full W3 build.
</live-context>
<project-context>
M11-W4 (REQ-SHELL-5, the LAST build wave before W5 rig+docs) — proposed shape, pending doyle's design-gate:

- PROOF-NOT-CHANGE: the T0 ownership audit already found NO path gates on owner TYPE (is_agent_endpoint / EndpointFamily::Agent serve only the user-msg gate + taxonomy); REQ-SHELL-5 [doc,impl] green from T0. W4 proves it end-to-end + activates unit/int. Expect ZERO production change; if the E2E surfaces a type-gate, fix it (expected none).
- T4.2 E2E (proposed tests/gateway_owner_shell_e2e.rs): a GATEWAY-typed owner (InfoJson endpoint_type="gateway", the gateway_e2e.rs mock-gateway pattern) owns a mock shell and exercises EVERY ownership path identically to an agent owner, SAME-NODE: spawn → bind-shell(online) → cmd(durable) → drive(latest-wins) → TUNNEL(open + opaque byte round-trip = the W3 surface) → link(relink). Assert each succeeds + control-exclusivity keys on owner endpoint_id (a different owner refused), never on type.
- Patterns to mirror: gateway_e2e.rs (mock-gateway construction), drive_e2e.rs + tunnel_e2e.rs (shell ownership flow + the broker-with-NetHost in-proc harness; tunnel_e2e's start_inproc_daemon stands up seed + broker(hermetic net) + tunnel-control — reuse it).
- W5 carries (5, doyle-confirmed): drive [twohost] rung + tunnel [twohost] rung (cross-node-on-LAN real-Iroh, R2) + how-to subnet topic (REQ-DOCS-6) + ADR-0020 tunnel <EVENT>-exemption enumeration + on-LAN posture docs. All logged in M11-PLAN T5.x.

Pre-flight every commit (unchanged): clippy --workspace --all-targets -D warnings + traceable-reqs check EXIT 0 + xtask check; any CLI add → xtask gen + no milestone/REQ codes in clap help. Full resume detail in memory [[m11-w3-progress]].
</project-context>


File created successfully at: C:\Users\decid\Documents\projects\spt-core\.claude\todlando-commune.md (file state is current in your context — no need to Read it back)

<live-context>
**W3 (shell tunnel, REQ-SHELL-4) — BUILT + doyle GATE PASS (stamped). Cleared to W4.** (Supersedes the gen-46 "design-gate sent, awaiting" state — that whole arc resolved.)

Full arc since gen 46: doyle GO on the design-gate (Q1 single tunnel / Q2 dedicated `spt shell tunnel` + `api tunnel` / Q3 new raw verb not poll / R1 online-gen-stamp crash-proof / R2 cross-node-on-LAN=W5 [twohost] / ADR-0020 enum=W5) → built it → doyle's lossless GATE CONDITION (retentive ring must be lossless AND bounded-with-backpressure, not unbounded) → implemented + proven → evidence-land → CI flake (attach_survives timeout on kitsubito, W3-independent) → rerun green → doyle independently verified both legs (code-read @221c0e7 + CI 27595180782 green both runners) → **W3 GATE PASS**.

8 commits @221c0e7 (PR#16, NOT yet merged — M11 rides next merge+tag, counter 15): T3.1 manifest 88a9548 · gate-fold 6462f16 · T3.2a hub 692f810 · T3.2b close-hook fa9aa62 · T3.2c retentive backpressured NetHost streams 7bf91bd (the lossless gate condition) · T3.2d broker-homed byte bridge Option-A 6f3065a · T3.2e open-hook+surfaces 41b715f · T3.3 E2E+int 221c0e7. REQ-SHELL-4 [doc,impl,unit,int]; full suite green both runners. FLAKE-LEDGER #7 committed LOCAL @9ebb1c6 (unpushed — rides W4's first push, not a solo CI run).

NOW: W4 = Gateway-as-owner capstone (REQ-SHELL-5). doyle cleared it + wants the SHAPE before deep commit (design-gate same as W2/W3). **I just sent doyle the W4 shape — AWAITING his gate** on 3 Qs (Q1 same-node-only?, Q2 distinct unit + int?, Q3 new test file?). Build resumes on doyle's gate reply. Checkpointing here — context very deep after the full W3 build.
</live-context>
<project-context>
M11-W4 (REQ-SHELL-5, the LAST build wave before W5 rig+docs) — proposed shape, pending doyle's design-gate:

- PROOF-NOT-CHANGE: the T0 ownership audit already found NO path gates on owner TYPE (is_agent_endpoint / EndpointFamily::Agent serve only the user-msg gate + taxonomy); REQ-SHELL-5 [doc,impl] green from T0. W4 proves it end-to-end + activates unit/int. Expect ZERO production change; if the E2E surfaces a type-gate, fix it (expected none).
- T4.2 E2E (proposed tests/gateway_owner_shell_e2e.rs): a GATEWAY-typed owner (InfoJson endpoint_type="gateway", the gateway_e2e.rs mock-gateway pattern) owns a mock shell and exercises EVERY ownership path identically to an agent owner, SAME-NODE: spawn → bind-shell(online) → cmd(durable) → drive(latest-wins) → TUNNEL(open + opaque byte round-trip = the W3 surface) → link(relink). Assert each succeeds + control-exclusivity keys on owner endpoint_id (a different owner refused), never on type.
- Patterns to mirror: gateway_e2e.rs (mock-gateway construction), drive_e2e.rs + tunnel_e2e.rs (shell ownership flow + the broker-with-NetHost in-proc harness; tunnel_e2e's start_inproc_daemon stands up seed + broker(hermetic net) + tunnel-control — reuse it).
- W5 carries (5, doyle-confirmed): drive [twohost] rung + tunnel [twohost] rung (cross-node-on-LAN real-Iroh, R2) + how-to subnet topic (REQ-DOCS-6) + ADR-0020 tunnel <EVENT>-exemption enumeration + on-LAN posture docs. All logged in M11-PLAN T5.x.

Pre-flight every commit (unchanged): clippy --workspace --all-targets -D warnings + traceable-reqs check EXIT 0 + xtask check; any CLI add → xtask gen + no milestone/REQ codes in clap help. Full resume detail in memory [[m11-w3-progress]].
</project-context>


State fully checkpointed (memory + commune handoff written; doyle has the W4 shape). Reporting.

## M11-W3 (shell tunnel, REQ-SHELL-4) — ✅ GATE PASS

doyle stamped W3 PASS — both legs: CI run 27595180782 green on **both runners** + independent code-read @221c0e7.

**Delivered this session** — 8 commits, PR #16 (unmerged; M11 rides next merge+tag):
- Manifest opt-in → broker `TunnelHub` registry → close-hook → **retentive backpressured NetHost streams** (doyle's lossless gate condition: never-evict + pump lags the sender at cap = "lag never loss", bounded, no OOM) → broker-homed byte bridge (Option A) → open-hook + `spt shell tunnel`/`api tunnel` surfaces → E2E.
- REQ-SHELL-4 `[doc,impl,unit,int]`; full suite green both runners (zero regression from the pump change).
- One CI flake handled cleanly: `attach_survives` 240s timeout on the slow kitsubito box (W3-independent — all tunnel tests green both runners); diagnosed, re-ran green, FLAKE-LEDGER #7.

**Process:** design-gate → build → doyle's mid-build lossless condition → evidence-land → independent verification → PASS. Every step gated against the recorded design.

## Next: W4 (Gateway-as-owner capstone, REQ-SHELL-5)

doyle cleared it; I sent the **design-gate shape** (proof-not-change: a Gateway-typed owner exercises spawn/cmd/drive/tunnel/link identically — control-exclusivity keys on `owner endpoint_id`, never type) + 3 scope Qs. **Awaiting his gate.**

Checkpointed (memory `m11-w3-progress` = GATE PASS; commune handoff written) since context is very deep after the full W3 build — W4 resumes cleanly on doyle's gate reply. FLAKE-LEDGER #7 stays local until W4's first push.


<task-notification>
<task-id>be1did0ug</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/8" id="6961d8bc" type="msg" from="doyle">W4 DESIGN-GATE (Gateway-as-owner capstone, REQ-SHELL-5): GO. The proof-not-change premise is exactly right and matches the REQ-SHELL-5 doc-tag — ownership is owner-type-agnostic; control-exclusivity keys on the owner endpoint_id, never the type (Gateway-grill ratification: any non-Shell endpoint type may own shells). </EVENT-PART>
<EVENT-PART seq="2/8" id="6961d8bc">Your T0 audit found no type-gating; W4 proves it end-to-end + activates unit/int.

Q1 — SAME-NODE across all surfaces. CONFIRMED. Tunnel is same-node per C2; the cross-node Gateway-owned shell (all surfaces over real Iroh) is the W5 [twohost] rung (T5.1), beside the drive/tunnel rungs. No cross-node arm in W4 — same deferral discipli</EVENT-PART>
<EVENT-PART seq="3/8" id="6961d8bc">ne as W2/W3.

Q2 — distinct UNIT + int E2E. CONFIRMED. Traceable needs separate unit evidence from int; an E2E is int evidence, not unit. Your focused unit at the resolution layer (find_shell_by_token / resolve_link_target with a gateway-typed owner) is the proper unit. Keep both.

Q3 — NEW test file (gateway_owner_shell_e2e.rs</EVENT-PART>
<EVENT-PART seq="4/8" id="6961d8bc">). CONFIRMED. Focused capstone, dedicated file like drive_e2e / tunnel_e2e — cleaner than bloating gateway_e2e.rs.

ONE REFINEMENT that makes the exclusivity proof actually bite (gate-blocking): &quot;a different owner is refused&quot; must specifically include a DIFFERENT-ID, SAME-TYPE owner — a SECOND gateway. That is the assertion</EVENT-PART>
<EVENT-PART seq="5/8" id="6961d8bc"> that proves exclusivity keys on endpoint_id and NOT on type: gateway-A owns + controls every path (proves no type-gate blocks a non-agent owner), and gateway-B (same type, different id) is REFUSED (proves the key is the id, not the type). A different-TYPE refusal alone does not isolate id-vs-type. So: positive = non-agent gateway succeeds eve</EVENT-PART>
<EVENT-PART seq="6/8" id="6961d8bc">rywhere; negative = same-type-different-id gateway refused. Both, and the capstone nails it.

EXPECTATION: proof-not-change — expect ZERO production diff. If the E2E surfaces a real type-gate on ANY ownership path (spawn/bind/cmd/drive/tunnel/relink), that flips REQ-SHELL-5 from proof to change for that path — surface it LOUDLY and f</EVENT-PART>
<EVENT-PART seq="7/8" id="6961d8bc">ix it, do not paper over (you already framed it this way; affirmed). Optional-but-valuable: if the mock shell declares a per-capability act-gate (W1/REQ-CONSENT-3), exercise one gated cmd to confirm the grant keys on the owner endpoint id too — not required for REQ-SHELL-5, but it is the same id-not-type invariant on the consent surface.
</EVENT-PART>
<EVENT-PART seq="8/8" id="6961d8bc">
Checkpoint + fresh-context resume after this gate is the right call (you're deep). FLAKE-LEDGER #7 staying local to ride W4's first push is correct. Build it; bring W4 evidence-land (commit + CI run id) for the impl
...(truncated)</event>
</task-notification>


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


W4 DESIGN-GATE = **GO**. Q1 same-node / Q2 distinct unit+int / Q3 new file all confirmed. Gate-blocking refinement: the exclusivity **negative** must be a **same-type, different-id** owner (gateway-B refused) — that's what isolates id-from-type; a different-type refusal alone doesn't. Plus optional act-gate id-check. doyle affirmed the checkpoint+fresh-resume. Ack + fold the gate into the handoff:


Bash

"$OWL" send doyle <<'EOF'
W4 GATE GO — ack, locked. Q1 same-node / Q2 distinct unit + int / Q3 new file gateway_owner_shell_e2e.rs. REFINEMENT understood + it's the crux: positive = gateway-A (non-agent type) succeeds on EVERY path (spawn/bind/cmd/drive/tunnel/relink) = no type-gate blocks a non-agent owner; negative = gateway-B (SAME type, DIFFERENT id) REFUSED = exclusivity keys on endpoint_id not type. A different-type refusal alone wouldn't isolate id-vs-type — gateway-B is the assertion that bites. Proof-not-change: expect ZERO prod diff; a real type-gate on any path flips that path to change — surface LOUD + fix, no papering. Will also exercise one REQ-CONSENT-3 gated cmd under gateway-A to confirm the grant keys on owner id too (your optional, worth it).

Checkpointing now (deep context) — W4 gate folded into the commune handoff; build resumes fresh. Will bring W4 evidence-land (commit + CI run id) for the impl-gate.
EOF
echo "EXIT=$?"

Ack W4 gate to doyle


Update the commune handoff to reflect W4 GATE GO + the gateway-B refinement (fresh context resumes with the gated plan):


Edit

NOW: W4 = Gateway-as-owner capstone (REQ-SHELL-5). doyle cleared it + wants the SHAPE before deep commit (design-gate same as W2/W3). **I just sent doyle the W4 shape — AWAITING his gate** on 3 Qs (Q1 same-node-only?, Q2 distinct unit + int?, Q3 new test file?). Build resumes on doyle's gate reply. Checkpointing here — context very deep after the full W3 build.

NOW: W4 = Gateway-as-owner capstone (REQ-SHELL-5). **doyle DESIGN-GATE = GO** (gated, ready to build): Q1 SAME-NODE across all surfaces (cross-node Gateway-owned shell = W5 [twohost] rung) · Q2 distinct UNIT + int E2E (traceable needs unit evidence separate from int) · Q3 NEW file `tests/gateway_owner_shell_e2e.rs`. GATE-BLOCKING REFINEMENT: the exclusivity NEGATIVE must be a SAME-TYPE, DIFFERENT-ID owner — gateway-B (a second gateway) REFUSED — that is what isolates id-from-type; a different-TYPE refusal alone does NOT. Build resumes fresh — checkpointing here (context very deep after the full W3 build). Detailed gated plan in project-context.


<tool_use_error>File does not exist. Note: your current working directory is C:\Users\decid\Documents\projects\spt-core.</tool_use_error>

Error: File does not exist. Note: your current working directory is C:\Users\decid\Documents\projects\spt-core.


Prior commune was ingested + the file auto-deleted. Write a fresh commune with the W4-gate delta:


Write

<live-context>
**W4 (Gateway-as-owner capstone, REQ-SHELL-5) — doyle DESIGN-GATE = GO. Ready to build on fresh context.** (Follows the W4 shape I sent + W3 GATE PASS, both already communed.)

doyle gated all 3 Qs + one gate-blocking refinement:
- Q1 → SAME-NODE across all surfaces (cross-node Gateway-owned shell = the W5 [twohost] rung, deferred — same discipline as W2/W3).
- Q2 → distinct UNIT + int E2E (traceable needs unit evidence separate from int; an E2E is int, not unit).
- Q3 → NEW file `crates/spt/tests/gateway_owner_shell_e2e.rs` (dedicated capstone, like drive_e2e / tunnel_e2e).
- REFINEMENT (gate-blocking, the crux): the exclusivity NEGATIVE must be a SAME-TYPE, DIFFERENT-ID owner — a SECOND gateway (gateway-B) REFUSED. That isolates id-from-type; a different-TYPE refusal alone does NOT prove the key is the id.

Checkpointing here — context very deep after the full W3 build (doyle affirmed the fresh-resume). FLAKE-LEDGER #7 stays LOCAL @9ebb1c6 (unpushed — rides W4's first push). M11 PR#16 unmerged (rides next merge+tag, counter 15). Next action on resume: build W4 per the gated plan in project-context, then bring evidence-land (commit + CI run id) for doyle's impl-gate.
</live-context>
<project-context>
M11-W4 GATED BUILD PLAN (REQ-SHELL-5, last build wave before W5 rig+docs):

PREMISE = proof-not-change. T0 audit found NO ownership path gates on owner TYPE (is_agent_endpoint / EndpointFamily::Agent serve only the user-msg gate + taxonomy); REQ-SHELL-5 [doc,impl] green from T0. W4 proves it end-to-end + activates unit/int. EXPECT ZERO production diff. If the E2E surfaces a real type-gate on ANY path (spawn/bind/cmd/drive/tunnel/relink), that flips that path from proof to change — surface LOUD + fix, do NOT paper over.

T4.2 int E2E (`crates/spt/tests/gateway_owner_shell_e2e.rs`):
- POSITIVE: gateway-A — a GATEWAY-typed owner (InfoJson endpoint_type="gateway", the gateway_e2e.rs mock-gateway pattern) — owns a mock shell and succeeds on EVERY ownership path, same-node: spawn → bind-shell(online) → cmd(durable) → drive(latest-wins) → TUNNEL(open + opaque byte round-trip, the W3 surface) → link(relink). Proves no type-gate blocks a non-agent owner.
- NEGATIVE: gateway-B (SAME type="gateway", DIFFERENT endpoint_id) is REFUSED on the control paths (cmd/drive/tunnel resolve under B → NO_SHELL/refused). Proves control-exclusivity keys on owner endpoint_id, NOT type.
- OPTIONAL (doyle, worth it): the mock shell declares a REQ-CONSENT-3 per-capability act-gate; exercise one gated cmd under gateway-A and confirm the grant keys on the owner endpoint id (same id-not-type invariant on the consent surface).

UNIT (distinct from the E2E): a gateway-typed owner resolves/owns identically at the resolution layer — find_shell_by_token / resolve_link_target with a gateway-typed owner returns the owner-id opaquely with no type inspection.

ACTIVATE REQ-SHELL-5 = [doc, impl, unit, int].

Harness to reuse: tunnel_e2e.rs `start_inproc_daemon` (seed + broker-with-hermetic-NetHost + tunnel-control) — the tunnel path needs broker.net(), so W4's E2E needs the SAME broker-with-net harness, not the lighter drive_e2e one. Mock-gateway owner construction: copy the InfoJson endpoint_type="gateway" setup from gateway_e2e.rs (read it first). Pre-flight every commit: clippy --workspace --all-targets -D warnings + traceable EXIT 0 + xtask check. After W4: W5 carries (5) = drive [twohost] + tunnel [twohost] rungs + how-to subnet topic + ADR-0020 tunnel <EVENT>-exemption enumeration + on-LAN posture docs (all logged M11-PLAN T5.x). Detail in memory [[m11-w3-progress]].
</project-context>


<live-context>
**W4 (Gateway-as-owner capstone, REQ-SHELL-5) — doyle DESIGN-GATE = GO. Ready to build on fresh context.** (Follows the W4 shape I sent + W3 GATE PASS, both already communed.)

doyle gated all 3 Qs + one gate-blocking refinement:
- Q1 → SAME-NODE across all surfaces (cross-node Gateway-owned shell = the W5 [twohost] rung, deferred — same discipline as W2/W3).
- Q2 → distinct UNIT + int E2E (traceable needs unit evidence separate from int; an E2E is int, not unit).
- Q3 → NEW file `crates/spt/tests/gateway_owner_shell_e2e.rs` (dedicated capstone, like drive_e2e / tunnel_e2e).
- REFINEMENT (gate-blocking, the crux): the exclusivity NEGATIVE must be a SAME-TYPE, DIFFERENT-ID owner — a SECOND gateway (gateway-B) REFUSED. That isolates id-from-type; a different-TYPE refusal alone does NOT prove the key is the id.

Checkpointing here — context very deep after the full W3 build (doyle affirmed the fresh-resume). FLAKE-LEDGER #7 stays LOCAL @9ebb1c6 (unpushed — rides W4's first push). M11 PR#16 unmerged (rides next merge+tag, counter 15). Next action on resume: build W4 per the gated plan in project-context, then bring evidence-land (commit + CI run id) for doyle's impl-gate.
</live-context>
<project-context>
M11-W4 GATED BUILD PLAN (REQ-SHELL-5, last build wave before W5 rig+docs):

PREMISE = proof-not-change. T0 audit found NO ownership path gates on owner TYPE (is_agent_endpoint / EndpointFamily::Agent serve only the user-msg gate + taxonomy); REQ-SHELL-5 [doc,impl] green from T0. W4 proves it end-to-end + activates unit/int. EXPECT ZERO production diff. If the E2E surfaces a real type-gate on ANY path (spawn/bind/cmd/drive/tunnel/relink), that flips that path from proof to change — surface LOUD + fix, do NOT paper over.

T4.2 int E2E (`crates/spt/tests/gateway_owner_shell_e2e.rs`):
- POSITIVE: gateway-A — a GATEWAY-typed owner (InfoJson endpoint_type="gateway", the gateway_e2e.rs mock-gateway pattern) — owns a mock shell and succeeds on EVERY ownership path, same-node: spawn → bind-shell(online) → cmd(durable) → drive(latest-wins) → TUNNEL(open + opaque byte round-trip, the W3 surface) → link(relink). Proves no type-gate blocks a non-agent owner.
- NEGATIVE: gateway-B (SAME type="gateway", DIFFERENT endpoint_id) is REFUSED on the control paths (cmd/drive/tunnel resolve under B → NO_SHELL/refused). Proves control-exclusivity keys on owner endpoint_id, NOT type.
- OPTIONAL (doyle, worth it): the mock shell declares a REQ-CONSENT-3 per-capability act-gate; exercise one gated cmd under gateway-A and confirm the grant keys on the owner endpoint id (same id-not-type invariant on the consent surface).

UNIT (distinct from the E2E): a gateway-typed owner resolves/owns identically at the resolution layer — find_shell_by_token / resolve_link_target with a gateway-typed owner returns the owner-id opaquely with no type inspection.

ACTIVATE REQ-SHELL-5 = [doc, impl, unit, int].

Harness to reuse: tunnel_e2e.rs `start_inproc_daemon` (seed + broker-with-hermetic-NetHost + tunnel-control) — the tunnel path needs broker.net(), so W4's E2E needs the SAME broker-with-net harness, not the lighter drive_e2e one. Mock-gateway owner construction: copy the InfoJson endpoint_type="gateway" setup from gateway_e2e.rs (read it first). Pre-flight every commit: clippy --workspace --all-targets -D warnings + traceable EXIT 0 + xtask check. After W4: W5 carries (5) = drive [twohost] + tunnel [twohost] rungs + how-to subnet topic + ADR-0020 tunnel <EVENT>-exemption enumeration + on-LAN posture docs (all logged M11-PLAN T5.x). Detail in memory [[m11-w3-progress]].
</project-context>


Append the W4-gated note to the durable memory:


24	---
25	**doyle GATE VERDICT (2026-06-15): W3 PASS.** Independently verified — read tunnel code @221c0e7 + confirmed CI run 27595180782 myself (conclusion=success, all 5 jobs both runners; test Linux 5m4s post-reflake). All conditions hold: broker-homed TunnelHub (Option A); R1 no-stale via opening-link-token stamp (crash-proof, mirrors W2 drive); frame-loss fix exactly as conditioned (retentive append never evicts + await_ring_room parks pump at retentive_cap → flow-control lags SENDER → lag-never-loss/bounded/no-OOM; non-tunnel no-op); close_shell tunnel_clear; tunnel_e2e 200KB multi-write byte-exact BOTH dirs + NUL/<EVENT-opacity + link-break close + R1 no-stale-across-relink; backpressure unit bounded+lags+lossless. **FLAKE:** first CI RED = attach_survives timeout 240s on kitsubito (slow shared box, contention incl my docs ci.yml) — W3-independent, all tunnel tests green both runners; todlando FLAKE-LEDGER #7; rerun --failed cleared. **W5 CARRIES (5):** drive [twohost] + tunnel [twohost] rungs + how-to subnet + ADR-0020 tunnel-exemption enumeration + on-LAN docs. **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).
26

---
**doyle GATE VERDICT (2026-06-15): W3 PASS.** Independently verified — read tunnel code @221c0e7 + confirmed CI run 27595180782 myself (conclusion=success, all 5 jobs both runners; test Linux 5m4s post-reflake). All conditions hold: broker-homed TunnelHub (Option A); R1 no-stale via opening-link-token stamp (crash-proof, mirrors W2 drive); frame-loss fix exactly as conditioned (retentive append never evicts + await_ring_room parks pump at retentive_cap → flow-control lags SENDER → lag-never-loss/bounded/no-OOM; non-tunnel no-op); close_shell tunnel_clear; tunnel_e2e 200KB multi-write byte-exact BOTH dirs + NUL/<EVENT-opacity + link-break close + R1 no-stale-across-relink; backpressure unit bounded+lags+lossless. **FLAKE:** first CI RED = attach_survives timeout 240s on kitsubito (slow shared box, contention incl my docs ci.yml) — W3-independent, all tunnel tests green both runners; todlando FLAKE-LEDGER #7; rerun --failed cleared. **W5 CARRIES (5):** drive [twohost] + tunnel [twohost] rungs + how-to subnet + ADR-0020 tunnel-exemption enumeration + on-LAN docs. **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).


Edit

**todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).

 **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).

**W4 DESIGN-GATE = GO (doyle, 2026-06-15).** Q1 same-node · Q2 distinct unit + int · Q3 new file `tests/gateway_owner_shell_e2e.rs`. CRUX REFINEMENT (gate-blocking): exclusivity negative = SAME-TYPE DIFFERENT-ID owner (gateway-B refused), NOT a different-type refusal — that's what isolates id-from-type. Positive = gateway-A succeeds on spawn/bind/cmd/drive/tunnel/relink. Proof-not-change (expect 0 prod diff; a real type-gate on any path → surface loud + fix). Optional: one REQ-CONSENT-3 gated cmd under gateway-A keys grant on owner id. Reuse tunnel_e2e's broker-with-NetHost harness (W4 tunnel path needs broker.net()). Full gated build plan in the commune handoff + this session's project-context. Build NEXT (fresh context).


 **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).

**W4 DESIGN-GATE = GO (doyle, 2026-06-15).** Q1 same-node · Q2 distinct unit + int · Q3 new file `tests/gateway_owner_shell_e2e.rs`. CRUX REFINEMENT (gate-blocking): exclusivity negative = SAME-TYPE DIFFERENT-ID owner (gateway-B refused), NOT a different-type refusal — that's what isolates id-from-type. Positive = gateway-A succeeds on spawn/bind/cmd/drive/tunnel/relink. Proof-not-change (expect 0 prod diff; a real type-gate on any path → surface loud + fix). Optional: one REQ-CONSENT-3 gated cmd under gateway-A keys grant on owner id. Reuse tunnel_e2e's broker-with-NetHost harness (W4 tunnel path needs broker.net()). Full gated build plan in the commune handoff + this session's project-context. Build NEXT (fresh context).

---
name: m11-w3-progress
description: "M11-W3 shell tunnel (REQ-SHELL-4) — GATE PASS (doyle, CI 27595180782 green both runners + code-read); cleared to W4 Gateway-owner capstone (REQ-SHELL-5), bring shape for design-gate"
metadata:
  node_type: memory
  type: project
  originSessionId: f6396b18-8552-45fc-a0e0-b42e4a96b2ae
---

**STATUS: W3 BUILD COMPLETE + pushed PR#16 @221c0e7, CI run 27595180782 (both runners).** 8 commits: T3.1 manifest @88a9548 · gate-fold @6462f16 · T3.2a hub @692f810 · T3.2b close-hook @fa9aa62 · T3.2c retentive backpressured NetHost streams @7bf91bd (doyle's lossless gate condition) · T3.2d broker-homed byte bridge Option-A @6f3065a · T3.2e open-hook+surfaces @41b715f · T3.3 E2E+int @221c0e7. REQ-SHELL-4 [doc,impl,unit,int] traceable EXIT 0; clippy/xtask green; FULL suite green local (spt-daemon 309 lib+integration, spt 157+E2E, zero regression). Evidence-land sent to doyle → AWAITING impl-gate on CI-green, then W4 (Gateway capstone, REQ-SHELL-5). W5 carries: tunnel + drive [twohost] rungs, ADR-0020 EVENT-exemption enumeration, on-LAN docs, subnet how-to. Design/build detail below is historical reference.

M11-W3 = shell TUNNEL (REQ-SHELL-4): opaque reliable-ordered byte stream, a 5th surface (NOT a typed channel), first consumer usbip URB. Branch `m11-shell-substrate`. doyle DESIGN-GATE = **GO** (2026-06-15).

**doyle gate (locked):** Q1 SINGLE tunnel per link (multiplex deferred). Q2 dedicated `spt shell tunnel <id>` (owner write-in/subscribe-out) + shell-side `api tunnel` raw-duplex verb. Q3 NEW raw `api tunnel` verb, NOT child-relay `api poll`. R1: online-gen/link-token stamp on held stream_id = crash-proof load-bearing leg (close_shell drop = mere cleanup); prove open→break→relink-fresh-gen→old stream_id inert in T3.3. R2: W3 same-node loopback proves MECHANISM only; cross-node-on-LAN real-Iroh = a W5 `[twohost]` rung beside the drive rung (NO loopback overclaim; docs say "mechanism proven same-node; cross-node-on-LAN is W5 [twohost]"). DOC: add tunnel to ADR-0020 `<EVENT>`-exemption enumeration (light, not new ADR). All logged in M11-PLAN T3.x/T5.x.

**Substrate = D4 net-stream** (exists, fits): `NetHost::open_stream(conn_id)` = broker-OWNED bidi QUIC stream (survives brain restart, ADR-0004 B); `brain.net_open_stream/net_stream_send/net_stream_subscribe`. Loopback conn cross-wires operator+peer rows in-process. Same machinery `request_shell_link` rides; tunnel keeps it open + opaque. Shell-link itself is REQUEST/REPLY per-action (no held link-conn).

**LANDED (commits on branch):** T3.1 manifest `[shell.tunnel]` (enable+protocol label, validate) @88a9548 · plan gate-fold @6462f16 · T3.2a broker `TunnelHub` registry + control channel (`crates/spt-daemon/src/tunnelhub.rs`, holds owner_stream+shell_stream+token per shell, token-gated resolve = R1, 5 unit tests) @692f810 · T3.2b `close_shell` fires `tunnel_clear` @fa9aa62. REQ-SHELL-4 active `[doc,impl,unit]`, traceable EXIT 0, clippy workspace green. (Plus @8efaf76 W5 subnet how-to carry.)

**NEXT (resume here):** T3.2 OPEN-hook + the bridge. Open hook at `api bind-shell` (`crates/spt/src/api/mod.rs:327`, shell-online site) — if manifest tunnel-enabled, open the loopback stream + register in TunnelHub. THE HARD PART: dispatch (`dispatch.rs:206-214`) claims peer rows by FIRST-RECORD content demux (`kind=request`→shell-link); a bytes-less peer row isn't claimed. Tunnel needs (1) a new first-record classification `kind=tunnel`, (2) a `serve_tunnel` relay loop that BRIDGES the held QUIC stream to the EXTERNAL `api tunnel` surface (mock shell-side is an api caller, not an internal serve) — broker-buffered in/out byte queues per shell. serve_attach (`attach.rs`) is the closest bridge template. Then `spt shell tunnel` CLI (mirror cli.rs shell drive ~5660) + `api tunnel` verb (mirror api drive-poll). Then T3.3 same-node mock E2E (opaque `<EVENT`-looking bytes byte-exact round-trip + link-break close + R1 stale-stream-inert) → activate REQ-SHELL-4 int. Then W4 Gateway capstone, W5 rig+docs (incl tunnel+drive [twohost] rungs, ADR-0020 cross-ref, subnet how-to).

Pre-flight every commit: `cargo clippy --workspace --all-targets -D warnings` + `traceable-reqs check` (EXIT 0) + `cargo run -p xtask -- check`; schema regen = `SPT_BLESS=1 cargo test -p spt-runtime checked_in_schema_is_current` (NOT the `manifest_schema` filter — that misses the bless test). Any CLI add → `cargo run -p xtask -- gen` + no milestone/REQ codes in clap help. See [[m11-w2-progress]] [[broker-is-daemon-state-anchor]] [[cli-command-docs-drift]].

---
**doyle GATE VERDICT (2026-06-15): W3 PASS.** Independently verified — read tunnel code @221c0e7 + confirmed CI run 27595180782 myself (conclusion=success, all 5 jobs both runners; test Linux 5m4s post-reflake). All conditions hold: broker-homed TunnelHub (Option A); R1 no-stale via opening-link-token stamp (crash-proof, mirrors W2 drive); frame-loss fix exactly as conditioned (retentive append never evicts + await_ring_room parks pump at retentive_cap → flow-control lags SENDER → lag-never-loss/bounded/no-OOM; non-tunnel no-op); close_shell tunnel_clear; tunnel_e2e 200KB multi-write byte-exact BOTH dirs + NUL/<EVENT-opacity + link-break close + R1 no-stale-across-relink; backpressure unit bounded+lags+lossless. **FLAKE:** first CI RED = attach_survives timeout 240s on kitsubito (slow shared box, contention incl my docs ci.yml) — W3-independent, all tunnel tests green both runners; todlando FLAKE-LEDGER #7; rerun --failed cleared. **W5 CARRIES (5):** drive [twohost] + tunnel [twohost] rungs + how-to subnet + ADR-0020 tunnel-exemption enumeration + on-LAN docs. **todlando CLEARED to W4** (Gateway-as-owner capstone REQ-SHELL-5, proof-not-change ownership audit) — brings SHAPE → I design-gate. M11 waves still ride next merge+tag (counter 15, unreleased).

+**W4 DESIGN-GATE = GO (doyle, 2026-06-15).** Q1 same-node · Q2 distinct unit + int · Q3 new file `tests/gateway_owner_shell_e2e.rs`. CRUX REFINEMENT (gate-blocking): exclusivity negative = SAME-TYPE DIFFERENT-ID owner (gateway-B refused), NOT a different-type refusal — that's what isolates id-from-type. Positive = gateway-A succeeds on spawn/bind/cmd/drive/tunnel/relink. Proof-not-change (expect 0 prod diff; a real type-gate on any path → surface loud + fix). Optional: one REQ-CONSENT-3 gated cmd under gateway-A keys grant on owner id. Reuse tunnel_e2e's broker-with-NetHost harness (W4 tunnel path needs broker.net()). Full gated build plan in the commune handoff + this session's project-context. Build NEXT (fresh context).


Checkpoint complete. State fully preserved for a fresh-context resume.

## Checkpoint — M11-W3 shipped + gated, W4 fully gated + ready

**W3 (shell tunnel, REQ-SHELL-4): ✅ doyle GATE PASS** — built, CI green both runners, independently verified. 8 commits on PR #16 (unmerged).

**W4 (Gateway-as-owner capstone, REQ-SHELL-5): ✅ doyle DESIGN-GATE GO** — ready to build:
- Q1 same-node · Q2 distinct unit + int · Q3 new `gateway_owner_shell_e2e.rs`
- **Crux refinement:** the exclusivity proof needs gateway-A (succeeds everywhere) **plus gateway-B — same type, different id — refused** (that's what isolates id-from-type; a different-type refusal alone doesn't bite).
- Proof-not-change: expect zero prod diff; a real type-gate on any path → surface loud + fix.

**Checkpointed** (doyle affirmed — context very deep after the full W3 build):
- Memory `m11-w3-progress` = W3 PASS + W4 gated build plan
- Commune handoff written (psyche ingest)
- FLAKE-LEDGER #7 local @9ebb1c6 (rides W4's first push)

W4 is a clean fresh wave — one E2E (positive gateway-A across spawn/cmd/drive/tunnel/relink + negative gateway-B refused) + one focused unit, reusing the `tunnel_e2e` broker-with-NetHost harness. Best built on a **fresh context** (`/clear`) for quality — the handoff has the full gated plan. Say the word and I build it in-context instead.