# Manual reproduction driver

Real Linux web development apps at base 73cbc6b1ede1692c2642b01cf55d9ef75c37c5f4 and head 69710bb30f2963d4ce170569401ad9e8d40a78d8. Fresh independent databases. Chromium preview, light theme, 1280 x 800. The replay runner was deliberately not used: its generic ACP model cannot exercise Codex hooks.

1. Put seed.py, hook.py, trust.py, show-hooks.py in one scratch folder. Run seed.py using an available Codex account. The portable seed consolidates the Python setup commands used during the run. It copies only private local Codex connection configuration and auth into the scratch home; those files are never uploaded.
2. Positive control: CODEX_HOME=<scratch>/codex-home HOOK_LOG=<scratch>/control-hooks.jsonl codex exec --enable hooks --skip-git-repo-check --ephemeral -s read-only --model gpt-6-luna 'Reply with just the word ok'. Observed ok, SessionStart, Stop.
3. In each commit worktree install with vp i; start T3CODE_DEV_SEED=0 vp run dev. Pair the preview browser with each server. In Settings / Providers / Codex set CODEX_HOME path to the scratch home and Launch arguments to --enable hooks; blur to save.
4. Add the fixture project using the real server CLI: node apps/server/src/bin.ts project add <scratch>/hook-demo --title Hook-demo --base-dir .t3.
5. Start the same chat with node apps/server/src/bin.ts thread start <scratch>/hook-demo 'Reply with just the word ok' --provider codex --model gpt-6-luna --wait --json --base-dir .t3. Both completed with ok and ran expected app-server SessionStart and Stop hooks. The CLI sets a title directly, so explicit regeneration below exercises background title generation.
6. Navigate directly to /<environment-id>/<encoded-thread-id>. Hide thread details to keep environment identity out of captures. Open the app terminal. Set PS1='$ ' and clear it. Write the current number of app-hooks.jsonl lines to the offset file before each recorded generation; run python3 ../show-hooks.py for the baseline.
7. Record: thread actions / Regenerate title. Wait for completion (Title kept), run clear; python3 ../show-hooks.py in the integrated terminal, then stop recording. Base added SessionStart + Stop from codex exec; head added zero entries. The first base generation was repeated for the clean recording.
8. Repeat regeneration on both commits after changing Launch arguments to --enable=hooks --enable codex_hooks, then to empty. Base ran SessionStart + Stop in both cases; head completed with Title kept and no hook entries in either case.
9. Stills were taken after blurring the terminal; two consecutive captures on each side were pixel-identical, separated by more than 500 ms. Both recordings were inspected via extracted contact sheets before upload. The terminal reads the actual hook log; it does not simulate the outcome.

The two independent servers used the same synthetic hook fixture. The chat hook count is cumulative (2 when before was recorded, 4 when after was recorded). The title count uses the offset at the start of the recorded case.

hook-transcript.jsonl removes executable installation paths and generated schema/output paths. runtime-results.txt records each completed case. No user data, auth, config secrets, or machine identity is included.

Unexercised: first-message automatic title trigger, branch/commit/PR text generation, and a blocking Stop hook. The observed comparison proves the title regeneration path with real Codex hooks.
