diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh deleted file mode 100755 index 61417bc91..000000000 --- a/.claude/hooks/session-start.sh +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env bash -# SessionStart: perform the per-clone setup AGENTS.md documents, at the start of -# the session. -# -# NOT before the MCP servers start, which this header claimed for its whole life -# and CLOUD-734 measured false. Two samples, one per container incarnation on -# 2026-08-19/20: the generated MCP config is written, the client opens its -# connection logs ~2.4s later, and this hook writes its first byte ~5s after -# that — the connections are initiated BEFORE the hook and complete while it is -# still provisioning. What rescues the install fix below is therefore not -# ordering but the client's 120s connect timeout: serena's connection was -# established 45.5s in, from inside this hook's window. CLOUD-316 recorded that -# timeout as 30s, at which this session would have lost serena outright. -# -# Why this exists (CLOUD-196). `.mcp.json` launches Serena with -# `mise exec -- serena start-mcp-server`, and `mise exec` INSTALLS a missing -# tool on demand. On a cold container that install runs inside the MCP client's -# startup window: measured 24s for `pipx:serena-agent`, the handshake did not -# complete, and MCP servers are not retried mid-session — so Serena was absent -# for a whole session while being perfectly runnable seconds later. Nothing was -# wrong with Serena or with mise; the per-clone `mise install` step simply never -# ran, so `exec` was doing an installer's job at the worst possible moment. -# -# This hook is deliberately SYNCHRONOUS (no `{"async": true}` line). Async would -# reintroduce exactly the race it exists to close: the session would start while -# the install was still running. The cost is paid only when cold — a warm -# `mise install` measured 0.067s. -# -# Failure is loud, never silent: a session that could not provision its -# toolchain must say so, because the original defect's whole signature was an -# absence with no error anywhere. -# -# This hook is NECESSARY BUT NOT SUFFICIENT. Measured on a genuinely cold -# container: the hook ran and completed before the session started, serena was -# installed and startable (21 tools, 1.1s warm) — and it was STILL absent from -# the session. A `.mcp.json` server is project-scoped and requires per-project -# approval; a cold container gets a fresh `~/.claude.json` whose -# `enabledMcpjsonServers` is `[]`, and a remote session has nobody to answer the -# approval prompt. That second gate is closed by committing -# `"enabledMcpjsonServers": ["serena"]` in .claude/settings.json — do not remove -# it thinking this hook covers the case. Both are load-bearing. -# -# It also PREFLIGHTS THE CONTAINER (CLOUD-261), by calling `container-preflight` -# after provisioning. "The toolchain installed" and "this container can do the -# work" are different questions, and the second one used to be answered three -# tasks in, by a failure wearing someone else's name. That task owns the what -# and the why; this hook owns only the WHEN — the very beginning of the session, -# before any work is planned against a container that cannot land it. -set -uo pipefail - -cd "${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel)}" || exit 0 - -# THE SESSION STAMP (CLOUD-431), written before anything else this hook does. -# -# `claim-check`'s `refined-this-session` rule compares an issue's tracker-minted -# `updatedAt` against this file's mtime: refinement must PREDATE the session that -# implements it. That predicate is only honest if the stamp marks the beginning -# of the session, so it is written here — above the provisioning steps, and above -# the `exit 1` a degraded container takes — rather than appended at the end. A -# session whose install failed still began, and a stamp written after the work -# would date the session to whenever the agent got round to claiming, which is -# exactly the ordering the rule exists to refuse. -# -# Silent and never fatal: this hook's job is setup, and a clone whose git dir is -# unwritable has larger problems than the claim gate. `claim-check` refuses on a -# MISSING stamp rather than passing, so a failure here fails closed downstream. -if stamp_dir=$(git rev-parse --git-dir 2>/dev/null) && - mkdir -p "$stamp_dir/batten-receipts" 2>/dev/null; then - : >"$stamp_dir/batten-receipts/session-start" 2>/dev/null || true -fi - -fail=0 -step() { # step