# #1826 envelope-input-guard playground

Paste candidate identifiers (worktree names, session IDs) and see how `safeIdentifier()` would handle them in OrchestKit hook input paths. Background: orchestkit#1826 · prior fix: #1250 · upstream: #1794.

## 1. The leak

CC harness reads a hook's stdout (legitimately `{"continue":true,"suppressOutput":true}`) as if it were an identifier, then hands that back to OrchestKit as a worktree name or session ID. The historical incident (2026-05-13) created literal-named directories across 5 repos:

platform/{"continue":true,"suppressOutput":true}/.ruff_cache/ trading-ai-analyst/{"continue":true,"suppressOutput":true}/.ruff_cache/ jobscraper/{"continue":true,"suppressOutput":true}/.ruff_cache/ realconnect/{"continue":true,"suppressOutput":true}/.ruff_cache/ hq-ext-plugin/{"continue":true,"suppressOutput":true}/.ruff_cache/ + 191 corrupt session-summary files in ~/.claude/hooks/sessions/

Root cause lives in the CC harness (not OrchestKit). This PR makes OrchestKit fail-closed instead of propagating: any envelope-shaped identifier collapses to a fallback (`"unknown"` for worktree names, `"invalid"` for session IDs).

## 2. Try the guard

One candidate per line:

{"continue":true,"suppressOutput":true} feature-auth m167-1-wire-memory-hook ../escape .hidden --flag a/b session-abc123 foo bar
—

## 3. Results

Input | Verdict | Reason | Fallback used
--- | --- | --- | ---

## 4. What ships in this PR

File | Change
--- | ---
src/hooks/src/lib/safe-fs.ts | + `looksLikeIdentifier` / `safeIdentifier`
src/hooks/src/worktree/worktree-lifecycle-logger.ts | Wrap `input.name` read in `safeIdentifier(_, 'unknown')`
src/hooks/src/lib/paths.ts | `getSessionTempDir` collapses envelope IDs into `claude-session-invalid`
src/hooks/src/lifecycle/cleanup-envelope-corruption.ts | NEW · idempotent SessionStart sweep, marker-gated
tests · safe-fs / paths / cleanup-envelope-corruption | + 19 regression cases