{
  "summary": "## Release and commit identity\n\n- Private home: `BigscreenVR/claude-spt-bs` (`.git/config` names it as `origin`; the public `SaberMage/claude-spt` remote is only the source mirror).\n- `v0.22.0` points to commit **`b61c03144080d12130eea0a8d785b878032eada1`** (`.git/refs/tags/v0.22.0`). Its release was the private-home relocation cut; `adapter/claude-spt.toml:494-500` and `CHANGELOG.md:38-44` explicitly say there was **no functional/behavioral change**.\n- The Windows identity fix was implemented by **`ddc1334be8b41c42e9aa3165fd9701860f8dfa5e`**, commit subject `feat: v0.24.0 — wake emit flip, win32 live anchor, shortform failure loudness` (`.git/logs/HEAD:302-305`).\n- `v0.24.0` points to **`58110a4b5a2269555386abcd96ac352972177593`** (`.git/refs/tags/v0.24.0`); the extra commit after `ddc1334` only silenced non-Windows dead-code warnings.\n- Current `v0.24.1` points to **`5cd77949e7b6e82cef13f680c4692b58c160af1c`** (`.git/refs/tags/v0.24.1`). Its only advertised behavioral delta from 0.24.0 is old/new `spt whoami --json` shape compatibility, not SessionStart PID/SID export (`CHANGELOG.md:7-12`, `adapter/claude-spt.toml:523-529`). Thus the 0.24.0 Win32 anchor behavior remains current.\n\n## Exact v0.22.0 behavior\n\nAt tag `v0.22.0`, `tools/claude-spt/src/hook.rs` does the following:\n\n1. `handle_session_start` takes `sid` directly from the Claude Code stdin payload's top-level `session_id` (`hook.rs@v0.22.0:752-755`). It does **not** derive SessionStart SID from ambient `OWL_SESSION_ID`.\n2. Harness-hosted `api seed` uses only `env.host_pid()`—the `--host-pid` value passed by the shell dispatcher (`hook.rs@v0.22.0:841-843`).\n3. It always appends `export OWL_SESSION_ID={payload_sid}` and `export SPT_ADAPTER=claude-spt` to `CLAUDE_ENV_FILE` (`hook.rs@v0.22.0:908-909`).\n4. It appends `export SPT_HOST_PID={dispatch_pid}` only if the dispatch PID is numeric and greater than 1; `1`, empty, and nonnumeric inputs are logged/omitted (`hook.rs@v0.22.0:910-920`; plausibility helper around `:352-361`).\n5. `plugin/sptc/hooks/dispatch.sh:55-66` appends the hook-binary cache on SessionStart and executes the hook as `… --host-pid \"$PPID\"`. Therefore v0.22's PID owner/source is the MSYS shell's `$PPID`, not a native Win32 query.\n\nOn Windows, MSYS can report pseudo-init PID `1` when `sh` is parented by a native process. Under v0.22 this causes both effects: the initial SessionStart seed is made with the wrong/empty anchor, and no fresh `SPT_HOST_PID` line is appended. The requirement record captures the original gaki-n evidence exactly: adapter v0.23 lineage, `$SPT_HOST_PID` empty while `$OWL_SESSION_ID` was set, then `--pid` failed (`traceable-reqs.toml:591-604`). Because v0.22 was behavior-identical to that identity path, it is affected.\n\n## Exact v0.24.0 behavioral delta\n\nThe source diff `v0.22.0..v0.24.0` shows the load-bearing changes in `tools/claude-spt/src/hook.rs`:\n\n- `HookEnv::self_resolved_host_pid` is added (`hook.rs@v0.24.0:103-115`).\n- `HOST_BINARIES = &[\"claude\"]`, `find_host_anchor`, and `host_anchor` are added (`hook.rs@v0.24.0:366-415`). The walk checks exact lowercase basename after stripping `.exe`, is capped at 32 ancestors, and rejects cycles. Resolution preference is native self-resolution first, then a plausible dispatch PID.\n- Harness-hosted SessionStart seed switches from `env.host_pid()` to `host_anchor(env.self_resolved_host_pid(), env.host_pid())` (`hook.rs@v0.24.0:907-913`).\n- `OWL_SESSION_ID` export remains the same payload-derived append; only the PID export switches to the preferred resolver (`hook.rs@v0.24.0:978-1001`). This is important: **v0.24.0 contains no new OWL_SESSION_ID freshness algorithm**.\n- A native Windows Toolhelp snapshot is added using `CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS)`, `Process32FirstW`, and `Process32NextW`; it records `(pid, parent_pid, lowercase basename without .exe)` (`hook.rs@v0.24.0:1886-1945`).\n- The concrete Win32 `self_resolved_host_pid` walks from `std::process::id()` to the first exact `claude` ancestor; non-Windows returns `None`, retaining real Unix `$PPID` fallback (`hook.rs@v0.24.0:2150-2167`).\n- Regression tests cover exact ancestor matching, truncated chains, PID-reuse cycles, the real Toolhelp struct layout, resolver precedence, and the gaki-n end-to-end case where dispatch PID `1` is replaced with `21772` for **both** the seed and env export (`hook.rs@v0.24.0:3206-3277`).\n- The manifest's own version history states the intended delta verbatim: native Toolhelp instead of trusting MSYS `$PPID`, healing **both** `seed --pid` and `SPT_HOST_PID`, with no new core seam (`adapter/claude-spt.toml:509-522`). The release notes say the same and require an endpoint bounce because the fix rides the hook binary (`CHANGELOG.md:14-23`).\n\nCurrent 0.24.1 line anchors for the same behavior are `tools/claude-spt/src/hook.rs:375-412` (host list/walk/preference), `:829-833` (payload SID), `:918-923` (seed), `:987-1004` (OWL/SPT exports), `:1904-1952` (Toolhelp), and `:2161-2169` (concrete resolver).\n\n## Ownership and RCA\n\n- **Claude Code owns the hook payload `session_id`** and the lifecycle/sourcing of `CLAUDE_ENV_FILE`.\n- **The adapter owns parsing that payload and appending `OWL_SESSION_ID`, `SPT_ADAPTER`, and `SPT_HOST_PID` lines.** `append_env_file` currently opens the file append-only and silently ignores unset path/open/write failures (`tools/claude-spt/src/hook.rs:2142-2150`).\n- **v0.22 PID provenance is adapter/dispatcher-owned and defective on Windows:** the shell supplies MSYS `$PPID`; the Rust hook trusts it subject only to `>1` plausibility.\n- **v0.24/current PID provenance is adapter-owned and normally fixed:** the Rust hook natively snapshots and walks the live Win32 process tree, with shell `$PPID` only as fallback.\n- **spt-core owns seed storage and stale-anchor validation.** The deterministic `SEEDED` followed by `STALE_SEED` for an explicitly dead PID is correct rejection, not evidence that core minted the stale identity. An explicit current PID+SID binding proves core accepts the correct tuple. No core workaround is warranted.\n- `OWL_SESSION_ID` is a different axis. SessionStart always uses the stdin payload SID for both `api seed --session-id` and the env-file append. Ambient `OWL_SESSION_ID` is not preferred in this path. The unchanged receive-heal path also prefers a nonempty hook payload SID; at v0.22 its relevant logic is `hook.rs:1489-1502` (current equivalent `:1599-1623`). Therefore a trace that logs an old SID with `sid_in_payload=true` means Claude Code supplied that SID in the hook payload—or that the trace/event was attributed to the wrong boot/time—not that the adapter selected stale ambient OWL state.\n\n## Already fixed versus residual/new defect\n\n**Already fixed in v0.24.0:** the specific gaki-n/v0.23-lineage failure where a live SessionStart hook receives MSYS `$PPID=1` despite having a real `claude.exe` ancestor. Native Toolhelp now supplies the current Claude PID to both the startup seed and `SPT_HOST_PID` export.\n\n**Not fixed by v0.24.0:** stale-value invalidation when native resolution itself fails. `host_anchor(None, Some(\"1\"))` returns `None`; the handler logs and appends no PID line. It does **not** append `unset SPT_HOST_PID` or otherwise remove an older `export SPT_HOST_PID=<dead-pid>` already present in a reused/persistent session env file. The current live skill then accepts any set numeric value greater than 1 without checking process liveness (`adapter/strings/skills/live.md:44-58`). Thus an old but syntactically plausible dead PID can survive and deterministically produce `SEEDED` → `STALE_SEED`. This residual is distinct from the already-fixed `$PPID=1` provenance defect.\n\nThe repository separately documents that a per-session `CLAUDE_ENV_FILE` can survive resumes of that session (`docs/KNOWN-HAZARDS.md:435-438`). That makes omission—not explicit invalidation—material during relaunch/resume.\n\n## Prediction for upgrading gaki-n\n\n[INFERENCE] Updating from adapter 0.22.0 to current 0.24.1 **and then bouncing/restarting the endpoint so a new SessionStart runs** should fix gaki-n's stale/missing PID in the normal case: the new hook walks from itself to the current `claude.exe`, seeds with that PID, and appends a later authoritative `export SPT_HOST_PID=<current-pid>`. The same SessionStart will append `export OWL_SESSION_ID=<payload-session_id>`. spt-core 0.32.0 is compatible because the manifest floor remains 0.27.0; 0.24.1's whoami parser supports both pre-0.33 and 0.33 shapes.\n\n[INFERENCE] Updating without a bounce will not repair variables already resident in the active session; the release explicitly says the hook fix takes effect on next endpoint start.\n\n[INFERENCE] If the upgraded-and-bounced session still shows a dead numeric PID, the current residual path has been hit: Toolhelp did not find a `claude` ancestor (or the old hook binary actually ran), no invalidation line was written, and the stale env entry survived. If OWL remains stale after a logged current SessionStart, inspect the hook payload first. A current payload SID plus stale shell OWL points to a silent `CLAUDE_ENV_FILE` write/source failure; an old payload SID is Claude Code payload ownership, outside the Win32 anchor fix.\n\n## Clean adapter-side follow-up, if the upgraded bounce reproduces\n\n1. On SessionStart resolver failure, append an explicit `unset SPT_HOST_PID` (and log write failure) so a previously valid/dead value cannot survive by omission.\n2. Make env-file writes observable—return success/failure from `append_env_file` and log the path/open/write error for identity exports instead of silently ignoring it.\n3. Harden `live.md` beyond `numeric && >1`: verify the PID exists and corresponds to the live Claude ancestor before using it, or expose the adapter's native resolver directly to the live invocation. A dead numeric PID must route to the manual/native fallback, not into `seed`.\n4. Do not add a core bypass for stale seeds. Core must continue rejecting dead/recycled anchors; repair provenance/invalidation at the adapter and Claude env-file seam.",
  "files": [
    {
      "path": ".git/refs/tags/v0.22.0",
      "description": "Exact v0.22.0 tag target: b61c03144080d12130eea0a8d785b878032eada1."
    },
    {
      "path": ".git/refs/tags/v0.24.0",
      "description": "Exact v0.24.0 tag target: 58110a4b5a2269555386abcd96ac352972177593."
    },
    {
      "path": ".git/refs/tags/v0.24.1",
      "description": "Exact current tag target: 5cd77949e7b6e82cef13f680c4692b58c160af1c."
    },
    {
      "path": ".git/logs/HEAD",
      "description": "Records implementation commit ddc1334be8b41c42e9aa3165fd9701860f8dfa5e and the release/tag follow-ups."
    },
    {
      "path": "tools/claude-spt/src/hook.rs",
      "description": "Owns SessionStart payload parsing, seed construction, CLAUDE_ENV_FILE appends, Win32 Toolhelp snapshot/process-tree walk, resolver precedence, and regression tests."
    },
    {
      "path": "plugin/sptc/hooks/dispatch.sh",
      "description": "Static dispatcher; passes shell `$PPID` as `--host-pid` and forwards the Claude hook JSON payload untouched on stdin."
    },
    {
      "path": "adapter/strings/skills/live.md",
      "description": "Consumes `$SPT_HOST_PID` and `$OWL_SESSION_ID`; current check accepts a numeric >1 PID but does not establish that it is live."
    },
    {
      "path": "adapter/claude-spt.toml",
      "description": "Version-of-truth history: v0.22 relocation/no behavior change; v0.24 native Win32 anchor and both PID consumers healed; current version 0.24.1; host_binaries=[\"claude\"]."
    },
    {
      "path": "CHANGELOG.md",
      "description": "Release contract and bounce requirement for v0.24.0; v0.24.1 is whoami-shape compatibility only."
    },
    {
      "path": "traceable-reqs.toml",
      "description": "REQ-LIVE-ANCHOR-WIN32 records the original gaki-n field evidence, precise intended fix, and unit/integration evidence."
    },
    {
      "path": "docs/plans/WAKE-EMIT-ANCHOR-PLAN.md",
      "description": "Design scope for v0.24.0: native Toolhelp, exact claude ancestor, resolution order, both consumers, and skill check-then-run."
    },
    {
      "path": "docs/KNOWN-HAZARDS.md",
      "description": "Documents that a per-session CLAUDE_ENV_FILE survives resumes, explaining how omission can preserve a stale export."
    }
  ],
  "architecture": "```mermaid\nflowchart LR\n  CC[Claude Code SessionStart] -->|stdin JSON session_id| Dispatch[dispatch.sh]\n  Dispatch -->|--host-pid MSYS $PPID| Hook[claude-spt hook SessionStart]\n  Hook -->|always payload SID| SeedSID[spt api seed --session-id]\n  Hook -->|append export OWL_SESSION_ID=payload SID| Env[CLAUDE_ENV_FILE]\n\n  subgraph V022[v0.22.0]\n    DispatchPID[dispatch host_pid only] --> Plausible{numeric > 1?}\n    Plausible -->|yes| SeedPID22[seed --pid + export SPT_HOST_PID]\n    Plausible -->|no| Omit22[wrong seed/omit export]\n  end\n\n  subgraph V024[v0.24.0/current]\n    Snapshot[Kernel32 Toolhelp snapshot] --> Walk[own PID to first exact claude ancestor]\n    Walk --> Prefer[host_anchor: walk first, dispatch fallback]\n    Prefer --> SeedPID24[seed --pid + export SPT_HOST_PID]\n    Prefer -->|both fail| Omit24[log and omit; no stale-value invalidation]\n  end\n\n  Env --> LiveSkill[live.md uses SPT_HOST_PID and OWL_SESSION_ID]\n  LiveSkill --> Core[spt-core seed/listen]\n  Core -->|dead/recycled PID| Stale[STALE_SEED — correct guard]\n```\n\nThe architecture intentionally separates two identities that happen to be exported together: session identity is Claude-payload-derived; process anchor identity is adapter-resolved. v0.24 changes only the latter's provenance. The clean boundary is therefore: Claude supplies SID, adapter exports SID and resolves/exports current host PID, core validates the tuple and must reject stale anchors."
}