<task-notification>
<task-id>a3a5d590a5a035dc0</task-id>
<tool-use-id>toolu_01HAenvSGARJZGTuLdr9qUfF</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\a3a5d590a5a035dc0.output</output-file>
<status>completed</status>
<summary>Agent "Map heavy test files + flaky tests" came to rest</summary>
<note>A task-notification fires each time this agent comes to rest with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note>
<result>Perfect! Now I have all the information needed. Let me create the final comprehensive report:

---

## INTEGRATION TEST MAP & HEAVY TEST INVENTORY

### 1. INTEGRATION TEST FILES BY WORKSPACE CRATE

#### **spt-daemon** (11 test files) — Real broker/PTY infrastructure
The spt-daemon test suite exercises the broker kernel, PTY hosting, and brain lifecycle. Most spawn **real brokers** (via `Broker::bind()` or `net_broker()`), **real PTY children** (echo programs), and **real Unix domain sockets or named pipes**. Heavy tests use multi-threaded test coordination and process-wide `SPT_HOME` initialization.

| Test File | Description | Infrastructure |
|-----------|-------------|-----------------|
| **broker.rs** | B1: Real PTY child, output streaming, input injection over local IPC; single-threaded stream-based client | Real PTY (cat/findstr), real UDS/named-pipe, `Broker::bind()` (8 tests) |
| **attach.rs** | D5b: Remote terminal attach across two in-process brokers; real echo child, QUIC stream survival, restart idempotency | Real PTYs (×2 brokers), `net_broker()` loopback, NetConfig/NetHost (15 tests) |
| **inject_control_wedge.rs** | W1: Controller delivery non-wedge gate; controller backpressure against drain stall on real broker + real PTY. **CRITICAL**: Uses process-global `SPT_HOME` → must run isolated (process-per-test, NOT multi-threaded). Windows hangs on concurrent brokers. | Real broker, real PTY, real IPC, **requires `--test-threads=1` on Windows or `cargo nextest` (process-per-test)** (10 tests) |
| **netstream.rs** | D4b: QUIC stream gapless exactly-once across brain restarts; ring buffer replay; two in-process brokers | Two net_brokers (loopback QUIC), ring buffers (4 tests) |
| **netbroker.rs** | D4a: Broker owns iroh endpoint + live QUIC; perch identity seeding | net_broker + iroh identity (3 tests) |
| **access.rs** | D5d: Endpoint access whitelist over real wire | net_broker + access control (2 tests) |
| **dispatch.rs** | D9-1-1: Inbound dispatch E2E on serve half | net_broker (2 tests) |
| **daemon_e2e.rs** | B9: Whole daemon consolidated E2E; broker + brain lifecycle | Real broker, real PTY, local IPC (1 test) |
| **daemon_lifecycle_real_brain.rs** | M11-W0.4: Production-path integration, real brain restart | Real broker + real brain process (2 tests) |
| **mesh.rs** | Mesh-D6: Staggered + star subnet-mesh acceptance | net_broker ×2+ with mesh topology (2 tests) |
| **others** (14 files: brain_swap, budget, digest, driven_by_selfheal, handoff, idempotent, input_ack_deadlock, notifsync, pairjoin, presence, propagate, pump, pumpdeadline, replicate, reseed, rosterprop, seedproofx, shellchan, sync, two_origin_spanning, twohost, wanmsg, xfer) | D4/D5/D6/D7/D8/D9 protocol + mesh layers | Mix: some real broker/PTY, some net_brokers (mock transport tests), some spawn child processes |

---

#### **spt** (43 test files) — E2E harness + CLI integration
All *_e2e.rs tests spawn the **real `spt` binary** and interact with the harness contract (manifest, api surface, stdio/PTY). Some fork/exec the `mock-shell`/`mock-session` adapters. Many use `in_process_daemon()` (a seed socket, NOT a detached `spt daemon` process).

| Test File | Description | Infrastructure |
|-----------|-------------|-----------------|
| **shell_e2e.rs** | M5-D3e: Real `spt` binary + `mock-shell` binary adapter | Real `spt` binary spawn, real child process (mock-shell) |
| **contract_e2e.rs** | M2a: Harness contract E2E (inbound/outbound) | Real `spt` binary, in_process_daemon seed socket |
| **quickstart_e2e.rs** | M6-D4: Published human quickstart | Real `spt` binary, in_process_daemon |
| **drive_e2e.rs** | M11-W2: Drive-channel E2E | Real `spt` binary |
| **notify_shell_e2e.rs** | M5-D8a: Standalone shell adapter | Real `spt` binary + `notify-shell` child |
| **gateway_e2e.rs**, **gateway_owner_shell_e2e.rs** | M9-T5, M11-W4: Gateway endpoint type + shell integration | Real `spt` binary |
| **live_firsthost_e2e.rs**, **live_bind_firsthost_e2e.rs**, **live_resolve_e2e.rs** | M11-W0.2/3: First-host adapter resolution | Real `spt` binary, in_process_daemon |
| **livehost_bootgate_e2e.rs**, **livehost_bootrace_e2e.rs**, **livehost_nonresident_e2e.rs**, **livehost_psyche_fail_e2e.rs** | Boot liveness + harness residency gate | Real `spt` binary, harness child process |
| **brain_restart_psyche_dup_e2e.rs**, **brain_respawn_rename.rs**, **brain_split.rs**, **brain_survive.rs** | Process-level broker/brain lifecycle + restart | Real `spt` binary, spawned brain process |
| **n1_pairing.rs**, **n1_compat.rs** | N-1 broker/brain compat | Real `spt` binary, real brain process |
| **ready_resume_ledger_e2e.rs**, **resume_template_e2e.rs** | Ready agent + session resume | Real `spt` binary |
| **attach_wedge_e2e.rs**, **job_escape_e2e.rs**, **bind_cwd_project_e2e.rs** | Hazard regression gates (attach, viewer close, cwd) | Real `spt` binary |
| **shell_sleepwake_e2e.rs**, **shell_actgate_e2e.rs**, **poll_envelope_e2e.rs** | Shell integration, consent, envelope drain | Real `spt` binary |
| **tunnel_e2e.rs**, **oneliner_e2e.rs**, **release_verify_e2e.rs**, **dummy_harness_e2e.rs** | WAN tunnel, installer script, update dry-path, dummy adapter | Real `spt` binary ± child processes |
| **unhost_psyche_reap_e2e.rs** | Psyche reap after unhost | Real `spt` binary |

---

#### **spt-term** (5 test files) — PTY surface & input injection
All spawn **real PTY children** (sh/cmd with markers or cat). Exercise ConPTY DSR drains, byte streams, surface sizing.

| Test File | Description | Infrastructure |
|-----------|-------------|-----------------|
| **dsr.rs** | A2: ConPTY DSR auto-answer drain pump on real PTY | Real PTY (sh/cmd child) |
| **inject.rs** | A3: Input injection (`send_keys`/`send_line`) on real PTY | Real PTY |
| **stream.rs** | A4: Bounded byte-stream on real PTY | Real PTY |
| **surface.rs** | A1: Native-PTY SessionSurface backend | Real PTY |
| **winspawn.rs** | Windows REQ-HAZARD: PTY program resolve | Real PTY (Windows-specific) |

---

#### **spt-msg** (1 test file) — Message delivery
| Test File | Description | Infrastructure |
|-----------|-------------|-----------------|
| **killer_quickstart.rs** | M1: End-to-end message delivery + ready lifecycle across three perches (TCP live path + offline spool) | Real TCP listener, real spool file I/O, temp owlery |

---

#### **mock-adapter** (1 test file) — Harness contract mock
| Test File | Description | Infrastructure |
|-----------|-------------|-----------------|
| **manifest_valid.rs** | M2a: Manifest JSON schema validation | JSON file I/O only |

---

### 2. HEAVY TEST FUNCTION LOCATIONS (EXACT NAMES & LINE NUMBERS)

**Pattern-matched heavy tests** (flaky under high concurrency, real broker + PTY + IPC):

| Pattern | File | Line | Function Name | Notes |
|---------|------|------|---------------|-------|
| **g2_*** | `crates/spt-daemon/tests/inject_control_wedge.rs` | 1081 | `g2_no_commit_deadline_faults_binary_and_does_not_wedge_controller_input()` | Real broker, PTY child, controller backpressure. Real XLATE fixture. |
| **g4_*** | `crates/spt-daemon/tests/inject_control_wedge.rs` | 1228 | `g4_translation_child_is_reaped_no_zombie_after_session_down()` | Real broker, PTY child, choreography fixture, zombie reap. |
| **w5_a2*** | `crates/spt-daemon/tests/inject_control_wedge.rs` | 1469 | `w5_a2_abandoned_remote_controller_idle_session_keeps_controller_by_some()` | Real remote controller, loopback conn, abandoned detach. |
| **spawn_env*** | `crates/spt-daemon/tests/broker.rs` | 393 | `spawn_env_reaches_child()` | PTY child env injection via real broker IPC. |
| **attach_survives*** | `crates/spt-daemon/tests/attach.rs` | 635 | `attach_survives_target_brain_restart_exactly_once()` | Real echo child, two net_brokers, brain restart idempotency. |
| **controller_writer*** | `crates/spt-daemon/tests/broker.rs` | 1154 | `controller_writer_reorder_consumer_view_stays_monotonic_and_session_live()` | Controller reorder test, real broker, monotonic delivery assertion. |

**Additional heavy tests** (inferred from file descriptions + infrastructure):
- `crates/spt-daemon/tests/inject_control_wedge.rs:1081` `g1_choreography_happy_path_payload_reaches_pty_and_controller_keeps_control()` — Real XLATE choreography
- `crates/spt-daemon/tests/inject_control_wedge.rs:1160` `g3_content_free_ping_never_duplicates_operator_keystrokes_into_binary()` — Real XLATE, ping test
- `crates/spt-daemon/tests/inject_control_wedge.rs:1404` `w5_a1_locally_driven_session_reports_controller_by_none()` — Real broker, locally driven
- `crates/spt-daemon/tests/inject_control_wedge.rs:1683` `p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker()` — Real broker, paste wedge

---

### 3. CRATE PACKAGE NAMES (CARGO.TOML)

Workspace crates with integration tests:

```
spt-daemon          ← HEAVY tests (g2_, g4_, w5_a2_, spawn_env, attach_survives, controller_writer)
spt                 ← E2E tests (43 files, all spawn real binary)
spt-term            ← PTY surface tests (5 files, spawn real PTY children)
spt-msg             ← Message delivery test (1 file)
mock-adapter        ← Manifest validation (1 file)
```

**Package names as they appear in Cargo.toml:**
- `spt-daemon`
- `spt`
- `spt-term`
- `spt-msg`
- `mock-adapter`
- (Non-test crates: spt-live, spt-proto, spt-store, spt-net, spt-runtime, xtask)

---

### 4. EXISTING NEXTEST CONFIGURATION

**File:** `.config/nextest.toml`

**Current config:** **ONLY `slow-timeout` profile, NO test-groups or overrides**

```toml
[profile.default]
slow-timeout = { period = "60s", terminate-after = 4 }
```

**Status:**
- ✅ No existing `[test-groups]` section
- ✅ No existing `[[profile.default.overrides]]` for serialization
- ✅ No profile-specific or CI overrides anywhere in `.config/nextest.toml`

---

### 5. CI NEXTEST FILTER EXPRESSIONS (WORKFLOW FILES)

**File:** `.github/workflows/ci.yml`

**Current nextest invocation (line 74):**
```yaml
- name: Test (nextest)
  run: cargo nextest run --workspace
```

**Status:**
- ✅ No `--filter-expr` or `-E` filter expressions
- ✅ No `--test-group` or partitioning
- ✅ No per-binary concurrency limits
- ✅ No custom profile overrides in CI

**Other workflows:**
- `release.yml`: Only builds binaries, no nextest invocation
- `docs-publish.yml`: Publishes docs, no tests

---

### 6. CRITICAL ISOLATION REQUIREMENT

**File:** `crates/spt-daemon/tests/inject_control_wedge.rs` (lines 28–40)

The `inject_control_wedge.rs` test module **REQUIRES process isolation** (process-per-test):

```
## RUNNER CONTRACT — run process-per-test (nextest) or single-threaded (W1b F1)

These tests share ONE process-global `SPT_HOME` (set once via
[`std::env::set_var`] in [`init_wedge_home`]) and each spins a REAL broker +
PTY child. Under the default `cargo test` harness — N tests as parallel
THREADS in ONE process — the `set_var` mutation races and the concurrent
brokers contend, which HANGS on Windows (cost a 30-min wedge during W1b).

Run them isolated:
  - `cargo nextest run --test inject_control_wedge`  (process-per-test — CI's path, always safe), or
  - `cargo test --test inject_control_wedge -- --test-threads=1`  (single-threaded fallback).

Do NOT run a bare `cargo test --test inject_control_wedge` on Windows.
```

**Implication:** The 10 tests in `inject_control_wedge.rs` MUST be serialized or at least run in separate processes. Nextest's default behavior (process-per-test-binary) already isolates the entire binary, but capping concurrency within the binary is advisable.

---

### SUMMARY FOR NEXTEST CONFIGURATION

**Heavy test inventory ready for test-group creation:**

```
Test Group: "heavy-broker-pty"
├─ spt-daemon::broker::spawn_env_reaches_child
├─ spt-daemon::attach::attach_survives_target_brain_restart_exactly_once
├─ spt-daemon::broker::controller_writer_reorder_consumer_view_stays_monotonic_and_session_live
└─ [ALL 10 tests in inject_control_wedge.rs (g1_, g2_, g3_, g4_, w5_a1, w5_a2, p0_paste_wedge)]

Recommended profile override:
├─ max-threads = 1 (serialize all heavy tests within their binary)
├─ min-cpus = 1 (single CPU for shared SPT_HOME safety on Windows)
└─ timeout = "120s" (double the slow-timeout backstop for real broker overhead)
```

**Filter expressions for cargo nextest:**
```bash
# Run only heavy tests:
cargo nextest run -p spt-daemon --filter-expr 'test(/^spawn_env_reaches_child|attach_survives|controller_writer_reorder_consumer_view|g[1-4]_|w5_a[1-2]|p0_paste_wedge/)'

# Run inject_control_wedge with serial execution:
cargo nextest run --test inject_control_wedge --max-threads=1
```

---

This mapping is **precise and exhaustive**. You can now write filter expressions and test-group configs directly from these function names and locations.</result>
<usage><subagent_tokens>45114</subagent_tokens><tool_uses>42</tool_uses><duration_ms>134131</duration_ms></usage>
</task-notification>


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


<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>


Full map in hand. All observed flaky members live in 3 spt-daemon binaries: `broker` (spawn_env, controller_writer), `inject_control_wedge` (g2/g4/w5_a2), `attach` (attach_survives). Diagnosis = thundering-herd contention (forkpty + UDS-bind + real broker) under high nextest parallelism.

CI runs default profile → my `.config/nextest.toml` applies automatically, no workflow edit.

Design decision: one test-group `heavy-broker-pty`, scoped to the heavy real-broker/PTY spt-daemon binaries, `max-threads = 1`. Full serial = deterministic (cap >1 still races occasionally → "lucky" not reliable). Tail bounded because rest of workspace parallelizes alongside.


Write

# cargo-nextest configuration.
#
# CI runs the workspace test suite under `cargo nextest run` (one global parallel
# pool across all test binaries — far faster than `cargo test`'s serial-per-binary
# execution on a multi-core runner). See docs/RELEASE-RUNBOOK + ci-kitsubito.

[profile.default]
# A hung test (e.g. a blocking PTY/socket read that never completes under heavy
# parallelism) must FAIL FAST, never wedge the CI job up to its wall-clock
# timeout. Warn once a test exceeds 60s, then SIGKILL + fail it after 4 such
# periods (4 minutes). No legitimate test in this workspace runs that long; this
# is purely the no-infinite-wedge backstop.
slow-timeout = { period = "60s", terminate-after = 4 }

# Concurrency cap for the heavy real-broker + PTY integration tests.
#
# WHY: nextest fans the whole workspace into ONE global parallel pool sized to
# the runner's core count. The spt-daemon integration tests in `broker`,
# `attach`, and `inject_control_wedge` each spin a REAL broker, bind a real Unix
# domain socket / named pipe, and forkpty a real PTY child. When ~16 of those
# launch at once on a high-core CI box they contend for the scheduler, PTY
# allocation, and socket setup — producing wait-deadline / PTY-spawn / child-reap
# TIMING flakes whose victim changes run to run (observed: spawn_env_reaches_child
# on Linux forkpty, g4_translation_child_is_reaped on Windows, g2_*, w5_a2_*,
# controller_writer_*, attach_survives_*). This is load/timing contention, NOT a
# product bug — confirmed grounded (sockets use a per-process-unique name, so it
# is not a bind collision).
#
# FIX: serialize this heavy class via a test-group capped at one concurrent
# member. A cap > 1 would still let two real brokers race and stays "lucky, not
# reliable"; max-threads = 1 is contention-free and therefore deterministic. The
# wall-clock cost is bounded: the group only gates these binaries — the rest of
# the workspace (spt E2E, spt-term, net-transport tests) keeps running in
# parallel on the remaining threads. daemon_e2e + daemon_lifecycle_real_brain are
# included as the nearest heavy real-broker/PTY neighbours so they cannot become
# the next victim.
[test-groups]
heavy-broker-pty = { max-threads = 1 }

[[profile.default.overrides]]
filter = 'package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)'
test-group = 'heavy-broker-pty'


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

# cargo-nextest configuration.
#
# CI runs the workspace test suite under `cargo nextest run` (one global parallel
# pool across all test binaries — far faster than `cargo test`'s serial-per-binary
# execution on a multi-core runner). See docs/RELEASE-RUNBOOK + ci-kitsubito.

[profile.default]
# A hung test (e.g. a blocking PTY/socket read that never completes under heavy
# parallelism) must FAIL FAST, never wedge the CI job up to its wall-clock
# timeout. Warn once a test exceeds 60s, then SIGKILL + fail it after 4 such
# periods (4 minutes). No legitimate test in this workspace runs that long; this
# is purely the no-infinite-wedge backstop.
slow-timeout = { period = "60s", terminate-after = 4 }

# Concurrency cap for the heavy real-broker + PTY integration tests.
#
# WHY: nextest fans the whole workspace into ONE global parallel pool sized to
# the runner's core count. The spt-daemon integration tests in `broker`,
# `attach`, and `inject_control_wedge` each spin a REAL broker, bind a real Unix
# domain socket / named pipe, and forkpty a real PTY child. When ~16 of those
# launch at once on a high-core CI box they contend for the scheduler, PTY
# allocation, and socket setup — producing wait-deadline / PTY-spawn / child-reap
# TIMING flakes whose victim changes run to run (observed: spawn_env_reaches_child
# on Linux forkpty, g4_translation_child_is_reaped on Windows, g2_*, w5_a2_*,
# controller_writer_*, attach_survives_*). This is load/timing contention, NOT a
# product bug — confirmed grounded (sockets use a per-process-unique name, so it
# is not a bind collision).
#
# FIX: serialize this heavy class via a test-group capped at one concurrent
# member. A cap > 1 would still let two real brokers race and stays "lucky, not
# reliable"; max-threads = 1 is contention-free and therefore deterministic. The
# wall-clock cost is bounded: the group only gates these binaries — the rest of
# the workspace (spt E2E, spt-term, net-transport tests) keeps running in
# parallel on the remaining threads. daemon_e2e + daemon_lifecycle_real_brain are
# included as the nearest heavy real-broker/PTY neighbours so they cannot become
# the next victim.
[test-groups]
heavy-broker-pty = { max-threads = 1 }

[[profile.default.overrides]]
filter = 'package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)'
test-group = 'heavy-broker-pty'

+# Concurrency cap for the heavy real-broker + PTY integration tests.

+# WHY: nextest fans the whole workspace into ONE global parallel pool sized to

+# the runner's core count. The spt-daemon integration tests in `broker`,

+# `attach`, and `inject_control_wedge` each spin a REAL broker, bind a real Unix

+# domain socket / named pipe, and forkpty a real PTY child. When ~16 of those

+# launch at once on a high-core CI box they contend for the scheduler, PTY

+# allocation, and socket setup — producing wait-deadline / PTY-spawn / child-reap

+# TIMING flakes whose victim changes run to run (observed: spawn_env_reaches_child

+# on Linux forkpty, g4_translation_child_is_reaped on Windows, g2_*, w5_a2_*,

+# controller_writer_*, attach_survives_*). This is load/timing contention, NOT a

+# product bug — confirmed grounded (sockets use a per-process-unique name, so it

+# FIX: serialize this heavy class via a test-group capped at one concurrent

+# member. A cap > 1 would still let two real brokers race and stays "lucky, not

+# reliable"; max-threads = 1 is contention-free and therefore deterministic. The

+# wall-clock cost is bounded: the group only gates these binaries — the rest of

+# the workspace (spt E2E, spt-term, net-transport tests) keeps running in

+# parallel on the remaining threads. daemon_e2e + daemon_lifecycle_real_brain are

+# included as the nearest heavy real-broker/PTY neighbours so they cannot become

+filter = 'package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)'


Config written. Now verify nextest parses it + the filter actually selects the intended binaries (no typos in binary names).


Bash

cargo nextest list -p spt-daemon --message-format human 2>&1 | grep -iE "test-group|heavy|error|warning: profile|broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle" | head -40

List spt-daemon tests, check group assignment


Command running in background with ID: b5manjhxa. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\b5manjhxa.output. You will be notified when it completes. To check interim output, use Read on that file path.


Compiling (first nextest list builds test targets). Waiting. Meanwhile — verify the filter expr selects exactly the intended binaries with a separate list call.


cd "C:\Users\decid\Documents\projects\spt-core" && cargo nextest list --workspace -E 'package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)' --message-format human 2>&1 | tail -60

List tests matched by the group filter expr


Command running in background with ID: bbcjhr7lk. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bbcjhr7lk.output. You will be notified when it completes. To check interim output, use Read on that file path.


Both list calls compiling in background. Will check when done.


noop


ok


<task-notification>
<task-id>b5manjhxa</task-id>
<tool-use-id>toolu_01QzK6WbbPrj7SaE7EYWm2GB</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\b5manjhxa.output</output-file>
<status>completed</status>
<summary>Background command "List spt-daemon tests, check group assignment" completed (exit code 0)</summary>
</task-notification>


Two list verifications compiling (build lock may serialize them). Will report once they finish — checking the filter matches exactly {broker, attach, inject_control_wedge, daemon_e2e, daemon_lifecycle_real_brain} and nothing stray, and that nextest accepts the test-group config.


<system-reminder>Warning: the file exists but is shorter than the provided offset (1). The file has 1 lines.</system-reminder>


Prompt:
<task-notification>
<task-id>b5manjhxa</task-id>
<tool-use-id>toolu_01QzK6WbbPrj7SaE7EYWm2GB</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\b5manjhxa.output</output-file>
<status>completed</status>
<summary>Background command "List spt-daemon tests, check group assignment" completed (exit code 0)</summary>
</task-notification>


1	    applyhost::tests::apply_staged_update_set_refuses_broker_touching_payloads
2	    applyhost::tests::apply_staged_without_broker_hosted_sessions_swaps_binary
3	    broker::tests::advance_delivered_is_monotonic
4	    broker::tests::apply_once_enqueues_ptywrite_exactly_once_and_dedups_replay
5	    broker::tests::controller_deliver_is_bounded_never_a_permanent_park
6	    broker::tests::delivered_cursor_advances_only_on_ok_and_is_monotonic
7	    broker::tests::input_enqueue_drops_and_stamps_on_full_then_heals_on_drain
8	    broker::tests::input_writer_preserves_strict_fifo_order_through_the_sole_writer
9	    broker::tests::sessions_reply_resume_seq_defaults_when_absent
10	    broker::tests::superseded_controller_writer_is_epoch_gated_silent_one_live_writer_per_conn
11	    broker::tests::viewer_overflow_or_disconnect_evicts_never_blocks
12	    frame::tests::handshake_accepts_newer_brain_on_older_broker
13	    frame::tests::unknown_kind_is_not_a_decode_error
14	    livehost::tests::pull_reconcile_offlines_perch_when_broker_session_dies
15	    nethost::tests::bounded_block_on_cuts_a_never_completing_op_with_an_ordinary_error
16	    notif::tests::shell_notif_templates_render_for_attached_instances_only
17	    pairhost::tests::wire_errors_classify_for_the_guided_ux
18	    update::tests::classify_brain_only_when_broker_hosts_new_brain
19	    update::tests::classify_broker_breaking_on_resource_abi_change
20	    update::tests::classify_broker_compatible_when_brain_below_floor
21	spt-daemon::attach:
22	    attach_registers_remote_drive_detection
23	    attach_survives_target_brain_restart_exactly_once
24	    broker_spawns_the_pty_child_in_the_requested_cwd
25	    local_attach_via_loopback_conn_rides_the_same_pump
26	    loopback_attach_to_a_prepopulated_ring_delivers_without_deadlock
27	    re_serve_resets_resume_cursor_after_a_pre_attached_consume
28	    remote_attach_drives_a_real_pty_cross_daemon
29	    spt_hosted_bringup_then_cross_node_attach_drives_the_pty
30	spt-daemon::broker:
31	    broker_hosts_pty_child_streams_output_and_accepts_input
32	spt-daemon::daemon_e2e:
33	spt-daemon::daemon_lifecycle_real_brain:
34	    dispatcher_serves_a_remote_drive_attach_undriven
35	spt-daemon::inject_control_wedge:
36	    a_journaled_input_wedge_does_not_starve_a_concurrent_rc_attach
37	    p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
38	    input_flood_through_serve_attach_does_not_deadlock_broker
39	spt-daemon::netbroker:
40	    brain_dials_second_broker_over_loopback
41

    applyhost::tests::apply_staged_update_set_refuses_broker_touching_payloads
    applyhost::tests::apply_staged_without_broker_hosted_sessions_swaps_binary
    broker::tests::advance_delivered_is_monotonic
    broker::tests::apply_once_enqueues_ptywrite_exactly_once_and_dedups_replay
    broker::tests::controller_deliver_is_bounded_never_a_permanent_park
    broker::tests::delivered_cursor_advances_only_on_ok_and_is_monotonic
    broker::tests::input_enqueue_drops_and_stamps_on_full_then_heals_on_drain
    broker::tests::input_writer_preserves_strict_fifo_order_through_the_sole_writer
    broker::tests::sessions_reply_resume_seq_defaults_when_absent
    broker::tests::superseded_controller_writer_is_epoch_gated_silent_one_live_writer_per_conn
    broker::tests::viewer_overflow_or_disconnect_evicts_never_blocks
    frame::tests::handshake_accepts_newer_brain_on_older_broker
    frame::tests::unknown_kind_is_not_a_decode_error
    livehost::tests::pull_reconcile_offlines_perch_when_broker_session_dies
    nethost::tests::bounded_block_on_cuts_a_never_completing_op_with_an_ordinary_error
    notif::tests::shell_notif_templates_render_for_attached_instances_only
    pairhost::tests::wire_errors_classify_for_the_guided_ux
    update::tests::classify_brain_only_when_broker_hosts_new_brain
    update::tests::classify_broker_breaking_on_resource_abi_change
    update::tests::classify_broker_compatible_when_brain_below_floor
spt-daemon::attach:
    attach_registers_remote_drive_detection
    attach_survives_target_brain_restart_exactly_once
    broker_spawns_the_pty_child_in_the_requested_cwd
    local_attach_via_loopback_conn_rides_the_same_pump
    loopback_attach_to_a_prepopulated_ring_delivers_without_deadlock
    re_serve_resets_resume_cursor_after_a_pre_attached_consume
    remote_attach_drives_a_real_pty_cross_daemon
    spt_hosted_bringup_then_cross_node_attach_drives_the_pty
spt-daemon::broker:
    broker_hosts_pty_child_streams_output_and_accepts_input
spt-daemon::daemon_e2e:
spt-daemon::daemon_lifecycle_real_brain:
    dispatcher_serves_a_remote_drive_attach_undriven
spt-daemon::inject_control_wedge:
    a_journaled_input_wedge_does_not_starve_a_concurrent_rc_attach
    p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
    input_flood_through_serve_attach_does_not_deadlock_broker
spt-daemon::netbroker:
    brain_dials_second_broker_over_loopback


First list succeeded (exit 0) — confirms nextest **parsed my `[test-groups]` + override config without error** (key validation). All 5 target binaries exist: `attach`, `broker`, `daemon_e2e`, `daemon_lifecycle_real_brain`, `inject_control_wedge`. Guard test `re_serve_resets_resume_cursor...` and keystone `attach_survives...` both present.

Waiting on the filter-expr list to confirm exact selection.


Still building. Let me prep verification: run the heavy group locally under the new serial config to confirm green (Windows side — g4/translation reap flaked here). And check traceability obligations.


93:[Omitted long matching line]
97:[Omitted long matching line]
326:[Omitted long matching line]
720:title = "State-file reads tolerate concurrent writes (1.2)"
793:id = "REQ-HAZARD-REGISTRY-CONCURRENT"
794:title = "Concurrent SQLite openers (registry/spool) must not fail with 'database is locked' (4.7)"
826:title = "Strip Windows UNC prefix on serialized paths (5.4)"
850:title = "A surfaced concurrent context pair is durably preserved (both versions, tracked artifacts) until a strictly dominating write clears it; no reconcile failure path discards an unmerged version (6.6, ADR-0013)"
885:[Omitted long matching line]
932:title = "Per-agent pulse/psyche/echo-commune scheduling must not serialize across agents: each agent's bounded LLM call (echo-commune summarizer, Psyche turn) runs off the shared scheduler so one slow/hung call cannot stall another agent's tick (7.4)"
946:[Omitted long matching line]
1112:title = "Concurrent liveness probes: `spt subnet status --nodes` fans out its offline/serve-probes (REQ-SUBNET-5) CONCURRENTLY — total wall-time bounded by the single-probe ceiling (~3s), never k×ceiling. The mesh makes a node see ALL members (many possibly offline), so a serial probe loop would be offline_count×3s. (Planning verifies the current REQ-SUBNET-5 probe loop's behavior and fixes it if serial.)"
1113:[Omitted long matching line]
1215:[Omitted long matching line]
1232:[Omitted long matching line]
1233:[Omitted long matching line]
1251:[Omitted long matching line]
1257:[Omitted long matching line]
1269:[Omitted long matching line]
1280:[Omitted long matching line]
1281:[Omitted long matching line]


No REQ covers CI test-infra flake — pure harness config, no evidence stage to tag. traceable-reqs check still must pass (won't be affected).

Waiting on filter-expr list.


f="C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bbcjhr7lk.output"; wc -l <"$f"; tail -50 "$f"

Check filter-expr list output


Second list still running (was blocked behind first on the build lock). Awaiting completion notification.


Still compiling (full `--workspace` build). I'll wait for the completion notification rather than poll.


<task-notification>
<task-id>bbcjhr7lk</task-id>
<tool-use-id>toolu_01J9Gn15KRZa11VsQXBrXHCK</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bbcjhr7lk.output</output-file>
<status>completed</status>
<summary>Background command "List tests matched by the group filter expr" completed (exit code 0)</summary>
</task-notification>


f="C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bbcjhr7lk.output"; grep -E "^spt-daemon::|binary|tests:|::" "$f" | grep -vE "^    " | head; echo "---BINARIES---"; grep -oE "spt-daemon::[a-z_]+" "$f" | sort -u; echo "---COUNT---"; grep -cE "^    [a-z]" "$f"

Extract binaries + test count from filter list


spt-daemon::attach:
spt-daemon::broker:
spt-daemon::daemon_e2e:
spt-daemon::daemon_lifecycle_real_brain:
spt-daemon::inject_control_wedge:
---BINARIES---
spt-daemon::attach
spt-daemon::broker
spt-daemon::daemon_e
spt-daemon::daemon_lifecycle_real_brain
spt-daemon::inject_control_wedge
---COUNT---
35


Filter selects EXACTLY 5 intended binaries (attach, broker, daemon_e2e, daemon_lifecycle_real_brain, inject_control_wedge), 35 tests, zero stray. Config validated.

Now live-verify: run the heavy group under the new serial config on this Windows box (where g4 reap flaked) — confirm green + serialization takes effect.


cd "C:\Users\decid\Documents\projects\spt-core" && cargo nextest run --workspace -E 'package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)' 2>&1 | tail -50

Run heavy group under new serial config


Command running in background with ID: begs9w2qk. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\begs9w2qk.output. You will be notified when it completes. To check interim output, use Read on that file path.


Heavy group running serial (35 tests). Awaiting result.


<task-notification>
<task-id>begs9w2qk</task-id>
<tool-use-id>toolu_012Tt3LvozwBhVJc4zHbchJ9</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\begs9w2qk.output</output-file>
<status>completed</status>
<summary>Background command "Run heavy group under new serial config" completed (exit code 0)</summary>
</task-notification>


Read heavy group run result


 TERMINATING [>240.000s] (─────) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
     TIMEOUT [ 240.045s] (33/35) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
  stdout ───

    running 1 test
    test p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker has been running for over 60 seconds
  stderr ───
    PUMP_IPC_READER: spawned
    PUMP_IPC_READER: spawned
    PUMP_IPC_READER: exited
    PUMP_IPC_READER: exited

    (test timed out)

  Cancelling due to test failure:
────────────
     Summary [ 274.232s] 33/35 tests run: 32 passed, 1 timed out, 0 skipped
     TIMEOUT [ 240.045s] (33/35) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
warning: 2/35 tests were not run due to test failure (run with --no-fail-fast to run all tests, or run with --max-fail)
error: test run failed


Run FAILED — but not a config defect. `p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker` TIMED OUT at 240s (slow-timeout backstop = 60s×4 fired). 32/35 passed, fail-fast cancelled 2.

Key reasoning: test-group only changes SCHEDULING, not test behavior (process-per-test isolation). Serial makes each test face LESS contention, not more — so this wedge is NOT caused by my change. Likely my dev box thrashed (I just ran parallel workspace compiles + agents). p0_paste_wedge was NOT in doyle's flaky-failer list → passes in CI.

Confirm: run p0_paste_wedge alone on a quieter box.


cd "C:\Users\decid\Documents\projects\spt-core" && cargo nextest run -p spt-daemon --test inject_control_wedge -E 'test(p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker)' 2>&1 | tail -25

Run p0_paste_wedge alone


Command running in background with ID: bilmk23ul. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bilmk23ul.output. You will be notified when it completes. To check interim output, use Read on that file path.


Awaiting isolated p0_paste_wedge result. If green alone → confirmed environmental (my box load), config sound.


<task-notification>
<task-id>bilmk23ul</task-id>
<tool-use-id>toolu_01YGYbrweuk6pTXt8r4RX5ex</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bilmk23ul.output</output-file>
<status>completed</status>
<summary>Background command "Run p0_paste_wedge alone" completed (exit code 0)</summary>
</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.


Read isolated p0 result


    running 1 test
    test p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker has been running for over 60 seconds
  stderr ───
    PUMP_IPC_READER: spawned
    PUMP_IPC_READER: spawned
    PUMP_IPC_READER: exited
    PUMP_IPC_READER: exited

    (test timed out)

  Cancelling due to test failure:
────────────
     Summary [ 240.045s] 1 test run: 0 passed, 1 timed out, 9 skipped
     TIMEOUT [ 240.044s] (1/1) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
error: test run failed


p0_paste_wedge HANGS even ALONE (240s, isolated). But — this test PASSED in doyle's last CI (bdb51c2 only flaked g4 + spawn_env). So it's green in fresh CI, wedges on MY box.

Prime suspect: leaked daemon/broker processes. My earlier heavy-group run was fail-fast KILLED mid-flight (2 tests cancelled) → orphaned broker/PTY children holding sockets/pipes → poison the re-run. This is exactly the cross-run leak that commit 40b0072 guards in CI. Check + reap.


PowerShell

Get-Process | Where-Object { $_.ProcessName -match 'spt|broker|brain|daemon|cat|findstr|mock' } | Select-Object Id, ProcessName, StartTime | Format-Table -AutoSize

List possibly-leaked test daemon processes


    Id ProcessName               StartTime
    -- -----------               ---------
 19692 ApplicationFrameHost      6/10/2026 2:44:29 PM
244648 claude-spt-psyche         6/20/2026 4:41:41 PM
 42532 findstr                   6/20/2026 5:32:50 PM
 57272 findstr                   6/20/2026 8:08:30 PM
  5552 logioptionsplus_appbroker 6/10/2026 2:20:44 PM
206972 MoNotificationUx          6/20/2026 1:00:13 PM
 16508 RuntimeBroker             6/10/2026 2:20:44 PM
 18492 RuntimeBroker             6/10/2026 2:20:47 PM
 36948 RuntimeBroker             6/13/2026 9:32:34 PM
110580 RuntimeBroker             6/14/2026 12:06:59 AM
111500 RuntimeBroker             6/14/2026 12:03:28 AM
237828 RuntimeBroker             6/13/2026 9:29:35 PM
  3536 spt                       6/20/2026 8:18:23 PM
127440 spt
163632 spt                       6/20/2026 3:33:46 AM
179068 spt                       6/20/2026 3:33:46 AM
246196 spt
 52644 UserOOBEBroker            6/13/2026 7:38:01 PM


Leaked test processes confirmed: orphan `findstr` (Windows PTY echo child from broker tests) ×2 + several stale `spt`. These hold pipes/PTYs → wedge p0. But must NOT kill my owl perch (`claude-spt-psyche` 244648 + its broker). Identify before reaping — check command lines.


Get-CimInstance Win32_Process -Filter "Name='spt.exe' OR Name='findstr.exe'" | Select-Object ProcessId, Name, CreationDate, @{n='Cmd';e={$_.CommandLine}} | Format-List

Show command lines of spt/findstr processes


ProcessId    : 179068
Name         : spt.exe
CreationDate : 6/20/2026 3:33:46 AM
Cmd          : C:\Users\decid\AppData\Local\spt-core\bin\spt.exe  daemon run --detached

ProcessId    : 163632
Name         : spt.exe
CreationDate : 6/20/2026 3:33:46 AM
Cmd          : "C:\Users\decid\AppData\Local\spt-core\bin\spt.exe" daemon brain --generation 0 --start-reason cold

ProcessId    : 42532
Name         : findstr.exe
CreationDate : 6/20/2026 5:32:50 PM
Cmd          : C:\WINDOWS\system32\findstr.EXE .

ProcessId    : 246196
Name         : spt.exe
CreationDate : 6/20/2026 6:56:31 PM
Cmd          :

ProcessId    : 127440
Name         : spt.exe
CreationDate : 6/20/2026 6:56:31 PM
Cmd          :

ProcessId    : 57272
Name         : findstr.exe
CreationDate : 6/20/2026 8:08:30 PM
Cmd          : C:\WINDOWS\system32\findstr.EXE .

ProcessId    : 38888
Name         : spt.exe
CreationDate : 6/20/2026 8:18:48 PM
Cmd          : "spt" ready wall-a-psyche --once


Classified:
- **KEEP** (perch infra): `claude-spt-psyche` 244648, installed daemon `spt daemon run --detached` 179068 + its brain 163632, `spt ready wall-a-psyche` 38888.
- **TEST LEAKS → reap**: orphan `findstr .` 42532 + 57272 (Windows PTY echo children), `spt.exe` empty-cmd 246196 + 127440 (6:56PM leaked broker/child pair).

Reaping leaks only.


foreach ($pid_ in 42532,57272,246196,127440) { try { Stop-Process -Id $pid_ -Force -ErrorAction Stop; "killed $pid_" } catch { "skip $pid_ : $($_.Exception.Message)" } }; "---remaining---"; Get-CimInstance Win32_Process -Filter "Name='spt.exe' OR Name='findstr.exe'" | Select-Object ProcessId,@{n='Cmd';e={$_.CommandLine}} | Format-Table -AutoSize

Reap leaked test processes


killed 42532
killed 57272
skip 246196 : Cannot stop process "spt (246196)" because of the following error: Access is denied.
skip 127440 : Cannot stop process "spt (127440)" because of the following error: Access is denied.
---remaining---

ProcessId Cmd
--------- ---
   179068 C:\Users\decid\AppData\Local\spt-core\bin\spt.exe  daemon run --detached
   163632 "C:\Users\decid\AppData\Local\spt-core\bin\spt.exe" daemon brain --generation 0 --start-reason cold
   246196
   127440
    86092 findstr  /I "Code.exe Cursor.exe Windsurf.exe Devin.exe idea64.exe pycharm64.exe webstorm64.exe phpstorm64.exe rubymi…
   133792 "spt" ready wall-a-psyche --once


Killed both orphan `findstr` (the PTY resource holders). 2 empty-cmd `spt` survive (Access denied — need elevation). Re-test p0 now that PTY orphans gone.


Bash

cd "C:\Users\decid\Documents\projects\spt-core" && cargo nextest run -p spt-daemon --test inject_control_wedge -E 'test(p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker)' 2>&1 | tail -15

Re-run p0_paste_wedge after reaping findstr orphans


Command running in background with ID: bpho1lp7u. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bpho1lp7u.output. You will be notified when it completes. To check interim output, use Read on that file path.


<task-notification>
<task-id>bpho1lp7u</task-id>
<tool-use-id>toolu_01LBdvMQwBws3XHgd8xWwvK9</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bpho1lp7u.output</output-file>
<status>completed</status>
<summary>Background command "Re-run p0_paste_wedge after reaping findstr orphans" completed (exit code 0)</summary>
</task-notification>


Awaiting p0 re-run. While it runs — the deliverable is already substantially validated (config parses, selects exactly the 5 binaries, 32/35 pass, the 1 hang reproduces under plain single-test invocation = not config-caused). doyle's fresh CI is the authoritative gate.


1	    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.56s
2	────────────
3	 Nextest run ID a40b63fc-a04f-409f-8a69-5f87c890065d with nextest profile: default
4	    Starting 1 test across 1 binary (9 tests skipped)
5	        PASS [   3.069s] (1/1) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
6	────────────
7	     Summary [   3.069s] 1 test run: 1 passed, 9 skipped
8

    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.56s
────────────
 Nextest run ID a40b63fc-a04f-409f-8a69-5f87c890065d with nextest profile: default
    Starting 1 test across 1 binary (9 tests skipped)
        PASS [   3.069s] (1/1) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
────────────
     Summary [   3.069s] 1 test run: 1 passed, 9 skipped


Prompt:
<task-notification>
<task-id>bpho1lp7u</task-id>
<tool-use-id>toolu_01LBdvMQwBws3XHgd8xWwvK9</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bpho1lp7u.output</output-file>
<status>completed</status>
<summary>Background command "Re-run p0_paste_wedge after reaping findstr orphans" completed (exit code 0)</summary>
</task-notification>


CONFIRMED. p0_paste_wedge PASSES in **3.069s** after reaping the orphan `findstr`. The 240s hang was leaked-PTY-child contamination from my fail-fast-killed run — NOT my config, NOT a real hang. (CI passes because 40b0072 reaps these pre-checkout.)

Config sound. Final clean full-group run under the real serial config, `--no-fail-fast` (so a straggler can't abort-leak again) → want 35/35.


cd "C:\Users\decid\Documents\projects\spt-core" && cargo nextest run --workspace --no-fail-fast -E 'package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)' 2>&1 | tail -20

Full heavy group, serial config, no-fail-fast


Command running in background with ID: byldy5jfa. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\byldy5jfa.output. You will be notified when it completes. To check interim output, use Read on that file path.


Full group running serial (35 tests, no-fail-fast). Awaiting 35/35.


<task-notification>
<task-id>byldy5jfa</task-id>
<tool-use-id>toolu_01HymeYJuyFJGDohzSTNA6C8</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\byldy5jfa.output</output-file>
<status>completed</status>
<summary>Background command "Full heavy group, serial config, no-fail-fast" completed (exit code 0)</summary>
</task-notification>


Read full group result


        PASS [   3.202s] (26/35) spt-daemon::inject_control_wedge a_backed_up_controller_does_not_wedge_the_session
        PASS [   3.037s] (27/35) spt-daemon::inject_control_wedge a_journaled_input_wedge_does_not_starve_a_concurrent_rc_attach
        PASS [  12.422s] (28/35) spt-daemon::inject_control_wedge g1_choreography_happy_path_payload_reaches_pty_and_controller_keeps_control
        PASS [   0.660s] (29/35) spt-daemon::inject_control_wedge g2_no_commit_deadline_faults_binary_and_does_not_wedge_controller_input
        PASS [   0.806s] (30/35) spt-daemon::inject_control_wedge g3_content_free_ping_never_duplicates_operator_keystrokes_into_binary
        PASS [   0.694s] (31/35) spt-daemon::inject_control_wedge g4_translation_child_is_reaped_no_zombie_after_session_down
        PASS [   0.479s] (32/35) spt-daemon::inject_control_wedge injecting_a_large_payload_into_a_stdin_ignoring_child
        PASS [   3.045s] (33/35) spt-daemon::inject_control_wedge p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker
        PASS [   0.540s] (34/35) spt-daemon::inject_control_wedge w5_a1_locally_driven_session_reports_controller_by_none
        PASS [   4.293s] (35/35) spt-daemon::inject_control_wedge w5_a2_abandoned_remote_controller_idle_session_keeps_controller_by_some
────────────
     Summary [  44.144s] 35 tests run: 35 passed, 0 skipped


35/35 PASSED, 44.1s serial. All named flaky members green: **g2 ✓ g4 ✓ w5_a2 ✓ p0 ✓** (+ broker/attach in the 1-25 range). Serial tail 44s — runs alongside the parallel rest-of-suite, bounded.

Deliverable validated. Finalize: traceable-reqs gate (binding), then commit.


cd "C:\Users\decid\Documents\projects\spt-core" && traceable-reqs check 2>&1 | tail -15; echo "[exit ${PIPESTATUS[0]}]"

Run traceability gate


Command running in background with ID: btxmsxnve. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\btxmsxnve.output. You will be notified when it completes. To check interim output, use Read on that file path.


<task-notification>
<task-id>btxmsxnve</task-id>
<tool-use-id>toolu_01KMAJCWsbhn7ooV72WM1sfs</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\btxmsxnve.output</output-file>
<status>completed</status>
<summary>Background command "Run traceability gate" completed (exit code 0)</summary>
</task-notification>


cd "C:\Users\decid\Documents\projects\spt-core" && git diff --stat

Show staged diff stat


warning: in the working copy of '.config/nextest.toml', LF will be replaced by CRLF the next time Git touches it
 .config/nextest.toml | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)


Prompt:
<task-notification>
<task-id>btxmsxnve</task-id>
<tool-use-id>toolu_01KMAJCWsbhn7ooV72WM1sfs</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\btxmsxnve.output</output-file>
<status>completed</status>
<summary>Background command "Run traceability gate" completed (exit code 0)</summary>
</task-notification>


Read traceability result


       Platform-safe `spt update fetch` + apply platform-guard (v0.3.1 cross-OS brick fix): `spt update fetch` stages the signed multi-platform `SignedUpdateSet` (`update-set.json` + every platform artifact it names), never a platform-blind single `SignedRelease`, so local apply selects `current_platform()` and P2P re-serve lets each peer select ITS own platform. Defense-in-depth: `apply_staged` REFUSES a staged single-release artifact unless it is platform-stamped for THIS node (an unstamped pre-v0.3.2 single, or a single stamped for another OS, fail-safe refuses — the guard that alone prevents the v0.3.1 brick where a Linux ELF was applied as `spt.exe`). UX: a friendly post-apply message (`Updated spt-core to vX.Y.Z.` + changelog URL) driven by an additive `product_version` metadata field, with a release-counter fallback when absent.
  [OK] REQ-UPD-9  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       `gh_release` adapter [update] avenue (optional signing): an adapter declares `[update] avenue = "gh_release", repo = "user/repo"` (+ optional `asset`, default `adapter.spt`; + optional Ed25519 `signing_key`); spt-core's ripple compares the repo's LATEST GitHub release version against the installed adapter version and, when newer, auto-updates by fetching the release `.spt` archive (the REQ-INSTALL-9 `--release` fetch primitive) → verifies the `.spt` against `signing_key` if declared, else HTTPS+GitHub first-acquisition trust → re-extracts + re-registers the adapter root. Lets a harness adapter ship updates from its own GitHub releases with NO signing tooling or plugin coupling (removes the perri file_pull/delegated avenue blockers). Acquisition-trust mirrors `--release` + the installer first-fetch; does not alter spt-core self-update (REQ-UPD-1..8).
  [OK] REQ-WHOAMI-1  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       `spt whoami` is a thin ALIAS for `spt endpoint list` (full output: the SELF pin + the subnet roster) — the standalone bare-id command is dropped (the `id=$(spt whoami)` capture was never a real pattern: env vars don't persist between agent tool calls). The one new render: the `endpoint list` SELF pin carries the Self endpoint's authored `endpoint description` (info::read_info(...).resources) when present, inline after the liveness state. whoami stays a top-level hot-path verb (parse unchanged, REQ-MSG-9).
[exit 0]


Gate passes (exit 0). Diff = config-only, +29 lines. Committing.


cd "C:\Users\decid\Documents\projects\spt-core" && git add .config/nextest.toml && git commit -q -F - <<'EOF'
ci(nextest): serialize heavy real-broker/PTY tests to kill load-contention flakes

The spt-daemon integration tests in `broker`, `attach`, and
`inject_control_wedge` each spin a real broker, bind a real UDS/named pipe,
and forkpty a real PTY child. nextest fans the whole workspace into one
global parallel pool sized to the runner core count, so ~16 of these launch
at once on a high-core CI box and contend for the scheduler, PTY allocation,
and socket setup. The result is wait-deadline / PTY-spawn / child-reap TIMING
flakes whose victim rotates run to run (observed: spawn_env_reaches_child on
Linux forkpty, g4_translation_child_is_reaped on Windows, g2_*, w5_a2_*,
controller_writer_*, attach_survives_*). This is load/timing contention, not a
product bug — sockets use a per-process-unique name, so it is not a bind
collision.

Fix: a `heavy-broker-pty` test-group capped at one concurrent member
serializes this class. A cap > 1 would still let two real brokers race and
stays lucky-not-reliable; max-threads = 1 is contention-free and therefore
deterministic. Wall-clock cost is bounded — the group only gates these
binaries, so the rest of the workspace keeps running in parallel on the
remaining threads. daemon_e2e + daemon_lifecycle_real_brain are folded in as
the nearest heavy real-broker/PTY neighbours so they cannot become the next
victim.

Verified locally: filter selects exactly the 5 intended binaries (35 tests);
full group passes 35/35 serial in 44s (g2/g4/w5_a2/p0/controller_writer/
attach_survives/spawn_env all green). No test-logic edits.

Co-authored by: todlando
EOF
git log --oneline -1

Commit nextest test-group config


warning: in the working copy of '.config/nextest.toml', LF will be replaced by CRLF the next time Git touches it
f4b97e7 ci(nextest): serialize heavy real-broker/PTY tests to kill load-contention flakes


Committed f4b97e7. Pushing so doyle can fold + re-CI.


cd "C:\Users\decid\Documents\projects\spt-core" && git push origin v0.13.0-p1b-ack-deadlock 2>&1 | tail -8

Push commit to origin


To https://github.com/SaberMage/spt-core.git
   bdb51c2..f4b97e7  v0.13.0-p1b-ack-deadlock -> v0.13.0-p1b-ack-deadlock


Pushed f4b97e7. Notifying doyle to fold + re-CI.


Skill

doyle "Test-infra fix PUSHED → f4b97e7 on v0.13.0-p1b-ack-deadlock. Ready to fold + re-CI (run 2-3x per your reliability gate). WHAT: .config/nextest.toml — new test-group `heavy-broker-pty` {max-threads=1} over filter `package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)`. Serializes the heavy real-broker/PTY class that thundering-herds under nextest's global pool (forkpty+UDS-bind+real-broker contention). Cap=1 not >1: any >1 still races = lucky-not-reliable; 1 is contention-free deterministic. Group only gates those 5 binaries, rest of workspace stays parallel → bounded tail. CI runs default profile so it applies automatically, NO workflow edit. config-only, NO test-logic edits. VALIDATED LOCAL: filter selects exactly the 5 binaries (35 tests, zero stray); full group 35/35 green serial 44s — g2/g4/w5_a2/p0/controller_writer/attach_survives/spawn_env all PASS. traceable-reqs check green. ONE GOTCHA worth knowing (not in the fix): a fail-fast-killed run leaks orphan `findstr` PTY children on Windows that wedge the NEXT run's p0_paste_wedge to the 240s slow-timeout — reaped them, p0 then passed 3s. That's the 40b0072 pre-checkout-reap territory; fresh CI is clean. If a non-spt-daemon test (spt-term forkpty, spt *_e2e) flakes next, widen the binary regex — but all observed victims were in these 3 binaries. Over to you for fold + multi-run CI → then deployah cuts v0.13.0. Reachable. -todlando"


Launching skill: spt:send


Base directory for this skill: C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.25\skills\send

# /spt:send

All commands use `$OWL` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.

> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.

## Messaging Command Reference

Both commands read the message body from **stdin** (pipe or heredoc).

```bash
# send -- fire-and-forget to a target (use when you have your own perch)
$OWL send <target> <<'EOF'
message body
EOF

# send --reply-to -- respond to whoever messaged you
$OWL send --reply-to <sender> [your-id] <<'EOF'
response body
EOF

# ask -- deliver + create ephemeral reply perch + poll for reply (use when you have NO perch)
$OWL ask <target> [from] <<'EOF'
message body
EOF
```

If auto-detection fails, pass your ID explicitly: `$OWL send <target> <your-id>`, `$OWL send --reply-to <sender> <your-id>`, `$OWL ask <target> <impromptu-id>`.

**When to use which:**
- **`send`**: You already have a listener. Fire-and-forget, no reply perch created.
- **`send --reply-to`**: You received a message and want to respond. `from` is auto-set from the original sender.
- **`ask`**: You have no listener. Creates a temporary perch, delivers, then polls for the reply.

## Pre-checks

- If `<target>` matches this agent's own listener ID, refuse. Self-sends create loops.
- All send/reply-to/ask commands verify the target is alive automatically.

## If you already have a listener

Just send -- no reply perch needed. Run **not** in background:

```bash
$OWL send <target> <<'EOF'
<msg>
EOF
```

## If you have no listener

The `ask` subcommand creates an ephemeral reply perch, delivers, and polls for the reply. Run with `run_in_background: true` (or `false` for `--block`):

```bash
$OWL ask <target> <<'EOF'
<msg>
EOF
```

## On response

**Important:** If you have an active listener perch, always launch Agent tool calls with `run_in_background: true`. Foreground agents block your poll loop -- no messages (including replies to your send) can be delivered until the agent finishes.

When the response arrives, present it to the user. Responses arrive via one of three paths:
- **Background interject** -- the `$OWL ask` background task completes with the reply.
- **Inline** -- if you used `--block`, the reply prints directly.
- **Hook-injected XML** -- if you are mid-tool-call, the reply appears as `<owl_messages>` XML in your tool call context. The `from` attribute is the sender.

ARGUMENTS: doyle "Test-infra fix PUSHED → f4b97e7 on v0.13.0-p1b-ack-deadlock. Ready to fold + re-CI (run 2-3x per your reliability gate). WHAT: .config/nextest.toml — new test-group `heavy-broker-pty` {max-threads=1} over filter `package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)`. Serializes the heavy real-broker/PTY class that thundering-herds under nextest's global pool (forkpty+UDS-bind+real-broker contention). Cap=1 not >1: any >1 still races = lucky-not-reliable; 1 is contention-free deterministic. Group only gates those 5 binaries, rest of workspace stays parallel → bounded tail. CI runs default profile so it applies automatically, NO workflow edit. config-only, NO test-logic edits. VALIDATED LOCAL: filter selects exactly the 5 binaries (35 tests, zero stray); full group 35/35 green serial 44s — g2/g4/w5_a2/p0/controller_writer/attach_survives/spawn_env all PASS. traceable-reqs check green. ONE GOTCHA worth knowing (not in the fix): a fail-fast-killed run leaks orphan `findstr` PTY children on Windows that wedge the NEXT run's p0_paste_wedge to the 240s slow-timeout — reaped them, p0 then passed 3s. That's the 40b0072 pre-checkout-reap territory; fresh CI is clean. If a non-spt-daemon test (spt-term forkpty, spt *_e2e) flakes next, widen the binary regex — but all observed victims were in these 3 binaries. Over to you for fold + multi-run CI → then deployah cuts v0.13.0. Reachable. -todlando"


Bash

$OWL send doyle <<'EOF'
Test-infra fix PUSHED -> f4b97e7 on v0.13.0-p1b-ack-deadlock. Ready to fold + re-CI (run 2-3x per your reliability gate).

WHAT: .config/nextest.toml -- new test-group `heavy-broker-pty` {max-threads=1} over filter:
  package(spt-daemon) & kind(test) & binary(/^(broker|attach|inject_control_wedge|daemon_e2e|daemon_lifecycle_real_brain)$/)
Serializes the heavy real-broker/PTY class that thundering-herds under nextest's global pool (forkpty + UDS-bind + real-broker contention). Cap=1 not >1: any >1 still lets two real brokers race = lucky-not-reliable; 1 is contention-free deterministic. Group gates only those 5 binaries -- rest of workspace stays parallel, so the serial tail is bounded. CI runs default profile so it applies automatically: NO workflow edit, config-only, NO test-logic edits.

VALIDATED LOCAL: filter selects exactly the 5 binaries (35 tests, zero stray); full group 35/35 green serial in 44s -- g2 / g4 / w5_a2 / p0_paste_wedge / controller_writer / attach_survives / spawn_env all PASS. traceable-reqs check green.

GOTCHA (not in the fix, FYI): a fail-fast-killed run leaks orphan `findstr` PTY children on Windows that wedge the NEXT run's p0_paste_wedge to the 240s slow-timeout. Reaped them -> p0 passed in 3s. That's 40b0072 pre-checkout-reap territory; fresh CI is clean.

If a non-spt-daemon test (spt-term forkpty, spt *_e2e) flakes next, widen the binary regex -- but every observed victim lived in these 3 binaries.

Over to you: fold + multi-run CI -> then deployah cuts v0.13.0. Reachable. -todlando
EOF

Send fix verdict to doyle


➜ SENT:doyle


Delivered to doyle. Done.

**P1c status:** DONE per doyle (keystone 5/5, guard valid normal+revert, folded PR #26). Only the flaky test-infra cluster remained between here and green-both-runners.

**This session — test-infra fix shipped:**
- **Root cause** (doyle's diagnosis, confirmed): heavy spt-daemon real-broker/PTY tests thundering-herd under nextest's global parallel pool → forkpty + UDS-bind + scheduler contention → rotating timing-flake victim (g4/spawn_env/g2/w5_a2). Not a product bug.
- **Fix** `f4b97e7`: `.config/nextest.toml` test-group `heavy-broker-pty` {max-threads=1} over the 3 flaky binaries + 2 nearest-neighbor heavy ones. Applies via default profile → no workflow edit. Config-only, no test-logic edits.
- **Validated:** filter hits exactly 5 binaries (35 tests); 35/35 green serial in 44s; traceability gate green. Pushed.
- **Side find:** fail-fast-killed runs leak orphan `findstr` PTY children on Windows that wedge the next run's p0 to 240s — reaped, p0 → 3s. Local-only; CI reaps pre-checkout (40b0072).

**Next:** doyle folds + multi-run CI → deployah cuts v0.13.0 (this was the last build item).


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/3" id="3ca6c3f4" type="msg" from="doyle">GATE PASS + FOLDED. f4b97e7 FF'd onto v0.13.0-delivery-control (PR #26 head, clean FF, no force).

Verified your load-bearing claim independently: `cargo nextest list` over the override filter = 35 tests EXACTLY, zero stray. All named flake victims confirmed inside the group: spawn_env_reaches_child (broker), g4_t</EVENT-PART>
<EVENT-PART seq="2/3" id="3ca6c3f4">ranslation_child_is_reaped + w5_a2 + g2 + p0_paste_wedge (inject_control_wedge), controller_writer_reorder (broker), attach_survives (attach). Config-only, slow-timeout backstop intact, no test-logic edits. Reasoning grounded (per-process-unique socket names = not bind collision). Clean.

Push auto-fired CI reliability run #1 (2789212209</EVENT-PART>
<EVENT-PART seq="3/3" id="3ca6c3f4">0, queued). Running 2 more after for the flaky-cluster gate. Green-both-runners x3 -&gt; deployah cuts v0.13.0. Will ping on verdict. Solid work. -doyle</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: GATE PASS + FOLDED. f4b97e7 FF'd onto v0.13.0-delivery-control (PR #26), 35-tests-exactly independently verified. CI reliability run #1 queued (27892122090), 2 more after. Green-both-runners ×3 → deployah cuts v0.13.0.

Nothing on me — doyle owns the gate + pings on verdict, my perch stays reachable. Test-infra cluster was the last build item. Waiting on the ×3 CI verdict.


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/7" id="6a3022cc" type="msg" from="doyle">RELIABILITY GATE: RED x2 — NOT shippable yet. But your group fix WORKED; it exposed a separate deterministic bug. Keep nextest.toml.

EVIDENCE (run 27892122090, both attempts, kitsubito/Linux):
- PRE-fix runs: FAST assertion-fails (26-27ms), ROTATING victims (spawn_env, controller_writer), ~70s total = thunde</EVENT-PART>
<EVENT-PART seq="2/7" id="6a3022cc">ring-herd contention. a_journaled_input_wedge PASSED.
- POST-fix runs (serialized): a_journaled_input_wedge_does_not_starve_a_concurrent_rc_attach TIMEOUT 240s, SAME victim BOTH times, ~300s total. Windows test + both n1-gates + traceability all GREEN.
- So serialization traded the rotating contention-fails for ONE deterministic teardown</EVENT-PART>
<EVENT-PART seq="3/7" id="6a3022cc"> WEDGE on kitsubito.

WHERE IT HANGS (read inject_control_wedge.rs:642-647): hang is in TEARDOWN, not the product assertion. Proof: timed-out capture shows only `PUMP_IPC_READER: spawned/exited`, NO `=== W1b JOURNAL-WEDGE GATE` eprintln (line 675), NO `brain IPC read deadline`, no panic. Main thread stuck at `_broker.session_pid(sid)` (6</EVENT-PART>
<EVENT-PART seq="4/7" id="6a3022cc">42) or `driver.join()` (646) — both have NO timeout; if the broker IPC is wedged or send_effect parks permanently, they ride to the 240s nextest backstop. The product facet-B path is 30s-watchdog'd and did NOT fast-fail with its known symptom.
- Passes locally serial (your 44s) + passed on kitsubito pre-fix concurrent → kitsubito-specif</EVENT-PART>
<EVENT-PART seq="5/7" id="6a3022cc">ic under serialization (full-CPU flood child, and/or a persistent Linux orphan).

ASKS (your domain — test-infra/test-robustness):
1. KEYSTONE — harden teardown so a wedged broker fails FAST not 240s: bound `driver.join()` (join-with-timeout) and `session_pid` (bounded probe). This ALSO re-discriminates: post-harden a FAST ~30s fa</EVENT-PART>
<EVENT-PART seq="6/7" id="6a3022cc">il w/ subscribed=false = REAL product wedge (P1b reopen); a PASS = teardown-join artifact. Right now the 240s backstop masks which.
2. Linux pre-checkout reap — ci.yml:211 reap is `if: runner.os == 'Windows'` ONLY. kitsubito has NO orphan cleanup across failed runs. Add the Linux analog (forkpty children + workspace daemons). If a persist</EVENT-PART>
<EVENT-PART seq="7/7" id="6a3022cc">ent orphan holds the journal/PTY, this clears it.
3. If you can reach kitsubito: check for a stuck orphan broker/forkpty child from the old failed runs holding the journal lock.

Keep the group. Fix the wedge + add Linux reap, push, I re-run x2-3. Solid diagnosis handoff — over to you. -doyle</EVENT-PART></event>
</task-notification>


doyle: RED ×2. Group fix WORKED but exposed a separate **deterministic teardown wedge** on kitsubito/Linux: `a_journaled_input_wedge_does_not_starve_a_concurrent_rc_attach` hangs 240s in TEARDOWN (not product assertion) — stuck at `session_pid` (642) or `driver.join()` (646), both unbounded. Keep the group.

3 asks (my domain): (1) harden teardown → fail-fast not 240s [keystone, also re-discriminates real-wedge vs artifact], (2) add Linux pre-checkout reap (ci.yml:211 is Windows-only), (3) check kitsubito for stuck orphan.

Reading the code.


600	                        break;
601	                    }
602	                }
603	                Ok(_) => continue,
604	                Err(_) => continue, // slice timeout — keep waiting until the deadline
605	            }
606	        }
607
608	        // Report the result IMMEDIATELY (the main thread's watchdog can proceed),
609	        // THEN best-effort detach + drain. We deliberately do NOT block the result
610	        // on the serve thread: a controller's `serve_attach` forward can block on a
611	        // non-draining operator socket while the flood child keeps producing — the
612	        // main thread kills the child to unblock it. Reporting first then draining
613	        // keeps the watchdog free of that wedge.
614	        let _ = result_tx.send((subscribed, got_output));
615	        let _ = operator.net_stream_send(stream_b, &[], None, true);
616	        // Keep draining the operator so serve's forwards drain and serve_attach can
617	        // observe our detach + the child's eventual death and return cleanly.
618	        let drain_deadline = std::time::Instant::now() + Duration::from_secs(12);
619	        while std::time::Instant::now() < drain_deadline {
620	            match operator
621	                .read_event_until(Some(std::time::Instant::now() + Duration::from_millis(200)))
622	            {
623	                Ok(BrokerEvent::NetStreamEof { .. }) => break,
624	                Ok(_) => continue,
625	                Err(_) => continue,
626	            }
627	        }
628	        let _ = server.join();
629	    });
630
631	    // Watchdog: a hard wedge surfaces as a recv timeout (a test FAILURE), never a
632	    // hang. The attacher reports its result before any blocking teardown, so this
633	    // recv is the hard ceiling on the whole gate.
634	    let (subscribed, got_output) = result_rx
635	        .recv_timeout(Duration::from_secs(30))
636	        .unwrap_or((false, false));
637
638	    // Tear down so nothing leaks AND so any backpressured helper thread unblocks:
639	    // kill the flood child FIRST (ends the output backing up serve's forwards),
640	    // then signal the driver to stop. The helper threads' own bounded drains let
641	    // them exit; we never block the GATE/assertions on a wedged thread.
642	    if let Some(pid) = _broker.session_pid(sid) {
643	        kill_pid(pid);
644	    }
645	    stop.store(true, std::sync::atomic::Ordering::Relaxed);
646	    let _ = driver.join();
647	    let _ = attacher.join();
648	    let pumped_n = pumped.load(std::sync::atomic::Ordering::Relaxed);
649
650	    // On Unix, fold the W1-missing park-(b) liveness probe HERE (before tearing the
651	    // broker down — the probe needs a live broker). On Windows the journaled write
652	    // is absorbed, so there is no park to assert liveness against.
653	    #[cfg(unix)]
654	    let alive = probe(&name, KIND_NET_STATUS, KIND_NET_STATUS_REPLY, Duration::from_secs(5));
655
656	    // ── PROCESS-CLEAN-EXIT teardown (test-robustness, not the gate). ──
657	    // Each `cold_start_pump` Brain (the driver's `w`, the attacher's `operator`)
658	    // spawns a DETACHED `pump-ipc-reader` thread parked in a BLOCKING named-pipe
659	    // `read_frame` on its RecvHalf. Joining the helper threads drops those Brains,
660	    // which drops each Brain's SendHalf → the broker's per-conn handler read errors
661	    // → the broker closes its end → the client RecvHalf read finally errors → the
662	    // reader thread exits. But that close CHAIN is asynchronous and the reader's
663	    // JoinHandle is only dropped, never joined (it is abandoned by design — KH 7.6).
664	    // On Windows, if the test returns before that chain completes, a reader thread
665	    // is still parked inside a named-pipe read syscall at process teardown, and a
666	    // thread blocked in that syscall on a half-closed handle aborts the WHOLE
667	    // process with exit 0xffffffff (the flaky ~50% abort under `--test-threads=1`,
668	    // where the next test / process exit races the chain). FIX: after the Brains
669	    // are dropped (helpers joined), settle briefly so the close chain completes and
670	    // every abandoned reader observes its read error and exits before the harness
671	    // moves on. Verified by `PUMP_IPC_READER: exited` printing within this window.
672	    // This is purely teardown hygiene; the assertions below are unchanged.
673	    thread::sleep(Duration::from_millis(750));
674
675	    eprintln!(
676	        "=== W1b JOURNAL-WEDGE GATE: journaled_ops_pumped={pumped_n} \
677	         concurrent_attach_subscribed={subscribed} attach_received_pty_output={got_output} \
678	         (fixed = both true; pre-fix on Unix: subscribed=false — attach wedged on the \
679	         held journal lock) ==="
680	    );
681
682	    // [int->REQ-HAZARD-EFFECT-JOURNAL-PTY-WEDGE]
683	    // FACET B — the gate W1 lacked: a concurrent rc attach must be SERVICED even
684	    // while a journaled input effect is in-flight (and, on Unix, blocked holding
685	    // the journal lock). Pre-fix on a forkpty platform this subscribe wedges
686	    // ("brain IPC read deadline elapsed"); post-fix the effect runs off the lock.
687	    assert!(
688	        subscribed,
689	        "a concurrent `spt rc` attach must be SERVICED while a journaled PtyWrite \
690	         is in-flight: the attach's subscribe reply did not arrive within the \
691	         watchdog deadline — the journal lock is held across write_input (facet B, \
692	         REQ-HAZARD-EFFECT-JOURNAL-PTY-WEDGE)."
693	    );
694	    // POSITIVE (W1 lacked): a real rc-client attach RECEIVES PTY output bytes.
695	    assert!(
696	        got_output,
697	        "the concurrent attach must actually RECEIVE PTY output from the child \
698	         (not just liveness): no FLOOD bytes rendered over the real attach pump."
699	    );

                        break;
                    }
                }
                Ok(_) => continue,
                Err(_) => continue, // slice timeout — keep waiting until the deadline
            }
        }

        // Report the result IMMEDIATELY (the main thread's watchdog can proceed),
        // THEN best-effort detach + drain. We deliberately do NOT block the result
        // on the serve thread: a controller's `serve_attach` forward can block on a
        // non-draining operator socket while the flood child keeps producing — the
        // main thread kills the child to unblock it. Reporting first then draining
        // keeps the watchdog free of that wedge.
        let _ = result_tx.send((subscribed, got_output));
        let _ = operator.net_stream_send(stream_b, &[], None, true);
        // Keep draining the operator so serve's forwards drain and serve_attach can
        // observe our detach + the child's eventual death and return cleanly.
        let drain_deadline = std::time::Instant::now() + Duration::from_secs(12);
        while std::time::Instant::now() < drain_deadline {
            match operator
                .read_event_until(Some(std::time::Instant::now() + Duration::from_millis(200)))
            {
                Ok(BrokerEvent::NetStreamEof { .. }) => break,
                Ok(_) => continue,
                Err(_) => continue,
            }
        }
        let _ = server.join();
    });

    // Watchdog: a hard wedge surfaces as a recv timeout (a test FAILURE), never a
    // hang. The attacher reports its result before any blocking teardown, so this
    // recv is the hard ceiling on the whole gate.
    let (subscribed, got_output) = result_rx
        .recv_timeout(Duration::from_secs(30))
        .unwrap_or((false, false));

    // Tear down so nothing leaks AND so any backpressured helper thread unblocks:
    // kill the flood child FIRST (ends the output backing up serve's forwards),
    // then signal the driver to stop. The helper threads' own bounded drains let
    // them exit; we never block the GATE/assertions on a wedged thread.
    if let Some(pid) = _broker.session_pid(sid) {
        kill_pid(pid);
    }
    stop.store(true, std::sync::atomic::Ordering::Relaxed);
    let _ = driver.join();
    let _ = attacher.join();
    let pumped_n = pumped.load(std::sync::atomic::Ordering::Relaxed);

    // On Unix, fold the W1-missing park-(b) liveness probe HERE (before tearing the
    // broker down — the probe needs a live broker). On Windows the journaled write
    // is absorbed, so there is no park to assert liveness against.
    #[cfg(unix)]
    let alive = probe(&name, KIND_NET_STATUS, KIND_NET_STATUS_REPLY, Duration::from_secs(5));

    // ── PROCESS-CLEAN-EXIT teardown (test-robustness, not the gate). ──
    // Each `cold_start_pump` Brain (the driver's `w`, the attacher's `operator`)
    // spawns a DETACHED `pump-ipc-reader` thread parked in a BLOCKING named-pipe
    // `read_frame` on its RecvHalf. Joining the helper threads drops those Brains,
    // which drops each Brain's SendHalf → the broker's per-conn handler read errors
    // → the broker closes its end → the client RecvHalf read finally errors → the
    // reader thread exits. But that close CHAIN is asynchronous and the reader's
    // JoinHandle is only dropped, never joined (it is abandoned by design — KH 7.6).
    // On Windows, if the test returns before that chain completes, a reader thread
    // is still parked inside a named-pipe read syscall at process teardown, and a
    // thread blocked in that syscall on a half-closed handle aborts the WHOLE
    // process with exit 0xffffffff (the flaky ~50% abort under `--test-threads=1`,
    // where the next test / process exit races the chain). FIX: after the Brains
    // are dropped (helpers joined), settle briefly so the close chain completes and
    // every abandoned reader observes its read error and exits before the harness
    // moves on. Verified by `PUMP_IPC_READER: exited` printing within this window.
    // This is purely teardown hygiene; the assertions below are unchanged.
    thread::sleep(Duration::from_millis(750));

    eprintln!(
        "=== W1b JOURNAL-WEDGE GATE: journaled_ops_pumped={pumped_n} \
         concurrent_attach_subscribed={subscribed} attach_received_pty_output={got_output} \
         (fixed = both true; pre-fix on Unix: subscribed=false — attach wedged on the \
         held journal lock) ==="
    );

    // [int->REQ-HAZARD-EFFECT-JOURNAL-PTY-WEDGE]
    // FACET B — the gate W1 lacked: a concurrent rc attach must be SERVICED even
    // while a journaled input effect is in-flight (and, on Unix, blocked holding
    // the journal lock). Pre-fix on a forkpty platform this subscribe wedges
    // ("brain IPC read deadline elapsed"); post-fix the effect runs off the lock.
    assert!(
        subscribed,
        "a concurrent `spt rc` attach must be SERVICED while a journaled PtyWrite \
         is in-flight: the attach's subscribe reply did not arrive within the \
         watchdog deadline — the journal lock is held across write_input (facet B, \
         REQ-HAZARD-EFFECT-JOURNAL-PTY-WEDGE)."
    );
    // POSITIVE (W1 lacked): a real rc-client attach RECEIVES PTY output bytes.
    assert!(
        got_output,
        "the concurrent attach must actually RECEIVE PTY output from the child \
         (not just liveness): no FLOOD bytes rendered over the real attach pump."
    );


185	          }
186
187	  # ---- D7-2 new-brain × old-broker N-1 verb-surface gate (ADR-0018 V6) ----
188	  # Steady state after every routine brain-only update is a NEW brain talking to
189	  # an OLDER broker, so the current brain must serve the socket verbs against a
190	  # prior-era broker without a clap/arity reject or a verb desync (KH-2.3). The
191	  # "old broker" is the restoration split boundary — D1 skeleton 0c95435 (a
192	  # pre-restoration release has no brain supervisor and cannot pair; doyle D7 vet
193	  # call 4a). It is built ONCE into a stable cache keyed by the pin SHA (mirrors
194	  # the traceability job's self-heal) and reused until the pin advances — to the
195	  # latest PUBLISHED split-era release once one exists. The n1_pairing harness is
196	  # env-gated (SPT_N1_OLD_BROKER), so the normal `cargo test` sweep silent-skips
197	  # it; this job sets the env and turns the skip into a real gate on both runners.
198	  n1-gate:
199	    strategy:
200	      fail-fast: false
201	      matrix:
202	        runner: [[self-hosted, Linux, kitsubito], [self-hosted, Windows, hfenduleam]]
203	    runs-on: ${{ matrix.runner }}
204	    # Headroom for the first-ever cold pin build (the multi-exe iroh link);
205	    # cached thereafter, so steady-state runs are a warm test + seconds.
206	    timeout-minutes: 40
207	    steps:
208	      # Pre-checkout exe-lock guard (Windows) — see the `test` job's note: reap a
209	      # cross-run leaked workspace daemon before checkout's clean hits the EPERM
210	      # lock. Scoped to $GITHUB_WORKSPACE, Windows-only, runner-account session-0.
211	      - name: Reap stray workspace daemons (pre-checkout exe-lock guard)
212	        if: runner.os == 'Windows'
213	        shell: pwsh
214	        run: |
215	          Get-Process spt,notify-shell -ErrorAction SilentlyContinue |
216	            Where-Object { $_.Path -like "$env:GITHUB_WORKSPACE\*" } |
217	            Stop-Process -Force -ErrorAction SilentlyContinue
218	          exit 0
219	      - uses: actions/checkout@v5
220	        with:
221	          # The pinned old-broker ref (0c95435) must be reachable to build it.
222	          fetch-depth: 0
223	      # Same Linux mold linker as the `test` job — and, since both Linux jobs run
224	      # on the same runner and share its target dir, matching RUSTFLAGS lets this
225	      # job REUSE the `test` job's warm artifacts instead of a cold full rebuild.
226	      - name: Use mold linker (Linux)
227	        if: runner.os == 'Linux'
228	        run: echo 'RUSTFLAGS=-C link-arg=-fuse-ld=mold' >> "$GITHUB_ENV"
229	      - name: N-1 gate (new-brain × old-broker) — linux
230	        if: runner.os == 'Linux'
231	        shell: bash
232	        run: |
233	          set -euo pipefail
234	          PIN=0c95435
235	          CACHE="$HOME/spt-n1-oldbroker"
236	          BIN="$CACHE/target/debug/spt"
237	          # Self-healing cache: rebuild the pin only when it moves. Clone OUTSIDE
238	          # the workspace (a clone inside gets adopted by the cargo workspace).
239	          if [ ! -x "$BIN" ] || [ "$(cat "$CACHE/.builtpin" 2>/dev/null || true)" != "$PIN" ]; then
240	            rm -rf "$CACHE"
241	            git clone "$GITHUB_WORKSPACE" "$CACHE"
242	            git -C "$CACHE" checkout "$PIN"
243	            cargo build --manifest-path "$CACHE/Cargo.toml" -p spt --bin spt
244	            echo "$PIN" > "$CACHE/.builtpin"
245	          fi
246	          SPT_N1_OLD_BROKER="$BIN" cargo test -p spt --test n1_pairing -- --nocapture
247	      - name: N-1 gate (new-brain × old-broker) — windows
248	        if: runner.os == 'Windows'
249	        # pwsh (PowerShell 7), NOT `powershell` (5.1): the runner's 5.1 execution
250	        # policy refuses the unsigned temp script GitHub generates
251	        # (PSSecurityException) — the same default shell the green `test` job uses.
252	        shell: pwsh
253	        run: |
254	          $ErrorActionPreference = "Stop"

          }

  # ---- D7-2 new-brain × old-broker N-1 verb-surface gate (ADR-0018 V6) ----
  # Steady state after every routine brain-only update is a NEW brain talking to
  # an OLDER broker, so the current brain must serve the socket verbs against a
  # prior-era broker without a clap/arity reject or a verb desync (KH-2.3). The
  # "old broker" is the restoration split boundary — D1 skeleton 0c95435 (a
  # pre-restoration release has no brain supervisor and cannot pair; doyle D7 vet
  # call 4a). It is built ONCE into a stable cache keyed by the pin SHA (mirrors
  # the traceability job's self-heal) and reused until the pin advances — to the
  # latest PUBLISHED split-era release once one exists. The n1_pairing harness is
  # env-gated (SPT_N1_OLD_BROKER), so the normal `cargo test` sweep silent-skips
  # it; this job sets the env and turns the skip into a real gate on both runners.
  n1-gate:
    strategy:
      fail-fast: false
      matrix:
        runner: [[self-hosted, Linux, kitsubito], [self-hosted, Windows, hfenduleam]]
    runs-on: ${{ matrix.runner }}
    # Headroom for the first-ever cold pin build (the multi-exe iroh link);
    # cached thereafter, so steady-state runs are a warm test + seconds.
    timeout-minutes: 40
    steps:
      # Pre-checkout exe-lock guard (Windows) — see the `test` job's note: reap a
      # cross-run leaked workspace daemon before checkout's clean hits the EPERM
      # lock. Scoped to $GITHUB_WORKSPACE, Windows-only, runner-account session-0.
      - name: Reap stray workspace daemons (pre-checkout exe-lock guard)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          Get-Process spt,notify-shell -ErrorAction SilentlyContinue |
            Where-Object { $_.Path -like "$env:GITHUB_WORKSPACE\*" } |
            Stop-Process -Force -ErrorAction SilentlyContinue
          exit 0
      - uses: actions/checkout@v5
        with:
          # The pinned old-broker ref (0c95435) must be reachable to build it.
          fetch-depth: 0
      # Same Linux mold linker as the `test` job — and, since both Linux jobs run
      # on the same runner and share its target dir, matching RUSTFLAGS lets this
      # job REUSE the `test` job's warm artifacts instead of a cold full rebuild.
      - name: Use mold linker (Linux)
        if: runner.os == 'Linux'
        run: echo 'RUSTFLAGS=-C link-arg=-fuse-ld=mold' >> "$GITHUB_ENV"
      - name: N-1 gate (new-brain × old-broker) — linux
        if: runner.os == 'Linux'
        shell: bash
        run: |
          set -euo pipefail
          PIN=0c95435
          CACHE="$HOME/spt-n1-oldbroker"
          BIN="$CACHE/target/debug/spt"
          # Self-healing cache: rebuild the pin only when it moves. Clone OUTSIDE
          # the workspace (a clone inside gets adopted by the cargo workspace).
          if [ ! -x "$BIN" ] || [ "$(cat "$CACHE/.builtpin" 2>/dev/null || true)" != "$PIN" ]; then
            rm -rf "$CACHE"
            git clone "$GITHUB_WORKSPACE" "$CACHE"
            git -C "$CACHE" checkout "$PIN"
            cargo build --manifest-path "$CACHE/Cargo.toml" -p spt --bin spt
            echo "$PIN" > "$CACHE/.builtpin"
          fi
          SPT_N1_OLD_BROKER="$BIN" cargo test -p spt --test n1_pairing -- --nocapture
      - name: N-1 gate (new-brain × old-broker) — windows
        if: runner.os == 'Windows'
        # pwsh (PowerShell 7), NOT `powershell` (5.1): the runner's 5.1 execution
        # policy refuses the unsigned temp script GitHub generates
        # (PSSecurityException) — the same default shell the green `test` job uses.
        shell: pwsh
        run: |
          $ErrorActionPreference = "Stop"


Teardown wedge clear: result captured at line 614 (watchdog-bounded 30s), but teardown 642-647 (`session_pid`, `driver.join()`, `attacher.join()`) is unbounded → rides to 240s. Need join-with-timeout + bounded `session_pid`.

Reading `session_pid` (can it block on broker IPC?) + the driver/helper defs + any existing timeout util.


cd "C:\Users\decid\Documents\projects\spt-core" && grep -rn "fn session_pid" crates/spt-daemon/src/ | head; echo "---DRIVER/ATTACHER/JOIN HELPERS in test---"; grep -nE "let driver|let attacher|let stop|fn join_|join_with_timeout|recv_timeout|JoinHandle|fn kill_pid|thread::spawn" crates/spt-daemon/tests/inject_control_wedge.rs | head -40

Locate session_pid impl + test thread helpers


crates/spt-daemon/src/broker.rs:1202:    pub fn session_pid(&self, id: u64) -> Option<u32> {
---DRIVER/ATTACHER/JOIN HELPERS in test---
76:fn kill_pid(pid: u32) {
124:    thread::spawn(move || {
151:    rx.recv_timeout(deadline).unwrap_or(false)
159:    thread::spawn(move || {
242:    thread::spawn(move || {
273:    thread::spawn(move || while read_frame(&mut a).is_ok() {});
282:    thread::spawn(move || {
317:    let inject_acked = rx.recv_timeout(Duration::from_secs(8)).unwrap_or(false);
410:    thread::spawn(move || {
476:    let stop = Arc::new(AtomicBool::new(false));
478:    let driver_name = name.clone();
479:    let driver_stop = Arc::clone(&stop);
480:    let driver_pumped = Arc::clone(&pumped);
481:    let driver = thread::spawn(move || {
520:    //    recv_timeout watchdog bounds the whole gate — a wedged subscribe (facet B,
525:    let attacher = thread::spawn(move || {
528:        // returns IS the facet-B wedge — and the OUTER `recv_timeout(30s)` watchdog
561:        // never answers it; the outer recv_timeout watchdog then fails the gate.
575:        let server = thread::spawn(move || {
635:        .recv_timeout(Duration::from_secs(30))
663:    // JoinHandle is only dropped, never joined (it is abandoned by design — KH 7.6).
789:    let stop = Instant::now() + deadline;
828:    let stop = Instant::now() + deadline;
864:    thread::spawn(move || {
905:    rx.recv_timeout(deadline).unwrap_or(false)
984:    let stop = Arc::new(AtomicBool::new(false));
986:    let drainer = thread::spawn(move || {
1514:    let stop = Arc::new(AtomicBool::new(false));
1517:    let server = thread::spawn(move || {
1547:    let _origin = ready_rx.recv_timeout(Duration::from_secs(5)).ok();
1719:    let stop = Arc::new(AtomicBool::new(false));
1721:    let driver_name = name.clone();
1722:    let driver_stop = Arc::clone(&stop);
1723:    let driver_pumped = Arc::clone(&pumped);
1724:    let driver = thread::spawn(move || {
1770:    let attacher = thread::spawn(move || {
1812:        let server = thread::spawn(move || {
1853:        .recv_timeout(Duration::from_secs(30))


1196	    pub fn session_count(&self) -> usize {
1197	        self.sessions.lock().unwrap().len()
1198	    }
1199
1200	    /// The child pid of a hosted session, if present (tests assert it is stable
1201	    /// across brain restarts).
1202	    pub fn session_pid(&self, id: u64) -> Option<u32> {
1203	        self.sessions
1204	            .lock()
1205	            .unwrap()
1206	            .get(&id)
1207	            .and_then(|h| h.session.process_id())
1208	    }
1209
1210	    /// The count of live QUIC connections the broker-owned net endpoint holds
1211	    /// (D4a, ADR-0004 §B), or 0 when net-less. A direct read of the broker's own
1212	    /// state — the survival-assertion seam for the D7-1 process-level E2E: a QUIC
1213	    /// connection the broker holds must outlive a brain-PROCESS swap (restoration
1214	    /// D7-1), the net sibling of [`session_count`](Self::session_count).
1215	    // [impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]
1216	    pub fn net_conn_count(&self) -> usize {
1217	        self.net.get().map(|h| h.conn_count() as usize).unwrap_or(0)

    pub fn session_count(&self) -> usize {
        self.sessions.lock().unwrap().len()
    }

    /// The child pid of a hosted session, if present (tests assert it is stable
    /// across brain restarts).
    pub fn session_pid(&self, id: u64) -> Option<u32> {
        self.sessions
            .lock()
            .unwrap()
            .get(&id)
            .and_then(|h| h.session.process_id())
    }

    /// The count of live QUIC connections the broker-owned net endpoint holds
    /// (D4a, ADR-0004 §B), or 0 when net-less. A direct read of the broker's own
    /// state — the survival-assertion seam for the D7-1 process-level E2E: a QUIC
    /// connection the broker holds must outlive a brain-PROCESS swap (restoration
    /// D7-1), the net sibling of [`session_count`](Self::session_count).
    // [impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]
    pub fn net_conn_count(&self) -> usize {
        self.net.get().map(|h| h.conn_count() as usize).unwrap_or(0)


476	    let stop = Arc::new(AtomicBool::new(false));
477	    let pumped = Arc::new(AtomicU64::new(0));
478	    let driver_name = name.clone();
479	    let driver_stop = Arc::clone(&stop);
480	    let driver_pumped = Arc::clone(&pumped);
481	    let driver = thread::spawn(move || {
482	        // PUMP carrier (cold_start_pump): the reader thread drains the driver's
483	        // OWN incoming frames (KIND_APPLIED acks + session output) so the broker's
484	        // reply side never backs up and blocks reading our input — which would
485	        // deadlock send_effect on a plain Whole conn (observed: it stalled after a
486	        // few ops). The pump keeps the driver's socket healthy so it can keep a
487	        // journaled write in-flight without ever wedging the CLIENT.
488	        let mut w = match Brain::cold_start_pump(&driver_name, 1, Duration::from_secs(2)) {
489	            Ok(b) => b,
490	            Err(_) => return,
491	        };
492	        // Bind the driver brain to the same session so send_effect has a target.
493	        if w.attach(sid, 0).is_err() {
494	            return;
495	        }
496	        let chunk = vec![b'W'; 16 * 1024];
497	        let mut op = 1u64;
498	        // Sustain journaled input: keep ops in flight (and, on Unix, parked under
499	        // the journal lock) until teardown. Each send_effect writes a frame; the
500	        // pump reader drains replies concurrently so the write never deadlocks.
501	        while !driver_stop.load(std::sync::atomic::Ordering::Relaxed) {
502	            if w.send_effect(op, &chunk).is_err() {
503	                break;
504	            }
505	            driver_pumped.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
506	            op += 1;
507	            // Drain any queued replies (acks/output) so the carrier stays healthy,
508	            // then a brief pace so we don't spin a CPU.
509	            let _ = w.read_event_until(Some(std::time::Instant::now() + Duration::from_millis(20)));
510	        }
511	    });
512
513	    // Give the driver time to get a journaled write in-flight (and, on Unix, to
514	    // park write_input under the journal lock).
515	    thread::sleep(Duration::from_secs(2));
516
517	    // ── The CONCURRENT `spt rc` attach: a SEPARATE operator connection requests a
518	    //    real attach over a loopback conn, subscribes, and (via serve_attach)
519	    //    receives the child's output. Run on its own thread so the OUTER
520	    //    recv_timeout watchdog bounds the whole gate — a wedged subscribe (facet B,
521	    //    the "brain IPC read deadline elapsed" symptom) surfaces as a clean test
522	    //    failure, never a hang. ──
523	    let (result_tx, result_rx) = std::sync::mpsc::channel::<(bool, bool)>();
524	    let attach_name = name.clone();
525	    let attacher = thread::spawn(move || {
526	        // The operator is a plain Whole conn (the proven attach-test carrier): its
527	        // request/subscribe are request-reply, so a blocking read that NEVER
528	        // returns IS the facet-B wedge — and the OUTER `recv_timeout(30s)` watchdog
529	        // converts that into a clean test FAILURE (subscribed never reported),
530	        // never a hang. No fragile pump-poison semantics on the servicing path.
531	        let mut operator = match Brain::cold_start(&attach_name, 1) {
532	            Ok(b) => b,
533	            Err(_) => {
534	                let _ = result_tx.send((false, false));
535	                return;

    let stop = Arc::new(AtomicBool::new(false));
    let pumped = Arc::new(AtomicU64::new(0));
    let driver_name = name.clone();
    let driver_stop = Arc::clone(&stop);
    let driver_pumped = Arc::clone(&pumped);
    let driver = thread::spawn(move || {
        // PUMP carrier (cold_start_pump): the reader thread drains the driver's
        // OWN incoming frames (KIND_APPLIED acks + session output) so the broker's
        // reply side never backs up and blocks reading our input — which would
        // deadlock send_effect on a plain Whole conn (observed: it stalled after a
        // few ops). The pump keeps the driver's socket healthy so it can keep a
        // journaled write in-flight without ever wedging the CLIENT.
        let mut w = match Brain::cold_start_pump(&driver_name, 1, Duration::from_secs(2)) {
            Ok(b) => b,
            Err(_) => return,
        };
        // Bind the driver brain to the same session so send_effect has a target.
        if w.attach(sid, 0).is_err() {
            return;
        }
        let chunk = vec![b'W'; 16 * 1024];
        let mut op = 1u64;
        // Sustain journaled input: keep ops in flight (and, on Unix, parked under
        // the journal lock) until teardown. Each send_effect writes a frame; the
        // pump reader drains replies concurrently so the write never deadlocks.
        while !driver_stop.load(std::sync::atomic::Ordering::Relaxed) {
            if w.send_effect(op, &chunk).is_err() {
                break;
            }
            driver_pumped.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
            op += 1;
            // Drain any queued replies (acks/output) so the carrier stays healthy,
            // then a brief pace so we don't spin a CPU.
            let _ = w.read_event_until(Some(std::time::Instant::now() + Duration::from_millis(20)));
        }
    });

    // Give the driver time to get a journaled write in-flight (and, on Unix, to
    // park write_input under the journal lock).
    thread::sleep(Duration::from_secs(2));

    // ── The CONCURRENT `spt rc` attach: a SEPARATE operator connection requests a
    //    real attach over a loopback conn, subscribes, and (via serve_attach)
    //    receives the child's output. Run on its own thread so the OUTER
    //    recv_timeout watchdog bounds the whole gate — a wedged subscribe (facet B,
    //    the "brain IPC read deadline elapsed" symptom) surfaces as a clean test
    //    failure, never a hang. ──
    let (result_tx, result_rx) = std::sync::mpsc::channel::<(bool, bool)>();
    let attach_name = name.clone();
    let attacher = thread::spawn(move || {
        // The operator is a plain Whole conn (the proven attach-test carrier): its
        // request/subscribe are request-reply, so a blocking read that NEVER
        // returns IS the facet-B wedge — and the OUTER `recv_timeout(30s)` watchdog
        // converts that into a clean test FAILURE (subscribed never reported),
        // never a hang. No fragile pump-poison semantics on the servicing path.
        let mut operator = match Brain::cold_start(&attach_name, 1) {
            Ok(b) => b,
            Err(_) => {
                let _ = result_tx.send((false, false));
                return;


`session_pid` = pure Mutex read (no IPC) — hangs only if broker's per-conn handler holds `sessions` lock while wedged. The real 240s-rider is `driver.join()`: driver parks in `send_effect` on the wedged broker, never sees `stop`, join blocks. Both need bounding.

For a detached bounded probe of `session_pid` I need `_broker`'s type (Arc-cloneable?). Checking.


cd "C:\Users\decid\Documents\projects\spt-core" && grep -nE "_broker|let \(.*broker|Arc<Broker>|harness|fn .*-> .*Broker|sid =" crates/spt-daemon/tests/inject_control_wedge.rs | sed -n '1,25p'

Find _broker binding + type in test