Skip to content

Commit 0288818

Browse files
Add Claude Code on the web SessionStart hook (Rust + Elixir) (#155)
## What Adds a cloud-only `SessionStart` hook so [Claude Code on the web](https://code.claude.com/docs/en/claude-code-on-the-web) sessions start with VeriSimDB's toolchain ready. Cloud images ship `cargo` but **not** Erlang/Elixir (which `elixir-orchestration` needs), so the hook installs them and warms the workspace. ## Changes (`.claude/`) `.claude/hooks/session-start.sh` — cloud-only (`CLAUDE_CODE_REMOTE`), idempotent, best-effort: - **Installs Erlang** (apt, OTP 25) + **Elixir 1.17** (precompiled from GitHub, built for OTP 25) — satisfies `elixir-orchestration`'s `elixir: "~> 1.17"`. - **Warms Rust** with `cargo fetch` (the ~20-crate workspace compiles on first use — too heavy to build synchronously every session). - **Best-effort `mix deps.get`** for the Elixir deps. Plus `.claude/settings.json` (registers the hook on `startup|resume`) and a `REUSE.toml` annotation covering `.claude/**`. ## Scope notes - **ReScript playground intentionally excluded** — per current policy ReScript is banned (the estate frontend is moving to AffineScript), so the hook only sets up the active toolchains (Rust + Elixir). *(Note: this repo's `CLAUDE.md` still lists ReScript as allowed — worth reconciling.)* - Every step is best-effort so a blocked registry never aborts session startup. ## Network caveat (important) `mix deps.get` needs the **Hex registry** (`repo.hex.pm` / `builds.hex.pm`). On a restrictive **Trusted** network allowlist those are blocked (HTTP 403) and skipped cleanly; on **Full** network access they complete. `cargo` works on Trusted (crates.io is allowlisted). For faster startup, the install step is a good candidate to move to a cached **Setup Script** (environment config). ## Validation (in a cloud session) - ✅ Hook exits 0; installs Erlang + Elixir (`mix 1.17.3`) and runs `cargo fetch` - ✅ Local-skip is a no-op when `CLAUDE_CODE_REMOTE` is unset - ⚠️ `mix deps.get` 403s on this session's Trusted network (degrades gracefully, as designed) — validates fully only on Full network > [!NOTE] > Synchronous hook: it guarantees the toolchain is ready before the session starts, at the cost of install time on first run. Can switch to async, or move installs to a cached Setup Script, if preferred. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017nyxs8RgqZa72PzrTu3L75 --- _Generated by [Claude Code](https://claude.ai/code/session_017nyxs8RgqZa72PzrTu3L75)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent bbfd48c commit 0288818

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

.claude/hooks/session-start.sh

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
#
5+
# SessionStart hook — Claude Code on the web (cloud sessions).
6+
#
7+
# Cloud sessions ship cargo but NOT Erlang/Elixir, which VeriSimDB's
8+
# orchestration layer (elixir-orchestration) needs. This hook installs
9+
# Erlang+Elixir, warms the Rust workspace, and best-effort prefetches the
10+
# Elixir deps, so a cloud session starts ready to build and test.
11+
#
12+
# Cloud-only: a no-op on local checkouts (your machine already has the
13+
# toolchain). Every step is best-effort — a blocked registry or network
14+
# must never abort session startup.
15+
#
16+
# Network note: `mix deps.get` needs outbound access to the Hex registry
17+
# (repo.hex.pm / builds.hex.pm). On a restrictive "Trusted" network
18+
# allowlist those are blocked (HTTP 403) and skipped cleanly; on "Full"
19+
# network access they complete. The cargo path works on Trusted (crates.io
20+
# is allowlisted). For faster, cached startup, the install step is better
21+
# moved to the environment's Setup Script (snapshotted once) — see
22+
# https://code.claude.com/docs/en/claude-code-on-the-web
23+
set -uo pipefail
24+
25+
[ "${CLAUDE_CODE_REMOTE:-}" != "true" ] && exit 0
26+
27+
ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
28+
cd "$ROOT" || exit 0
29+
30+
LOG="${TMPDIR:-/tmp}/verisimdb-session-start.log"
31+
: >"$LOG"
32+
log() { echo "[verisimdb] $*"; }
33+
run() { "$@" >>"$LOG" 2>&1; } # best-effort: failures land in the log, never abort
34+
35+
# ── Install Erlang + Elixir (idempotent: skip when already present) ────
36+
# Cloud images omit BEAM. Erlang/OTP 25 from apt + a precompiled Elixir
37+
# 1.17 (built for OTP 25) from GitHub satisfies elixir-orchestration's
38+
# `elixir: "~> 1.17"` requirement.
39+
if ! command -v mix >/dev/null 2>&1; then
40+
log "installing Erlang (apt, OTP 25) + Elixir 1.17 (GitHub)…"
41+
run apt-get update -qq
42+
run apt-get install -y -qq erlang-nox
43+
z="${TMPDIR:-/tmp}/elixir.zip"
44+
if run curl -fsSL -o "$z" \
45+
https://github.com/elixir-lang/elixir/releases/download/v1.17.3/elixir-otp-25.zip; then
46+
run mkdir -p /usr/local/elixir && run unzip -o "$z" -d /usr/local/elixir
47+
for b in elixir elixirc mix iex; do
48+
[ -f "/usr/local/elixir/bin/$b" ] && run ln -sf "/usr/local/elixir/bin/$b" /usr/local/bin/
49+
done
50+
fi
51+
fi
52+
53+
# ── Warm the Rust workspace (works on Trusted; the core of VeriSimDB) ──
54+
# `cargo fetch` downloads the whole workspace's dependency tree so later
55+
# `cargo build` / `cargo test` are offline-capable. A full `cargo build`
56+
# is deliberately NOT run — the ~20-crate workspace is heavy and this hook
57+
# is synchronous; it compiles on first use instead.
58+
log "fetching Rust workspace dependencies (cargo fetch)…"
59+
run cargo fetch --locked || run cargo fetch
60+
61+
# ── Best-effort: Elixir orchestration deps (needs Hex; 403 on Trusted) ─
62+
if command -v mix >/dev/null 2>&1 && [ -f elixir-orchestration/mix.exs ]; then
63+
log "fetching Elixir deps (best-effort; needs Hex registry access)…"
64+
(
65+
cd elixir-orchestration || exit 0
66+
run mix local.hex --force
67+
run mix local.rebar --force
68+
run mix deps.get
69+
)
70+
fi
71+
72+
log "ready (full log: $LOG)."
73+
exit 0

.claude/settings.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"hooks": {
3+
"SessionStart": [
4+
{
5+
"matcher": "startup|resume",
6+
"hooks": [
7+
{
8+
"type": "command",
9+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
10+
}
11+
]
12+
}
13+
]
14+
}
15+
}

REUSE.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ precedence = "aggregate"
148148
SPDX-FileCopyrightText = "2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>"
149149
SPDX-License-Identifier = "MPL-2.0"
150150

151+
# Claude Code on the web — SessionStart hook + settings
152+
[[annotations]]
153+
path = [
154+
".claude/**",
155+
]
156+
precedence = "aggregate"
157+
SPDX-FileCopyrightText = "2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>"
158+
SPDX-License-Identifier = "MPL-2.0"
159+
151160
# Nickel config
152161
[[annotations]]
153162
path = [

0 commit comments

Comments
 (0)