#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:

3. Results

InputVerdictReasonFallback used

4. What ships in this PR

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