{
  "summary": "Read-only source PREP complete. Pinned xtask supports the required explicit artifact/stage/state/version route without a build, but current signing custody is NOT safe under the two-process-only exception: xtask spawns two git provenance children which inherit its seed. V2 has a usable child-only EnvironmentFile injection seam, requiring future private-file/wrapper integration; this alone does not cure git-child inheritance. Historical r4 currently uses a DIFFERENT sequence-state filename from its phase logger, but changing its --state to the required rollout-state.json without removing/relocating that logger creates overwrite collisions.",
  "files": [
    {
      "path": ".worktrees/304-w2-repr/docs/DEBUG-ROLLOUT.md",
      "description": "Required runbook read first; setup/flags/protocol overview, not permission to execute its cargo or long-lived-env examples."
    },
    {
      "path": ".worktrees/304-w2-repr/docs/adr/0016-platform-targeted-update-sets.md",
      "description": "Required ADR read first: signed platform sets, exclusive channel pinning, monotonic version, local staging, no auto-minted signing seed."
    },
    {
      "path": ".worktrees/304-w2-repr/crates/xtask/src/main.rs",
      "description": "Pinned worktree source: keygen 2203–2217; pin 2220–2260; rollout 2379–2518; identity/parse/build/state/provenance helpers 2707–2779."
    },
    {
      "path": ".worktrees/304-w2-repr/crates/spt-daemon/src/relcache.rs",
      "description": "stage_update_set 262–285: directory creation, legacy artifact/stamp removal, per-artifact atomic writes, signed metadata last."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/provision-rig-r4.sh",
      "description": "Historical, ungranted guidance only: phase logger 129–136; secret intake 172–184; pin 222–241; signing 264–300; post-sign floor 307–314."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/launch-v2.ps1",
      "description": "Public native-launch interface 1–15; absolute/new destination checks 62–83; Add-Type before native environment-file parsing 94–97."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/launch-v2.cs",
      "description": "Argument and env JSON parsing 250–293; inherited env serialization 295–309; suspended child with explicit env block 371–377."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/fp-driver-d2-r5.sh",
      "description": "Step launcher 853–857 lacks EnvironmentFile forwarding; run-daemon path 885–896 uses a separate run scope plus EnvironmentFile."
    },
    {
      "path": ".spt/preserved/hertz-fp-driver-review/d2/run-daemon-control.py",
      "description": "1–9 writes daemon EnvironmentFile with only SPT_HOME and SPT_INSTALL_NO_FIREWALL; launcher itself consumes, does not generate, that input."
    },
    {
      "path": "crates/xtask/src/main.rs",
      "description": "Current main comparison only: corresponding keygen/pin 2035/2052; rollout 2176; provenance 2263–2266 and git helper 2560–2568 exhibit same inheritance problem. Do not cite these as pinned-binary provenance."
    }
  ],
  "architecture": "debug-keygen prints key material; debug-pin writes the isolated home's generic release-key overlay; debug-rollout reads a seed from env, reads supplied artifact bytes, gathers git provenance, signs/self-verifies, stages through ReleaseCache, then writes sequence state. V2 reads arguments/environment from files after Add-Type, builds an inherited-plus-overrides environment block, and creates an owned suspended native child. No existing interface removes the seed before xtask's git children.",
  "report": "STATUS / PROVENANCE\nAll findings below are static reads, not executed behavior or an attestation that the prebuilt xtask candidate was compiled from these exact sources. Subject remains .worktrees/304-w2-repr/target/release/spt.exe, required SHA256 72d309011415b419aabfdb0b4065df1183bfd0069a9f95b5779da62e43349b10; never rebuild/substitute. The xtask candidate is .worktrees/304-w2-repr/target/debug/xtask.exe; Main owns its hash. No binaries, validation, builds, tests, keygen, writes, or fleet-home access occurred.\n\nSUPPORTED COMMANDS AND OUTPUT (pinned worktree main.rs)\n• debug-keygen <key-id> (2203–2217): first positional argument; no --home, output-file or machine-readable flag. Generates an identity and prints stdout lines exactly `key_id:     <id>`, `public_hex: <64hex>`, `seed_hex:   <64hex>`, blank line, then setup prose and a cargo debug-pin example. ALL stdout is secret-bearing. No explicit disk write in this function. Do not send output to terminal/transcript/evidence/archive; the printed cargo command is guidance, not an authorized action.\n• debug-pin --home <H> --key-id <id> --public-key <64hex> (2220–2259). No seed needed. Unknown flags panic. Public key is validated. Creates H/identity, reads existing release-keys.json; unreadable/invalid/non-object JSON becomes {}; invalid/missing keys becomes {}; inserts/replaces selected key id, sets channel=debug, adds revoked=[] only if absent, writes pretty JSON non-atomically. Thus “leaves all existing keys alone” is too broad: same-id replacement occurs and malformed policy may be reset. Existing revoked is preserved, not guaranteed empty. Success stdout: `debug-pinned node release policy: <path>`.\n• debug-rollout supports --key-id (default dev-debug-2026), --product-version (default empty), --channel (default debug), --version <u64>, --expires-days <u64> (default 14), --stage-dir, --state, repeatable --artifact <target=path>, and --build-current (2379–2419). It does NOT accept --home. Supply SPT_HOME=H to the process and explicit --stage-dir H/releases. Artifact parsing splits once at '=' (2726–2731). The requested single input is x86_64-pc-windows-msvc=RIG_ROOT/bin/spt.exe, not the original worktree path and never a live fleet executable.\n• Required future argv shape: `debug-rollout --key-id \"$KEY_ID\" --version \"$SET_VERSION\" --stage-dir \"$H/releases\" --state \"$RIG_ROOT/rollout-state.json\" --artifact \"x86_64-pc-windows-msvc=$RIG_ROOT/bin/spt.exe\"`. Include --product-version only if the accepted experiment needs its explicit stamp; leave debug channel unless a different channel is expressly ruled.\n• Explicit version bypasses sequence-state read; absent version reads numeric JSON or {\"version\":N}, treats unreadable/malformed as zero and chooses +1 (2422–2425; 2743–2753). Explicit --version does NOT prove this version exceeds recipient history: self-verification uses current_version=0 (2485–2490).\n• No implicit build when --build-current absent: only 2429–2432 calls build_release_spt, whose cargo invocation is 2733–2741. It still runs two external git commands for provenance! repo_root is compile-time CARGO_MANIFEST_DIR ancestry (29–36), not caller cwd.\n• Success stdout (2503–2516): `DEBUG_ROLLOUT_STAGED version=<N> channel=<channel> platforms=<comma-joined sorted triples> stage_dir=<path>`, followed by `Peers pinned to debug will fetch via the normal update pump; run/poke the daemon pump for immediate propagation.` No daemon is launched/poked by this function.\n\nSIDE EFFECTS / COLLISION\nRollout reads all supplied artifacts, hashes bytes, stamps IPC and broker ABI from the xtask build (not by inspecting the supplied spt binary), signs metadata, verifies against its own derived public key and current_version=0, verifies artifact digests, stages, then writes sequence state (2439–2516). The tool does not prove its signing key matches the earlier home overlay; custody/setup must associate public_hex and seed_hex from the same keygen result.\nReleaseCache::stage_update_set (relcache.rs 262–285) creates releases/artifacts, removes old single-artifact file and platform stamp (ignoring removal failures), atomically writes each platform artifact, and writes signed metadata last as commit point. This is not one atomic transaction across cache plus sequence-state file; a later state-write failure may leave a staged set. write_debug_version (2756–2761) creates parent and overwrites state with compact `{\"version\":N}`.\nHistorical provision-rig-r4.sh phase logger (129–136) overwrites RIG_ROOT/rollout-state.json with rig_id/phase/detail/utc. Its actual signer at 280 currently uses RIG_ROOT/debug-rollout-state.json, so those two historical paths do not currently collide. Adopting required --state RIG_ROOT/rollout-state.json while keeping historical logger is unsafe: STAGE_RETURNED at 289 and STAGED at 300 overwrite sequence state immediately. Future wrapper must reserve rollout-state.json exclusively for xtask sequence state and remove phase logger or redirect its events to a distinct evidence log. The phase record is never applied-state evidence.\n\nSECRET CUSTODY: CRITICAL UNRESOLVED INHERITANCE\nADR0016 permits long-lived debug seed custody generally, but the present ruling is stricter. Do not use runbook long-lived export. Seed must never enter argv, source, repository, transcripts, rig/evidence archives, or daemon/apply/helper environments. Disable Bash xtrace/verbose and PowerShell transcription/debug tracing before any secret operation; do not print caught raw keygen output. Unsetting removes bindings, not a secure erasure guarantee for managed/string/process memory.\nHistorical block (174–175; 276–286) retains SIGN_SEED non-exported, unsets SPT_DEBUG_RELEASE_SEED, prefixes a SIMPLE timeout command, captures rc, then unsets SIGN_SEED. Its future Bash SHAPE is:\n  SPT_DEBUG_RELEASE_SEED=\"$SIGN_SEED\" SPT_HOME=\"$H\" \\\n    timeout -k \"$KILL_GRACE_S\" \"$stage_left\" \"$XTASK_BIN\" debug-rollout \\\n      --stage-dir \"$H/releases\" --state \"$RIG_ROOT/rollout-state.json\" \\\n      --version \"$SET_VERSION\" --key-id \"$KEY_ID\" \\\n      --artifact \"x86_64-pc-windows-msvc=$RIG_ROOT/bin/spt.exe\" \\\n      >\"$EVID/stage.out\" 2>\"$EVID/stage.err\"\n  rc=$?; unset SIGN_SEED SPT_DEBUG_RELEASE_SEED\nTHIS IS NOT A SAFE/GRANTED EXECUTION RECIPE. In addition to GNU timeout not proving native-tree death, pinned xtask invokes git rev-parse HEAD and git status --porcelain (2465–2471); git_stdout (2764–2772) uses Command::new(\"git\").args(...).current_dir(root).output(), with no env removal/clear. Therefore both git children inherit the seed, and any git-spawned helper could inherit it too. Current main has the same issue (2263–2266; 2560–2568), but those are comparison facts only. No supported --no-provenance/--seed-file/--seed-stdin switch exists in pinned parser. An environment-cleaning shim still first inherits the seed, so does not satisfy a strict no-other-helper rule. A different seed transport into the parent alone also does not fix this.\n\nIMPLEMENTABLE EXISTING V2 SEAM, AND WHAT MUST STILL BE INTEGRATED\nNever prefix a Bash function wrapping r5 bounded/helper calls with SPT_DEBUG_RELEASE_SEED: the assignment is exported throughout that function's helper tree. Prefixing pwsh launch-v2 itself also gives the launcher the seed before Add-Type (PS94), not only the target process. V2 has no allowlist-based clean env: ProcessStartInfo inherits parent env; EnvironmentFile only overlays JSON string entries (CS250–293,295–309).\nThe supported seam is secret-free launcher-parent environment plus PRIVATE EnvironmentFile injection directly into the child after Add-Type. JSON object contents: {\"SPT_HOME\":\"<absolute H>\",\"SPT_DEBUG_RELEASE_SEED\":\"<seed>\"}. File belongs in an owner-restricted non-repo, non-transcript, non-archive custody directory, not RIG_ROOT or evidence. The V2 launcher READS this file; it does not write it. r5 daemon integration's run-daemon-control.py writes its own public env file (1–9), which must never gain the seed. r5 step wrapper does not presently forward EnvironmentFile (853–857), so provisioning needs an explicit future wrapper integration/direct V2 invocation, not an assertion that r5 already supports signing custody.\nNative shell shape (all paths absolute native Windows paths; values in ArgsFile are public):\n  pwsh -NoProfile -File <accepted-launch-v2.ps1> -Label provision-sign \\\n    -Seconds <remaining-authorized-step-seconds> -Scope step -Admission process-tree \\\n    -RecordFile <new-evidence-record.json> -ArgsFile <public-sign-argv.json> \\\n    -EnvironmentFile <private-custody-dir/sign-env.json> -Exe <pinned-xtask.exe> \\\n    -OutFile <new-stage.out> -ErrFile <new-stage.err>\nArgsFile is JSON string array [\"debug-rollout\",\"--key-id\",\"<id>\",\"--version\",\"<N>\",\"--stage-dir\",\"<H>/releases\",\"--state\",\"<RIG_ROOT>/rollout-state.json\",\"--artifact\",\"x86_64-pc-windows-msvc=<RIG_ROOT>/bin/spt.exe\"]. Secret is NOT in this array or pwsh argv. This seam leaves launcher's process environment clean, though launcher necessarily reads seed-bearing JSON into memory; file removal is a future custody action after signer-tree termination confirmation, including failure paths. Timeout remains the launcher's owned-job deadline, not another unmeasured outer timeout. Run-scoped daemon job remains independent, never nested inside this step's job.\n\nFUTURE KEYGEN / CONTROL ORDER (not executable now)\n1. Establish owner-only ACL custody directory outside repo/rig/evidence/archive, with transcript/history/tracing protections and no ambient SPT_DEBUG_RELEASE_SEED. Exact host path/ACL grant belongs to Main's future provisioning ruling.\n2. Dummy sentinel first, no real key: exercise the intended private EnvironmentFile route and full timeout/job cleanup with a benign probe, reporting only presence/absence booleans. Cover prelaunch helpers/Add-Type parent, target, and descendants; do not mistake post-run unset for interval proof. Include the xtask provenance-child failure in the custody gate; sentinel-only tests are not authorized in current PREP.\n3. FUTURE keygen command shape `\"$XTASK_BIN\" debug-keygen \"$KEY_ID\" >\"$PRIVATE/keygen.stdout\" 2>\"$PRIVATE/keygen.stderr\"`, executed from private custody cwd under the accepted bounding launcher, whose OutFile/ErrFile point to that private directory rather than evidence. With V2, ArgsFile is [\"debug-keygen\",\"<id>\"], -Exe pinned xtask, -Scope step, -Admission process-tree; no EnvironmentFile seed needed. Use precreated owner-only directory and unique files. Native PowerShell direct shape is `& $Xtask debug-keygen $KeyId > $PrivateKeygenOut 2> $PrivateKeygenErr` but direct call itself lacks V2 bound.\n4. Custody wrapper reads private output internally, requires exactly one anchored key_id/public_hex/seed_hex line and 64 hexadecimal characters for each key, publishes ONLY id/public_hex. Store/derive signing EnvironmentFile only within custody directory, with no secret argv to an encoder and no whole-output interpolation on failure. This parser/file-lifecycle integration does not currently exist in accepted launcher; it requires future work and grant.\n5. Public pin argv: `\"$XTASK_BIN\" debug-pin --home \"$H\" --key-id \"$KEY_ID\" --public-key \"$PUBLIC_HEX\"` with clean environment and V2 bound. All spt CLI/supervisor/brain/apply routes remain RIG_ROOT/bin/spt.exe; xtask is the explicitly separate maintainer tool.\n6. Do NOT proceed to real signing under the current two-process-only exception until git-descendant inheritance is resolved by an explicitly accepted mechanism/ruling. The inspected executable interfaces alone cannot guarantee it. Removing git from PATH is not, by itself, proof that Windows executable search cannot find it in other directories; a deliberately unavailable git/provenance design would need separate host-search analysis and acceptance of missing/misleading provenance, not an invented safety claim. Rebuilding/replacing the pinned subject is forbidden; prebuilt xtask candidate changes also require their own explicit approval/pin.\n7. On every completion/error/cancellation path, remove secret environment bindings and retained seed variables before helper/apply/daemon work; dispose private files according to granted custody policy only after native termination confirmation. Never use debug-mark-applied. No mutation is authorized by this report."
}
