Skip to content

Commit 28c0e94

Browse files
build(bootstrap): make the SessionStart prover hook synchronous (#324)
## Summary Flip `.claude/hooks/session-start.sh` from **async** to **synchronous**, as requested. The session now blocks until Coq/Idris2/Zig are provisioned, so the first prompt is **guaranteed** a working prover instead of racing the background install. ## Why this is the right trade-off (not just a slower start) The ~10–15 min Idris2 source build is paid **once on a cold container**. Claude Code on the web **caches the container after the SessionStart hook completes**, so every subsequent session hits the installers' idempotent early-exits and starts fast — with the prover guarantee intact each time. And because the installers are fail-soft + idempotent, a cold build truncated by any hook timeout simply resumes on the next session until the container is cached. Net: pay once, fast + guaranteed thereafter. ## Change - Removed the async opt-in (`{"async": true, ...}`) so the hook runs synchronously. - The verbose Idris2 build is redirected to `$TMPDIR/ephapax-bootstrap.log`; only a one-line-per-prover summary reaches the session transcript (no build spam). - A header comment documents how to revert to async if the cold-start wait is ever unwelcome. Only `.claude/hooks/session-start.sh` changes; the installer scripts, devcontainer, `.tool-versions`, and `just bootstrap` are untouched. ## Validated in-session - Emits **no** async JSON (true sync mode). - Runs `bootstrap-provers.sh` to completion and prints the Coq/Idris2/Zig summary. - **No-ops silently** when `CLAUDE_CODE_REMOTE` is unset (devcontainers use postCreate instead). - This environment confirms `CLAUDE_CODE_REMOTE=true`, so the remote path is the one that fires. ## Hook execution mode **Synchronous.** Pro: provers guaranteed before the first prompt (no race). Con: first cold session waits for the build; amortized to ~0 by container caching thereafter. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AGFqWzByua4dKbA7W7oVsL --- _Generated by [Claude Code](https://claude.ai/code/session_01AGFqWzByua4dKbA7W7oVsL)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 04d3c3e commit 28c0e94

1 file changed

Lines changed: 23 additions & 11 deletions

File tree

.claude/hooks/session-start.sh

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,35 @@
66
# methods provers (Coq / Idris2 / Zig) via scripts/bootstrap-provers.sh so
77
# a fresh remote session can run the proofs without a manual install dance.
88
#
9-
# ASYNC (non-blocking): the first stdout line opts into async mode, so the
10-
# session becomes usable immediately while the provers install in the
11-
# background. Coq (apt) and Zig (vendor binary) land in ~1–2 min; the
12-
# Idris2 source build takes ~10–15 min and finishes within asyncTimeout.
13-
# Everything is idempotent + fail-soft, so re-runs and blocked egress are
14-
# both safe. PATH for idris2 is persisted via $CLAUDE_ENV_FILE.
9+
# SYNCHRONOUS (blocking): the session does not start until the provers are
10+
# provisioned, so the first prompt is GUARANTEED a working Coq/Idris2/Zig
11+
# (no race where Claude runs a proof before its prover exists). The heavy
12+
# Idris2 source build (~10–15 min) is paid ONCE on a cold container; Claude
13+
# Code on the web caches the container after the hook completes, so every
14+
# later session hits the idempotent early-exits and starts fast. Everything
15+
# is fail-soft, so a blocked egress domain degrades gracefully instead of
16+
# wedging startup; and because the installers are idempotent, a hook cut
17+
# short by any timeout simply resumes on the next session until cached.
18+
# PATH for idris2 is persisted via $CLAUDE_ENV_FILE.
19+
#
20+
# (To return to non-blocking startup, restore async mode by emitting
21+
# `{"async": true, "asyncTimeout": 1200000}` as the first stdout line.)
1522

1623
set -uo pipefail
1724

18-
# Opt into async so session startup is not blocked by the ~15-min Idris2 build.
19-
echo '{"async": true, "asyncTimeout": 1200000}'
20-
2125
# Only meaningful in the remote (web) environment; devcontainers use the
2226
# postCreateCommand instead. No-op locally.
2327
if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
2428
exit 0
2529
fi
2630

27-
exec "${CLAUDE_PROJECT_DIR:-.}/scripts/bootstrap-provers.sh" \
28-
> "${TMPDIR:-/tmp}/ephapax-bootstrap.log" 2>&1
31+
LOG="${TMPDIR:-/tmp}/ephapax-bootstrap.log"
32+
echo "[session-start] Provisioning provers (Coq/Idris2/Zig) before the session starts."
33+
echo "[session-start] First cold start builds Idris2 (~10-15 min, then cached). Full log: $LOG"
34+
35+
# Run synchronously; keep the verbose Idris2 build out of the session
36+
# transcript by logging to a file, then surface a one-line-per-prover summary.
37+
"${CLAUDE_PROJECT_DIR:-.}/scripts/bootstrap-provers.sh" > "$LOG" 2>&1 || true
38+
39+
echo "[session-start] Prover bootstrap finished:"
40+
grep -E '^\[bootstrap-provers\] (coq|idris2|zig):' "$LOG" || tail -n 3 "$LOG"

0 commit comments

Comments
 (0)