---
name: w6-f039-blackhole-lifecycle-harness
description: REQ-CONN-BLACKHOLE-LIFECYCLE-HARNESS five-invariant rig — conn_handler_count floor gotcha when generalizing the r4 gate with a persistent unrelated viewer
metadata:
  type: project
---

MSG-IDENTITY W6 / F-039 leg (e): `crates/spt-daemon/tests/conn_blackhole_lifecycle.rs::blackholed_controller_lifecycle_five_invariants` (int, `#![cfg(windows)]` whole file). Standing conformance rig for hertz's RCA fix-shape item 5 (`.claude/hertz-pty-freeze-rca.md` L120-143). Generalizes `brain_decouple.rs`'s r4 gate `non_draining_controller_stall_evict_releases_writer_and_connection`, reusing its SEED/trigger/burst PowerShell choreography + hand-back-unread-Stream pattern.

**KEY GOTCHA (cost one RED iteration):** the r4 gate asserts invariant "writer exits" via `broker.conn_handler_count() == 0` because its ONLY persistent conn was the black-holed R. This harness adds a SECOND unrelated session with a PERSISTENT draining viewer (for invariant 1 "unrelated sessions continue") — that viewer's handler holds `conn_handler_count` at a floor of 1 forever, so `== 0` can NEVER pass. Fix: assert `<= 1`. `conn_handlers` is a GLOBAL atomic (broker.rs:2877, one inc per physical `handle_conn`); while BOTH R's handler and the unrelated viewer's handler are alive it is atomically `>= 2`, so a `<= 1` sample proves R's handler exited — airtight because invariant 1 independently proves the unrelated viewer stayed alive across the window.

**Why:** future edits to this test class (or new tests that keep any persistent draining conn while watching a specific conn retire) must NOT copy the r4 `== 0` seam blindly.

**How to apply:** when watching `conn_handler_count` fall to prove ONE conn's handler exited, the target floor = count of OTHER persistent healthy handlers still attached, not 0. Corroborate their liveness with a separate assertion.

Platform: Windows-only is correct for this REQ — invariants 2/3/5-proxy are physical-retirement asserts and the deterministic black-hole only reproduces on the Windows named-pipe no-write-timeout path. A Unix companion would be a different (buffer-fill `shutdown`) mechanism = not faked. Mirrors the r4 gate's own `#[cfg(windows)]` leg. HEAVY class (real broker + 2 PTY children): registered in all three sync'd filter sites — `.config/nextest.toml` override + both `-E` lines in `.github/workflows/ci.yml` (alphabetically after `budget`). Runs ~1s, deterministic 3/3.

Related: [[msg-identity-w4-delivery]], invariants consume REQ-CONN-POISON-ATTRIBUTION's conn-id lifecycle records.
