{
  "summary": "## Ranked findings\n\n### 1 — Confirmed dominant brain read/CPU mechanism: the readiness heartbeat re-reads and hashes the entire running `spt.exe` every 500 ms\n\nThis is the only source mechanism that quantitatively explains the observed sustained **63.91 MiB/s brain reads**.\n\n* `crates/spt-daemon/src/brainproc.rs:63` sets `BRAIN_HEARTBEAT` to **500 ms**.\n* `crates/spt-daemon/src/brainproc.rs:259-297` sleeps one heartbeat, performs `brain.net_status()`, performs `brain.sessions()`, and then calls `write_ready(generation)` on every iteration.\n* `crates/spt-daemon/src/brainproc.rs:365-373` has `write_ready` call `current_exe_hash()` before atomically rewriting `brain.ready`.\n* `crates/spt-daemon/src/brainproc.rs:381-384` implements `current_exe_hash()` as `std::env::current_exe()` followed by `std::fs::read(exe)` of the **entire running executable into a fresh `Vec<u8>`**, followed by SHA-256 over all bytes.\n* Therefore steady-state brain file-read throughput is\n  `2 heartbeats/s × deployed_spt_exe_size`.\n  The observed 63.91 MiB/s predicts a deployed executable size of **31.955 MiB**. That is an unusually exact fit, not merely an order-of-magnitude match.\n* The same path allocates a fresh executable-sized buffer and hashes it twice per second. That explains sustained one-core CPU as well as process I/O read bytes. Windows process Read Bytes includes cached `ReadFile` traffic, so this need not imply 64 MiB/s of physical disk media traffic.\n* The source comment at `brainproc.rs:376-380` says “One exe read + hash per brain start,” but the call at `brainproc.rs:297` makes it one read + hash **per heartbeat**. The initial ready call at `brainproc.rs:221` is indeed once at startup; the later heartbeat call is the regression.\n* `write_ready` also atomically rewrites the small `brain.ready` JSON every 500 ms. `crates/spt-store/src/atomic.rs:57-79` shows that this is a tmp-file write plus rename. It can explain a high operation count and visible metadata/write churn, but not tens of MiB/s by itself.\n\n**Falsifiable live probes**\n\n1. Compute the exact prediction without changing process state:\n   ```powershell\n   $p = Get-Process -Id 58272\n   $bytes = (Get-Item $p.Path).Length\n   [pscustomobject]@{\n     Path = $p.Path\n     Bytes = $bytes\n     MiB = $bytes / 1MB\n     PredictedReadMiBPerSec = 2 * $bytes / 1MB\n   }\n   ```\n   Confirmation is `PredictedReadMiBPerSec` approximately **63.91**. A materially different executable size falsifies this as the complete byte explanation.\n2. In Sysinternals Process Monitor, filter `PID is 58272`, `Operation is ReadFile`, and `Path is <the value of $p.Path>`. Over 10 seconds, expect about **20 full sequential reads**, totaling about `20 × executable length`, spaced near 500 ms. Filter out this one path and recompute brain read bytes; the sustained ~64 MiB/s should disappear.\n3. Add a Process Monitor filter for `brain.ready`. Expect tmp create/write/rename publication at approximately 2 Hz, synchronized with the executable reads. The ready JSON’s `exe_hash` should remain constant while its last-write timestamp advances.\n4. Capture sampled CPU stacks in WPA/Windows Performance Recorder or Process Explorer’s active-thread stack view. The continuously active brain thread should spend samples under the executable read and SHA-256 path. The falsifier is sustained 15.5% CPU entirely on other thread stacks after excluding this path.\n\n### 2 — Plausible secondary, session-count-scaled activity: one 5-second pulse thread per hosted live endpoint; transcript reads are gated, not unconditional\n\nThis explains why a five-second activity census can see roughly one active brain thread per hosted session, but it does **not** fit a flat 63.91 MiB/s nearly as well as mechanism 1.\n\n* `crates/spt-daemon/src/livehost.rs:637-639` starts one `run_pulse_loop` thread per hosted live lifecycle.\n* `crates/spt-daemon/src/config.rs:19-25` gives the default pulse period as **5 seconds**, configurable via `daemon.json`.\n* `crates/spt-daemon/src/lifecycle.rs:701-738` runs each endpoint on that configured deadline grid. Each due tick checks the manifest and calls `pulse_tick`.\n* `crates/spt-daemon/src/lifecycle.rs:465-490` reads and hashes the endpoint adapter’s small `manifest.toml` to detect live updates.\n* `crates/spt-daemon/src/lifecycle.rs:585-666` reads resting/info state, stamps active recency, enumerates configured commune/signoff drop directories, and consumes an echo gate.\n* Crucially, history/transcript access is **not performed on every pulse**. `lifecycle.rs:647-666` calls `fire_echo` only when `take_echo_gate(...)` succeeds. `lifecycle.rs:843-860` fetches history only inside that echo path.\n* When it does fire, `crates/spt-live/src/history.rs:103-129` can read an entire adapter-located raw transcript (`std::fs::read`) or entire perch `history.log` (`read_to_string`), then normalize it. Thus large transcript files can produce bursty large reads, but only on armed echo events, not on quiet five-second ticks.\n\n**Falsifiable live probes**\n\n1. In Process Monitor, exclude the running `spt.exe` path, then group remaining `ReadFile` bytes by path. If pulse history is material, large reads will name an adapter raw transcript or `<perch>\\history.log`, recur on the configured pulse grid, and correlate with echo-gate consumption. If those paths are absent or tiny, pulse threads explain the “five active threads” census but not the byte rate.\n2. Compare `daemon.json`’s `pulse_period_ms` with timestamps of reads to perches’ `info.json`, `resting.json`/rest records, adapter `manifest.toml`, and drop directories. Quiet-session metadata should recur around that period; raw transcript reads should occur only when an echo gate was armed.\n3. Inspect active-thread stacks: normal quiet pulse threads should appear briefly in `pulse_tick`, info/rest reads, directory enumeration, and sleeps. A thread continuously reading one transcript rather than waking once per grid point would falsify normal pulse behavior.\n\n### 3 — Project index: sub-second polling exists, but it is one cheap directory query; repository/session scans are event-driven or every five minutes\n\n* `crates/spt-daemon/src/projwriter.rs:44-53` defines a **500 ms** invalidation poll, **250 ms** debounce, and **300,000 ms / 5 minute** periodic reconcile.\n* `projwriter.rs:622-683` runs exactly one writer thread. On ordinary idle ticks it calls only `projinval::pending_at(...)` and continues.\n* `crates/spt-store/src/projinval.rs:90-95` proves the idle poll is one `read_dir` of `$SPT_HOME/index/invalidations`, short-circuiting after the first entry. It does not read repositories, transcripts, logs, or the project index on every 500 ms tick.\n* When invalidations exist, `projinval.rs:104-119` drains each tiny invalidation JSON file and deletes it, coalescing the batch.\n* At boot, on an invalidation, or at the five-minute backstop, `projwriter.rs:294-348` invokes one branch-tip enumeration of the context store’s bare seed Git repository and scans a `p-*` tree only when that branch tip changed.\n* `projwriter.rs:350-450` enumerates perches and reads each targeted `<perch>/sessions.log`; it derives project identity from recorded cwd values. It is not scanning the project worktree’s contents.\n* `crates/spt-store/src/sessions.rs:28-32,98-107` bounds every `sessions.log` at **64 rows**, so this input cannot grow without bound.\n* The unchanged-fingerprint no-work gate at `projwriter.rs:307-320` exits before tree/perch composition when there is no changed generation and no scoped work.\n\n**Falsifiable live probes**\n\n1. Process Monitor should show `QueryDirectory`/directory metadata traffic against `$SPT_HOME\\index\\invalidations` at about 2 Hz, with negligible bytes. It should not show repository worktree or transcript reads on that cadence.\n2. Watch `$SPT_HOME\\index\\project-index-stats.json`. `last_run_ms` and the `last_cycle` counters should change only after an invalidation/boot reconcile or about every five minutes—not every 500 ms. A continuously advancing `last_run_ms`, nonzero tree scans every tick, or an invalidation file that never drains would identify a separate malfunction.\n3. During a periodic full reconcile, expect one seed-repo branch enumeration, at most one tree scan per changed `p-*` branch, and bounded `sessions.log` reads. Reads of arbitrary repository files or raw transcripts during this phase would falsify the source contract and identify an untraced caller.\n\n### 4 — Dispatcher/registry streams: 100 ms IPC polling and 15-second poison windows can explain thread/telemetry symptoms, but not sustained brain file reads\n\n* `crates/spt-daemon/src/dispatch.rs:47-49` sets the dispatcher’s stream-table poll to **100 ms**.\n* `dispatch.rs:429-588` issues `brain.net_streams()` each tick, skips local/empty rows, claims at most eight workers, and keeps in-flight/retry/terminal state in a per-generation map. The idle path is broker IPC plus sleep, with no filesystem scan.\n* `dispatch.rs:1197-1207` starts exactly one dispatcher loop.\n* `dispatch.rs:970-1048` shows the Registry arm: load the small subnet/roster policy once per feed, subscribe from sequence zero, block on `brain.read_event`, decode rows, apply them, and return on EOF. It is event-driven, not a polling reader.\n* `crates/spt-daemon/src/registryhost.rs:69-80` loads subnet and roster stores once per inbound registry feed.\n* `registryhost.rs:241-344` merges rows under the in-memory registry lock, drops the lock, stamps heard state, and writes snapshots. There is no lock self-deadlock in these apply paths.\n* `registryhost.rs:738-765` serializes current in-memory registries to `$SPT_HOME/identity/registry/<subnet>.json` plus `heard.meta` after an admitted feed. This is the main registry-feed disk I/O. It is proportional to registry snapshot size and feed frequency, not transcript/repository size.\n* `crates/spt-daemon/src/nethost.rs:119-218` gives every installed stream subscriber a bounded queue and its own writer thread. Replay is written first, then live frames; the thread halts on its first `SharedSend::write` failure and marks the seat poisoned.\n* `crates/spt-daemon/src/broker.rs:167-188` fixes the production brain write deadline at **15 seconds**. Therefore a poison appearing almost exactly 15 seconds after subscriber attach is a blocked broker→brain connection write, not a registry scan cadence.\n* `nethost.rs:505-572` refuses a replacement only while a poisoned prior subscriber writer is still draining; every install gets a new lease. `dispatch.rs:209-333` classifies subscriber-busy/replay-halted/lease-canceled failures as seat-blocked and applies a global **2, 4, 8, 16, then 30 second capped** breaker, without burning the transient attempt budget.\n* On successful service, `dispatch.rs:492-510` calls `net_stream_retire`; `crates/spt-daemon/src/nethost.rs:1835-1840` removes the row from future stream-table enumeration. Thus the normal lifecycle is unique feed stream → subscribe → data → EOF → Served → retire. A single served stream should not be replayed indefinitely in one generation.\n\n**Interpretation of the observed Registry subscriber poison telemetry**\n\nRepeated 15-second Registry poison is real abnormal IPC backpressure and can create broker-side writer/watchdog threads and dispatch retries. It is, however, orthogonal to the 64 MiB/s brain `ReadFile` total: registry handling reads small policy files and writes snapshots; it never reads the executable, repository worktree, or transcript. The current-executable heartbeat continues on the brain main thread even while Registry workers are blocked.\n\n**Falsifiable live probes**\n\n1. Correlate `CONN_LIFECYCLE` records. `crates/spt-daemon/src/conn.rs:229-275` emits stable `conn=<id>`, role facts, wall/monotonic stamps, attach, and poison events. For a deadline poison, the same conn should show `stream-sub-attach`, then `CONN_WRITE_POISONED`/`stream-sub-writer-poison` about 15,000 ms later. A fast `CONN_WRITE_RETIRED` instead means organic peer death, not a 15-second black hole.\n2. Group `DISPATCH_EV` by `(gen, stream, family, attempt, conn)`. Unique Registry stream IDs about every peer registry cadence indicate new periodic feeds; repeated attempts for the **same** stream with breaker windows 2→4→8→16→30 seconds indicate recovery from a poisoned subscriber; a Served event followed by that same stream reappearing indicates retirement failed or an older broker lacks the retire verb.\n3. Process Monitor should show registry reads limited to subnet/roster policy files and writes to `identity\\registry\\*.json`/`heard.meta`. If the 64 MiB/s is attributed to registry, there must be comparably large named registry files and repeated full reads; source inspection predicts neither.\n4. Compare poison timestamps with thread count. Subscriber writer threads park on their queue while healthy and exit after clean detach or first failure. A monotonically increasing count without matching detach/writer-exit lifecycle events would establish a thread-retention bug; a sawtooth count around feed arrival/15-second failures is bounded churn rather than a leak.\n\n### 5 — Peer registry pump: 200 ms scheduler/marker checks; full local advertisement every 30 seconds by default\n\n* `crates/spt-daemon/src/pump/mod.rs:96-102` uses a **200 ms** scheduler tick and a **5-second** pump heartbeat write throttle.\n* `crates/spt-daemon/src/config.rs:35-46` sets default registry/notif/sync/update cadences to **30 s / 15 s / 60 s / 3600 s**; registry eviction defaults to 300 s.\n* `pump/mod.rs:583-613` polls workers’ one-shot wake markers on every 200 ms scheduler tick and sleeps immediately when nothing is due.\n* `crates/spt-daemon/src/pump/registry.rs:120-155` consumes repair/advertise markers, performs eviction, rotations, and `advertise_local` once per due/forced registry round.\n* `crates/spt-daemon/src/registryhost.rs:399-632` then enumerates self perches, reads small state/info records, adapter registry data, and each bounded `sessions.log`, updates the in-memory registry, and writes snapshots.\n* `registryhost.rs:893-919` derives at most eight recent project IDs per endpoint from the bounded session ledger. It does not traverse those project directories or read their transcripts.\n\nThis can generate periodic metadata and registry JSON churn, especially after many endpoint transition markers, but the normal cadence is 30 seconds, not sub-second full scanning. The 200 ms path consumes marker existence/removal state; it does not repeatedly scan every perch.\n\n**Falsifiable live probe:** group filesystem activity into 200 ms versus 30-second buckets. Normal idle pump activity is a tiny heartbeat/marker check; perch, session-ledger, adapter-registry, and registry-snapshot traffic appears in a burst at the due/forced registry round. Full perch scans every 200 ms would falsify the expected scheduler behavior.\n\n## Bottom line\n\nThe brain’s sustained read rate has a direct numerical source match: **`write_ready` re-hashes the entire ~31.955 MiB executable twice per second**. The four-to-five hosted sessions add expected five-second pulse-thread activity, which explains the active-thread census but not the exact byte rate. Project index, dispatcher, and registry loops do contain 100–500 ms polls, but their idle work is IPC or one directory/marker query; their repository, ledger, and snapshot work is gated and much less frequent. Registry subscriber poisons are a separate, falsifiable 15-second SharedSend backpressure condition worth tracking for broker thread churn, not a candidate for 64 MiB/s of brain file reads.",
  "files": [
    {
      "path": "crates/spt-daemon/src/brainproc.rs",
      "description": "Primary RCA. `BRAIN_HEARTBEAT` is 500 ms (line 63); the steady loop calls `write_ready` every heartbeat (259-297); `write_ready` calls `current_exe_hash` (365-373), which reads and SHA-256 hashes the entire running executable (381-384). Also wires live host, project writer, dispatcher, and peer pump."
    },
    {
      "path": "crates/spt-store/src/atomic.rs",
      "description": "Shows each 500 ms ready publication is a non-durable unique tmp write plus atomic rename; explains small write/metadata churn alongside the executable reads."
    },
    {
      "path": "crates/spt-daemon/src/livehost.rs",
      "description": "Hosts one lifecycle pulse thread per online live endpoint and performs a full live-host reconcile every 5 seconds; useful for explaining 4-5 intermittently active brain threads."
    },
    {
      "path": "crates/spt-daemon/src/lifecycle.rs",
      "description": "Defines configured pulse-loop semantics, per-tick state/drop scans, manifest hashing, and the one-shot echo gate. History/transcript fetch occurs only inside `fire_echo`, not on every quiet pulse."
    },
    {
      "path": "crates/spt-live/src/history.rs",
      "description": "Exact potentially large transcript reads: full raw transcript for `locate_normalize`, or full perch `history.log` for native history. Both are invoked only when lifecycle history is requested."
    },
    {
      "path": "crates/spt-daemon/src/projwriter.rs",
      "description": "Project-index writer: 500 ms invalidation directory poll, 250 ms debounce, five-minute periodic reconcile; branch-tip fingerprint gate and changed-branch-only seed-repo tree scans; bounded session-ledger reads during composition."
    },
    {
      "path": "crates/spt-store/src/projinval.rs",
      "description": "Proves the idle project-index poll is one short-circuiting `read_dir`; draining reads/deletes only queued tiny invalidation files."
    },
    {
      "path": "crates/spt-store/src/sessions.rs",
      "description": "Defines `<perch>/sessions.log` and its 64-row hard bound, ruling out unbounded session-ledger scan volume in registry/project-index loops."
    },
    {
      "path": "crates/spt-daemon/src/dispatch.rs",
      "description": "100 ms stream-table IPC poll; bounded eight-worker claim pool; keyed terminal/retry state; registry subscribe/read-to-EOF arm; success retirement; 2-to-30-second seat-blocked circuit breaker and discriminating telemetry."
    },
    {
      "path": "crates/spt-daemon/src/nethost.rs",
      "description": "StreamLog subscriber lifecycle: per-seat bounded writer queue/thread, replay-before-live, halt on first failed SharedSend write, poison/busy rules, fresh leases, EOF, detach, and retired stream removal."
    },
    {
      "path": "crates/spt-daemon/src/broker.rs",
      "description": "Defines the 15-second production brain write deadline and brokers stream subscribe/retire operations. The exact 15-second poison interval is a write deadline, not a registry cadence."
    },
    {
      "path": "crates/spt-daemon/src/conn.rs",
      "description": "Bounded physical broker connection writes, watchdog, stable connection IDs, and `CONN_LIFECYCLE` / `CONN_WRITE_POISONED` / `CONN_WRITE_RETIRED` telemetry needed to distinguish a deadline black hole from organic disconnect churn."
    },
    {
      "path": "crates/spt-daemon/src/pump/mod.rs",
      "description": "Peer-pump scheduler: 200 ms idle tick, five-second heartbeat write, config-driven workers, and due-only round loads."
    },
    {
      "path": "crates/spt-daemon/src/pump/registry.rs",
      "description": "Registry worker semantics: marker-triggered or cadence-triggered pre-round sweep and one fire-and-forget feed stream per peer/subnet."
    },
    {
      "path": "crates/spt-daemon/src/registryhost.rs",
      "description": "Inbound registry policy/apply/snapshot paths and outbound `advertise_local`; reads bounded perches/session ledgers and writes registry JSON snapshots, with no repository/transcript scan."
    },
    {
      "path": "crates/spt-daemon/src/config.rs",
      "description": "Production default cadences: pulse 5 s, registry 30 s, notif 15 s, sync 60 s, update 3600 s, registry eviction 300 s."
    }
  ],
  "architecture": "The deployed brain is a multi-threaded orchestrator around one 500 ms main heartbeat. The main thread owns a long-lived broker IPC client and, every heartbeat, requests net status and sessions, then republishes readiness. Readiness publication currently recomputes the resident executable hash by reading the entire binary, creating the dominant sustained read/CPU path. Separate brain threads host: (1) live-host reconciliation every 5 seconds plus one configured pulse loop per hosted endpoint; (2) one project-index writer whose idle loop performs a 500 ms invalidation-directory query and whose expensive reconcile is event/five-minute gated; (3) one dispatcher polling the broker’s in-memory stream table every 100 ms and spawning at most eight event-blocking workers; and (4) one peer pump with a 200 ms scheduler and 15/30/60/3600-second worker cadences. The dispatcher and peer pump share an `Arc<RegistryHost>`: outbound registry rounds scan bounded local state and push one finite stream per peer/subnet, while inbound dispatcher workers subscribe, merge, snapshot, wait for EOF, and retire served rows. Broker-owned StreamLogs attach each subscriber to a bounded dedicated writer; a broker→brain write blocked for 15 seconds poisons that physical connection and drives the dispatcher’s capped breaker. This IPC poison path can account for broker thread churn and repeated Registry telemetry, but it is independent of the brain main thread’s deterministic twice-per-second executable read/hash."
}