{
  "summary": "High-confidence release-regression assessment: v0.36's REDISPATCH subscriber isolation converts a pre-existing logical subscription-lifetime leak into one parked OS thread per completed pull. `SubscriberSeat::install` spawns one writer thread per `net-stream-subscribe`; after replay/live EOF that thread does not terminate—it waits on `rx.recv()` until its owning `StreamLog` drops the seat. The long-lived peer-pump `Brain` reuses one broker IPC carrier across every sync/update pull, subscribes each newly opened response stream, and never unsubscribes. Broker-side `handle_conn` retains every subscribed stream ID until the entire pump carrier reaches EOF; `Brain.net_cursors` likewise never removes completed IDs. Healthy QUIC connections retain the corresponding `StreamEntry` rows until the entire peer connection closes. This violates intended O(active) behavior in four coupled dimensions: broker subscriber-writer threads, broker handler subscription IDs, brain cursor entries, and broker stream rows all grow O(completed pulls). Approximately 532 broker threads with only four CPU-active is the exact expected shape: hundreds of unnamed seat writers parked in `std::sync::mpsc::Receiver::recv`, while only the dispatcher/pump/net runtime threads work. It is not explained by current dispatcher worker concurrency (`MAX_DISPATCH_WORKERS = 8`), NetHost Tokio workers (fixed at 2), or 4–5 hosted PTYs.\n\nA second, independent O(time) defect amplifies CPU/I/O: the pump opens and FINs a fresh locally initiated Registry feed stream per peer every registry round, but locally initiated rows are never dispatched or retired. The dispatcher nevertheless asks the broker for the complete non-retired stream table every 100 ms; only after transferring and deserializing the full reply does it skip `initiated_locally` rows. Thus a healthy long-lived peer connection makes `net-streams` replies grow linearly with runtime and the fresh post-refresh dispatcher rereads the entire historical local table at 10 Hz. Incoming Served rows are hidden by v0.36 retirement, but retirement intentionally does not physically remove their `StreamEntry`; physical stream memory remains O(time) until peer-connection close.\n\nReplay behavior is bounded in concurrency but not always bounded in lifetime. Ordinary transient failures have three total attempts with 500 ms/1 s retry backoff and then become terminal. `SeatBlocked` failures intentionally do not consume that budget; they requeue indefinitely behind a global 2/4/8/16/30-second circuit breaker. Any successful sibling serve resets the breaker to 2 seconds, so a poisoned row mixed with healthy traffic can replay forever at roughly two-second intervals. This is a paced replay loop, not a CPU-tight spin. Cold-backlog connection churn is also possible: a worker can use a temporary opener-query connection, a serve connection, and a success-retirement connection. Although admission is eight workers per 100-ms poll (up to 80 newly admitted workers/s), `active_workers` is decremented before retirement/telemetry/claim completion, so slow completion tails are outside the advertised pool bound and can transiently accumulate broker handler/watchdog pairs.\n\nThe observed brain read rate has a separate, exact source and should not be attributed to stream replay: every 500-ms brain heartbeat calls `write_ready`, which reads and SHA-256 hashes the entire current executable. Predicted read rate is `2 × executable_size` per second; 63.91 MiB/s implies a 31.955-MiB executable. Registry session-ledger scans occur only on registry rounds and each ledger is capped at 64 rows, so they do not fit the continuous 63.91-MiB/s signature.\n\nTargeted regression seam: keep one real pump Brain carrier and one real QUIC peer connection alive, run many fast non-empty sync pulls plus Registry pushes through production pump/dispatcher code, and never disconnect between rounds. Add a broker/NetHost lifecycle-stats seam reporting physical rows, eligible rows split by direction, installed subscriber seats/writers, and per-handler subscribed-ID count; add a Brain cursor-count seam. After warm-up and N completed rounds, assert all counts plateau at O(peers + in-flight exchanges), broker thread count remains at baseline + O(active), locally initiated eligible rows do not grow, and a serialized `net-streams` reply remains constant-sized. Then perform the production brain refresh boundary: assert the old pump carrier's seats/cursors/handler IDs fall to zero before the new generation runs and that the new generation again plateaus rather than restarting linear growth. The essential discriminator is that the pump carrier must remain open during the first assertion—closing it would invoke existing EOF cleanup and hide the bug. Existing REDISPATCH tests cover historical Attach retirement, rolled opener reconstruction, poisoned replay, retry classification, and one-poll worker admission; none exercises repeated completed requester subscriptions on one live carrier or asserts lifetime plateaus.\n\nFalsifiable live probes:\n1. Decompose broker threads by stack/name. The subscription leak predicts about the excess count blocked in `mpsc::Receiver::recv` from `SubscriberSeat::install`, only two `spt-broker-net` threads, and relatively few `conn-watchdog`/`handle_conn` threads. If instead approximately half the excess are `conn-watchdog` and half are `handle_conn/read_frame`, physical IPC connections—not seats—are retained.\n2. Compare `Broker::conn_handler_count()` with total broker OS threads. Low handler count plus ~500 excess parked threads confirms seats; ~250 handlers plus ~250 watchdogs falsifies it and identifies connection retention.\n3. Instrument/count `net-stream-subscribe` calls on the long-lived pump carrier and active seat writers. Predicted slope is exactly one broker writer thread and one Brain cursor entry per completed subscribed pull. The slope tracks non-empty sync/update pulls, not hosted-session count directly.\n4. Trigger a brain refresh and sample at sub-second cadence. Existing EOF cleanup predicts a sharp collapse of the old pump carrier's seat writers when `handle_conn` iterates `my_stream_subs`; failure to collapse means old-carrier EOF cleanup is stuck or the old brain remains alive. Stream rows may remain because peer QUIC connections are broker-owned and survive refresh.\n5. Sample `net-streams` twice Δt apart and split rows by `initiated_locally`, `finished`, and family/opener. Registry-only local-row growth should be approximately `live_peers × Δt / 30s`; notification feeds add their own 15-s cadence. Measure serialized reply bytes B: with breaker closed, dispatcher traffic predicts about `10 × B` broker-to-brain bytes/s. A 30-s breaker window should suppress this 10-Hz component; no I/O fall during that window falsifies enumeration as the measured broker-write source.\n6. Group `DISPATCH_EV` by stream ID. Transient failures must stop after attempts 0,1,2; SeatBlocked repeats preserve the attempt number and follow breaker windows. Repeats substantially faster than the breaker window would be a true spin and contradict current source semantics.\n7. For the brain read counter, measure executable size S and a 20-s `ReadFile` delta. Prediction is approximately `40 × S`, with ProcMon reads targeting the running executable and `brain.ready` updating at 500-ms cadence. If paths or rate disagree, executable hashing is falsified.",
  "files": [
    {
      "path": "crates/spt-daemon/src/nethost.rs",
      "description": "Lines 154–223 implement `SubscriberSeat`: every install spawns an OS writer thread; after replay it waits on `rx.recv()` until the seat's sender is dropped. Lines 430–607 show EOF only enqueues a final frame and `begin_attach` stores the seat in `StreamLog`; no terminal-frame auto-detach exists. Lines 949–970 remove all stream rows only when the whole QUIC connection closes. Lines 1000–1068 register each stream/read pump. Lines 1804–1853 enumerate every non-retired row and implement retirement as visibility plus ring clear, explicitly retaining the physical entry/subscriber. Lines 1967–1990 expose detach only as brain-disconnect cleanup."
    },
    {
      "path": "crates/spt-daemon/src/broker.rs",
      "description": "Lines 3025–3290 implement per-IPC-connection handling. Every successful `KIND_NET_STREAM_SUBSCRIBE` appends the stream ID to `my_stream_subs`; those IDs are detached only after the read loop ends because the Brain carrier disconnected. Lines 2921–2924 expose `conn_handler_count`, a decisive live/test discriminator between seat threads and retained IPC handler/watchdog pairs."
    },
    {
      "path": "crates/spt-daemon/src/brain.rs",
      "description": "Lines 271–276 define the per-stream `net_cursors` HashMap. Lines 813–830 advance entries while reading stream data. Lines 1590–1605 insert cursors on subscribe and provide no completion removal/unsubscribe operation. Pump-mode construction additionally owns one reader thread, but that is one per pump carrier, not one per stream."
    },
    {
      "path": "crates/spt-daemon/src/pump/mod.rs",
      "description": "Lines 519–593 construct two long-lived pump-mode Brain carriers and reuse the main one across all peer steps. Lines 1109–1123 open+FIN fresh fire-and-forget feed streams. Lines 1126–1142 show pump connections use `Brain::cold_start_pump`. The scheduler marks completed rounds and sleeps when idle, ruling out a cadence tight spin."
    },
    {
      "path": "crates/spt-daemon/src/pump/registry.rs",
      "description": "Lines 158–176 encode one Registry feed per peer/subnet and call `push_feed`; combined with the 30-s default this continually creates locally initiated, finished, non-retired stream rows."
    },
    {
      "path": "crates/spt-daemon/src/config.rs",
      "description": "Lines 39–42 define default cadences: Registry 30 s, Notif 15 s, Sync 60 s, Update 3600 s. These values make thread/row-growth slopes quantitatively falsifiable."
    },
    {
      "path": "crates/spt-daemon/src/sync.rs",
      "description": "Lines 335–346 open and subscribe every requester-side sync stream on the reused pump Brain. Lines 430–487 return immediately on the protocol `Done` record, before stream EOF cleanup, without unsubscribing, retiring, or removing the Brain cursor. This is the dominant periodic source of permanent seats on the live pump carrier."
    },
    {
      "path": "crates/spt-daemon/src/propagate.rs",
      "description": "Lines 355–356 and 557–558 repeat the same open+subscribe-without-unsubscribe pattern for update pulls and status queries. Lower cadence makes this a secondary contributor but the same invariant violation."
    },
    {
      "path": "crates/spt-daemon/src/dispatch.rs",
      "description": "Line 87 sets the 100-ms poll. Lines 179–341 define three-attempt transient retry versus unbounded-but-breaker-paced SeatBlocked retry. Lines 429–563 fetch the whole `net-streams` table before filtering local rows, admit at most eight active workers, decrement the active gauge before completion-tail retirement, and retire Served incoming rows. Lines 639–1048 show each worker's opener query/fresh serve connection and Registry serve loop."
    },
    {
      "path": "crates/spt-daemon/src/conn.rs",
      "description": "Lines 389–453 and 638–661 implement one named watchdog per physical broker IPC connection and join it on `BrokerConn` drop. `CONN_LIFECYCLE` write-start/transport-close deltas and thread names distinguish connection churn from the unnamed SubscriberSeat writer leak."
    },
    {
      "path": "crates/spt-daemon/src/brainproc.rs",
      "description": "Lines 62–63 set a 500-ms heartbeat; lines 296–297 call `write_ready` every heartbeat; lines 365–384 read and hash the entire current executable on every write. This predicts the observed ~63.91-MiB/s brain reads independently of REDISPATCH."
    },
    {
      "path": "crates/spt-store/src/sessions.rs",
      "description": "Lines 30–31 cap each session ledger at 64 rows, ruling out unbounded session-ledger scans as the continuous brain-read explanation."
    },
    {
      "path": "crates/spt-daemon/tests/redispatch.rs",
      "description": "Existing production-path coverage for finished historical Attach retirement, active Attach survival, durable opener reconstruction after ring roll, and bounded transient retry. It does not keep one requester/pump Brain alive across many completed subscriptions."
    },
    {
      "path": "crates/spt-daemon/tests/redispatch_stall.rs",
      "description": "Existing T1–T7 coverage for poisoned replay, producer non-blocking behavior, lease cancellation/recovery, and mixed-image fallback. It tests incident mechanics but not long-lived pump subscription/stream-count plateaus."
    },
    {
      "path": "crates/spt-daemon/tests/mesh_recovery.rs",
      "description": "Best existing integration harness for the proposed regression: it already runs real brokers, real QUIC, production dispatcher, and production pump with accelerated cadences. Extend it with repeated non-empty sync/Registry rounds and lifecycle gauges while deliberately keeping the pump carrier connected."
    }
  ],
  "architecture": "Causal graph:\n\n`run_peer_pump` keeps one Brain IPC carrier alive → each periodic non-empty `request_sync`/update request opens a fresh QUIC stream and calls `net_stream_subscribe` → Brain inserts a permanent cursor; broker handler appends a permanent `my_stream_subs` ID; `StreamLog::begin_attach` installs a `SubscriberSeat` and spawns one writer thread → protocol `Done`/EOF completes the logical exchange but no unsubscribe removes cursor/ID/seat → writer parks forever on the empty channel while healthy pump IPC and peer QUIC connections remain alive → broker thread count grows O(completed pulls), almost all sleeping.\n\nIn parallel, every fire-and-forget Registry/Notif push opens and FINs a local stream → no dispatcher claims local rows and no requester retires them → broker stream table grows O(rounds × peers) → dispatcher refresh reconstructs an empty claim map and polls `net-streams` every 100 ms → broker clones/serializes and brain reads/deserializes every non-retired local historical row before filtering → sustained small-core CPU and broker IPC writes with little daemon-log growth.\n\nIncoming historical rows follow a different path: fresh dispatcher workers classify from the pinned opener, subscribe/replay, serve, then mark Served and issue best-effort retirement. Retirement hides the row but deliberately keeps physical state until connection close. Ordinary errors terminate after three attempts; subscriber-seat errors are retry-forever but globally paced. The worker pool bounds serve bodies, not all completion tails, so a cold refresh can create short-lived IPC churn even though it cannot explain hundreds of permanently parked threads by itself.\n\nA brain refresh is currently the only reclamation boundary for pump-carrier seats/cursors/handler IDs, because carrier EOF runs broker detach cleanup. It does not reclaim broker-owned stream rows riding healthy peer QUIC connections, and the replacement brain immediately primes every pump leg and reconstructs dispatcher polling. Therefore refresh can temporarily drop thread count yet preserve/re-expose the table-size cost and begin the same linear thread accumulation again."
}