{
  "summary": "Supported W1b recipe exists: launch an owned copy of main289e3cdf in a fresh home, sign the same compatible bytes as a newer debug update set with main-built xtask, stage only that home, then invoke its copied spt update apply. Real apply owns AppliedPending and brain restart; no synthetic state or product edits needed. Read-only investigation only; nothing executed.",
  "files": [
    {
      "path": "crates/xtask/src/main.rs:2204-2259,2377-2524",
      "description": "debug-keygen/debug-pin/debug-rollout arguments, same-invocation seed binding, automatic ABI metadata, signed-set verification and staging."
    },
    {
      "path": "crates/spt-daemon/src/update.rs:37-182",
      "description": "Resource ABI=1, classifier and signature/digest-before-classification gate."
    },
    {
      "path": "crates/spt-daemon/src/frame.rs:27-31",
      "description": "IPC version=1 and minimum compatible version=1; also checked in .worktrees/hertz-307-evidence."
    },
    {
      "path": "crates/spt-daemon/src/applyhost.rs:120-345,528-554,900-944,1032-1202",
      "description": "Real apply gates, executable swap, pending record and broker restart; relevant isolation/class/platform/quarantine tests."
    },
    {
      "path": "crates/spt-daemon/src/brainproc.rs:477-497,670-746,840-931,994-1118,1822-2191",
      "description": "Readiness/hash receipts, record-driven trial, drain-gated promotion, restart trace, rollback and focused trial tests."
    },
    {
      "path": "crates/spt-daemon/src/broker.rs:169-240,2724-2788",
      "description": "5-second controller handoff and 15-second brain writer thresholds; epoch-safe controller slot transition preserving caller cause."
    },
    {
      "path": "crates/spt-daemon/src/relcache.rs:34-39,263-284,469-485",
      "description": "release.json, artifacts/<target>.bin and applied-state.json durable layout."
    },
    {
      "path": "crates/spt-daemon/src/endpoint.rs:18-95",
      "description": "Home-derived broker/seed names, daemon.pid and brain.ready; PID file is not liveness evidence."
    },
    {
      "path": "docs/DEBUG-ROLLOUT.md",
      "description": "Supported isolated debug-key trust overlay, signed-set rollout and explicit apply."
    },
    {
      "path": "docs/49-267-REFRESH-OBSERVATION-PLAN.md:44-240",
      "description": "Identity gates, 300-second observation plus separate 120-second teardown, progress/input attribution and interpretation limits; old admission and binary pin superseded."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/v9-exercises/r8/staged-r8-cleanup-exit.sh:951-1049",
      "description": "Retained #304 keygen/pin/sign recipe; omit its build-current and synthetic debug-mark-applied steps for this trial."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/field-rig-r10-w7-a2-QWHXWYUH/support/fp-bin/launch-v2.ps1",
      "description": "Small retained native launcher interface: child environment file, bounded run/step scopes, fresh receipts and native job ownership; requires adjacent launch-v2.cs."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/field-rig-r10-w7-a2-QWHXWYUH/support/fp-bin/launch-v2.cs:265-305,314-327,530-545,722-785",
      "description": "Explicit child environment, retained native process/job identity and membership receipts; rejects session-guard overrides."
    }
  ],
  "architecture": "debug-rollout signs ABI-tagged per-platform bytes and stages a SignedUpdateSet. update apply re-verifies trust/channel/expiry/hash and classifies, swaps its own executable, writes AppliedPending and requests broker-supervised brain restart. Supervisor promotes only after matching-generation readiness plus old-generation drain, with candidate hash check; broker/PTY remain held.",
  "report": "CALLABLE RECIPE (not executed)\nLet W=.worktrees/hertz-307-evidence, X=Main's xtask built from that checkout, A=Main's Windows spt.exe artifact, H=fresh absolute run-owned home, R=fresh retained evidence directory, E=run-owned writable copy of A (not Main's build output or resident executable), K=unique run key id. Every child invocation, including X, receives explicit SPT_HOME=H. Keep signer seed only in the signing child's environment/private keygen output, never shared daemon environment or ordinary logs.\n1. X debug-keygen K. Parse precisely one key_id/public_hex/seed_hex from that invocation; public hex must equal the public key repeated in the printed debug-pin instruction. The retained r8 driver:951-984 already does these identity/shape checks. Discard inherited SPT_DEBUG_RELEASE_SEED; use this invocation's seed only.\n2. X debug-pin --key-id K --public-key PUBLIC --home H. This writes H/identity/release-keys.json, adding K and channel=debug. Only H is trusted for this experiment; never pin the resident home. release.rs:371-382 shows the file OVERLAYS builtin trust, not replaces it, and revoked keys still apply. The private seed is not needed by the recipient.\n3. Launch E daemon run in the retained run-scope launcher with child environment {SPT_HOME:H, SPT_INSTALL_NO_FIREWALL:'1', SPT_PUMP_TRACE:'1'}, captured stderr, owned PID/birth/job receipts and isolated network configuration as separately admitted by Main. Use a run-owned copy because apply swaps current_exe, not a path selected by SPT_HOME. Start observation at run start, not restart-marker arrival.\n4. X debug-rollout --key-id K --channel debug --version 1 --product-version <actual-main-product-version> --artifact x86_64-pc-windows-msvc=A --stage-dir H/releases --state R/debug-rollout-state.json, with SPT_DEBUG_RELEASE_SEED from step 1 on this child only. Use version 1 only for genuinely fresh state; otherwise N must exceed applied/pending/staged/channel counters. No --build-current: Main owns the one build. An empty product_version is supported for this core-only trial; if supplied, do not copy #304's obsolete 0.69.0-fp stamp. Supply A as an immutable source separate from E. xtask verifies its new metadata and each digest before stage, printing DEBUG_ROLLOUT_STAGED. Its explicit --version path is caller-selected, so independently retain monotonic evidence rather than claim the local stage operation consulted recipient history.\n5. With the same explicit child H and verified isolation/ownership, invoke E update apply. This is the production signed path. applyhost:prepare_apply re-verifies stored metadata and artifact, then swap_and_record creates pending and request_brain_restart signals the broker. Do NOT run debug-mark-applied; r8:1047-1049 was a bootstrap-serving fixture shortcut, not acceptable W1b evidence. Do NOT write applied-state.json, edit signed JSON or forge readiness. Same artifact bytes with a higher release counter are supported: there is no different-bytes predicate. Reusing the same version yields AlreadyApplied (including an open pending trial) and does not create another trial. Reapply therefore signs a strictly higher N, not --force (quarantine override is deferred).\n\nCOMPATIBILITY: NO MISSING EXTERNAL ABI VALUE FOR SAME-BUILD REAPPLY\nConfirmed in target worktree: frame.rs:27/31 gives IPC=1 and floor=1; update.rs:42 gives broker resource ABI=1. xtask main.rs:2447-2448 automatically signs these compiled constants; it has no flags to override them. classify (update.rs:112-123) is BrainOnly iff resource ABI matches and candidate brain IPC >= broker floor. Main-built xtask + Main-built A + owned daemon booted from A therefore gives (1,1,1), a supported compatible same-byte reapply. Important source limitation: despite BrokerAbi documentation describing live-broker input, prepare_apply actually passes BrokerAbi::current() (target worktree applyhost.rs:205,221), not a fresh live ABI query. Bind running broker, applying CLI, signer and candidate to Main's producer/hash receipts; do not claim arbitrary resident/different-build compatibility from comments. For a different candidate, exact prerequisites are its true brain IPC/resource ABI and provenance matching the signer's constants, plus actual running-broker compatibility. Unknown values mean refuse; metadata signature alone cannot establish that an arbitrary file really implements xtask's ABI tags.\n\nARTIFACTS / RECEIPTS\nH/releases/release.json contains metadata_json plus signature_hex. Signed metadata artifacts['x86_64-pc-windows-msvc'] contains SHA256, brain_ipc_version=1, broker_resource_abi=1 and asset_name. Bytes reside H/releases/artifacts/x86_64-pc-windows-msvc.bin. Retain source SHA, build invocation/exit and hashes of A/E before apply, signer executable identity, public policy, exact signed JSON and staged artifact SHA; keep secret seed out of receipts. xtask self-verification proves its own signature/hash consistency; actual apply proves acceptance under recipient policy. xtask release-verify is NOT the right standalone verifier here: main.rs:2160+ parses SignedRelease and builtin-only policy, not this debug SignedUpdateSet/home overlay.\nReal swap writes H/releases/applied-state.json phase AppliedPending with version, rollback_binary, candidate_started_ms and prior_version, plus last-outcome=apply-pending (applyhost:280-293). Its write is best-effort, so receipt absence cannot be papered over; if the trial record is not observed, report that limitation rather than manufacturing it. Preserve the aside file path from the record. Supervisor consumes pending regardless of start-reason, clears stale ready before spawn, and promotes only when ready_generation matches and old_gen_drained is true. Retain brain.ready pid/generation/exe_hash, BRAIN_UPDATE_RESTART, BRAIN_PROMOTED or BRAIN_PROMOTE_REJECTED/BRAIN_TRIAL_EXIT/rollback evidence and final applied-state/applied/last-outcome records. Promotion updates Applied and legacy convergence records only after readiness. Hash-unavailable degrades to readiness-only, so require the hash receipt for this experiment's stronger byte claim. Same-byte reapply needs PID/generation change as restart evidence; unchanged SHA alone cannot prove a new process.\n\nSMALLEST RETAINED HELPER REUSE\nReuse r8 keygen parsing/pin/sign sequence as a reference, not the entire field driver (it includes unrelated mutations and debug-mark-applied). Reuse launch-v2.ps1 + adjacent launch-v2.cs, optionally launch-record.py for JSON argv/receipt preparation. Shape: pwsh -NoProfile -File <helper>/launch-v2.ps1 -Label <run-id> -Seconds <remaining-bound> -RecordFile <absolute-fresh.json> -ArgsFile <absolute-argv.json> -Exe <absolute-E> -OutFile <absolute-stdout> -ErrFile <absolute-stderr> -Admission process-tree -Scope run -StopFile <absolute-fresh-stopfile> -EnvironmentFile <absolute-child-env.json>. argv JSON is ['daemon','run']; per-command steps use -Scope step. Launcher refuses preexisting destinations; run scope requires StopFile; environment overlay forbids OWL_SESSION_ID/SPT_AGENT_ID/SPT_ENDPOINT_ID/SPT_SESSION_ID overrides. Existing run-daemon-control.py only writes SPT_HOME and SPT_INSTALL_NO_FIREWALL, so unchanged invocation alone will NOT add required SPT_PUMP_TRACE. Main must supply a child environment JSON with trace too (not mutate product or ambient parent). Native helper verifies job assignment/member/kill-on-close, records process creation, and offers H-bound supervisor/brain membership requests. It does not itself prove resident exclusions or broker socket address; those remain explicit admission checks.\n\nIDENTITY / SOCKET GATES\nBefore every mutation, especially apply/refresh/stop: retain child's actual H, H's absence-at-admission, E path/hash, launch-created PID plus birth, ancestry/owned-job membership; exclude resident daemon identity and caller ancestor chain (old plan's PID 4732 is a historical exclusion, not sufficient present-day census). endpoint.rs derives spt-daemon-broker-<home-hash>.sock and seed socket from Rust PathBuf hash; it is NOT a file under H or a SHA256 prefix. Establish actual isolated broker/seed endpoints and prove they differ from resident targets. daemon.pid is only a breadcrumb, not liveness/ownership proof. Retained launcher native membership binds home and birth; socket checks must be separately captured. Abort if identity, ancestry, current birth, environment or endpoint cannot be bound. Cleanup only owned run tree, confirm broker/brain/process-tree gone plus isolated sockets gone, retain all artifacts. Observe <=300s from run start, then separate <=120s cleanup; no later marker resets budget. Phase truncation must be named.\n\nCONTROLLER / BACKPRESSURE MEASUREMENTS\nSPT_PUMP_TRACE=1 on the launched daemon enables broker.rs:2765 CONTROLLER_SLOT_CLOSED:<session> conn=<id> cause=<caller-cause> with log stamp, emitted only on occupied-to-empty transition. Epoch-guarded no-op emits nothing. Causes defined at 2730+ are send_deadline, stall_evict, detach; preserve these literals, never infer clean close from a generic absent connection. send_deadline is the caller's observed handoff deadline, NOT proof writer exited. BRAIN_SUBSCRIBER_STALL_EVICT provides session+conn attribution; map run's controller/session connections directly, not from BRAIN_CONN_RETIRED (that token's unrelated error path is not general close evidence). brainproc.rs:914 BRAIN_PROMOTE_GATE_NOT_DRAINED reports first false drain read after readiness per trial generation; no session, no proof heartbeat brain.sessions() completed. Thresholds remain production 5s controller /15s brain writer (broker.rs:179,226); do not inherit SPT_BRAIN_WRITE_DEADLINE_MS override. Blocked-write onset has no emitted receipt, so eviction minus BRAIN_UPDATE_RESTART is NOT sink deadline latency. Keep generation/receipt clocks and counters per PROG line; queued tagged input requires child ACK plus distinct resulting state, ordered, not terminal echo. Missing event/tag means not observed within bounded window, never established absence/loss. One isolated success does not clear field behavior.\n\nTEST REFERENCES (READ ONLY; NOT RUN)\napplyhost.rs:900 apply_staged_swaps_binary_and_hands_off_without_touching_the_endpoint; :1032 apply_staged_update_set_selects_local_platform_artifact; :1082 apply_staged_update_set_refuses_broker_touching_payloads; :1112 apply_staged_gates_fail_closed_with_the_seat_untouched; :1165 apply_staged_refuses_a_quarantined_version; :528 staged_already_applied_classifies_up_to_date_vs_newer. brainproc.rs:1822 trial_promotes_on_ready_then_supervises_the_accepted_binary; :1870 ready_but_old_gen_never_drains_does_not_promote_rolls_back; :1918 ready_candidate_promotes_once_old_gen_drains; :2002 ready_but_wrong_bytes_rolls_back_never_promotes; :2045 ready_with_matching_bytes_promotes; :2090 trial_rolls_back_after_budget_of_pre_ready_exits; :2145 trial_kills_alive_never_ready_candidate_before_rollback; :2191 stale_generation_minus_one_ready_never_promotes. These fixtures establish source contracts, not a license to fabricate live pending state.\n\nHONEST REFUSALS\nUnproven producer/ABI relation; wrong platform; bad signature/digest; unknown/revoked key; wrong channel; expired metadata; counter rollback/no-op; quarantined version; RefusedClass for broker-touching metadata; dead broker before swap; restart failure after swap; missing pending/hash receipt; unknown ownership/socket; preexisting home; budget expiry. AppliedPending API result alone does not prove restart (applyhost accepts Ok(_honored), including false); require new brain identity/generation and supervisor traces. If signed pending trial cannot be observed, report refresh-only evidence separately rather than claiming W1b. All live evidence is still Main's future execution prerequisite; no builds, tests, daemon calls or mutations occurred in this investigation."
}