gsd-core-hooks
A rule an agent can read once and forget is not enforced — it's remembered, which is a different thing.
What this is
The .claude/hooks/ directory from one real project (gsd-core), lifted wholesale. Every file here
exists because a specific incident happened first and the hook was written afterward to close the
gap — the header comment on each script names the incident, the date, and the exact failure mode.
This is not a designed-in-advance framework; it is a project's actual scar tissue, in installable
form.
It backs the rules stated in ../commands/ (bug-fixer.md, feature-builder.md,
triage-review.md, test.md) — those files say things like "gsd-test MUST exit 0 before any git
push" and "NEVER run node --test locally"; the hooks in this folder are what make those true
regardless of whether the agent remembers reading them.
The three failure classes it guards against
- Self-attestation. "I ran the tests" / "the review passed" is cheap to claim and expensive to
check by hand.
pre-pr-gate.sh + record-gsd-verdict.sh bind a push/PR action to a machine-read
verdict line for the exact commit sha being shipped — not "tests passed at some point," but
"tests passed for this sha, recorded by the one sanctioned writer, checked at the moment of push."
- Prose that goes stale mid-session. A directive is read once, at the start, then hours of
tool calls go by with nothing re-presenting it.
gsd-phase-gate.cjs is the original,
project-specific incident behind this project's separately-shipped ../artifact-gates/
engine — see the note in this folder's README.md before installing both.
- Harness ceiling false-negatives. A backgrounded or foregrounded long-running dispatch dies at
the tool harness's own timeout (commonly 600000ms) with no distinguishing signal from a real
failure — and a
pgrep -f "<cmd>" waiter can match its own argv and spin forever even after the
watched job finished. gsd-async-poll-guard.sh, gsd-foreground-poll-guard.sh,
gsd-pgrep-waiter-guard.sh, gsd-bench-pin-guard.sh, and gsd-test-single-flight-guard.sh each
close one verified instance of this.
Read this before installing
None of this is generic. Every script assumes gsd-core's specific toolchain: a remote dockerized
test runner called gsd-test that emits a {"type":"verdict","outcome":"passed"} line, a per-sha
pass-marker file under <git-common-dir>/gsd-passes/, Memtrace as the code-discovery MCP server, and
(in emitted-cjs-read-guard.cjs) a .cts → .cjs build pipeline specific to that repo. Installing
these unedited against a different project will produce confident denials referencing tools you do
not have.
Full per-hook breakdown, what each one assumes, and what to repoint: README.md.
When to reach for this vs. artifact-gates/
- Want the general "blocking step needs a produced artifact" engine, config-driven, with tests and
two example contracts? Use
../artifact-gates/.
- Want to see the concrete, incident-driven version that engine was generalized from, or want the
rest of this bundle (test-verdict gating, no-local-test, Memtrace-first, poll-loop guards) as a
starting shape for your own project's hooks? Use this folder.
Installing both leaves two independent engines answering the same "was the artifact produced"
question for Edit|Write|Bash — pick one, or scope them to disjoint tool matchers.
1---2name: gsd-core-hooks3description: A full PreToolUse/PostToolUse hook suite that machine-enforces a project's workflow rules instead of leaving them as prose — a passing test-runner verdict bound to the exact commit sha before push, no local `node --test`, Memtrace-first code discovery, no-defer artifact gating for long directives, and a family of guards against harness poll/timeout false-negatives (backgrounded dispatch, foreground poll-loop kills, self-matching pgrep waits, unpinned bench contention, double-dispatch). Use when an agent keeps skipping "ABSOLUTE"/"MANDATORY" rules in CLAUDE.md despite them being written down, when a CI/test gate needs to be unbypassable rather than self-attested, when local test runs are orphaning processes or leaving container relics, or when a long-running remote job's poll loop keeps dying against the harness's own timeout ceiling. Companion to `commands/` — those directives *describe* these rules in prose; this ships the mechanism that actually holds the line.4---56# gsd-core-hooks78> A rule an agent can read once and forget is not enforced — it's remembered, which is a different thing.910## What this is1112The `.claude/hooks/` directory from one real project (gsd-core), lifted wholesale. Every file here13exists because a specific incident happened first and the hook was written afterward to close the14gap — the header comment on each script names the incident, the date, and the exact failure mode.15This is not a designed-in-advance framework; it is a project's actual scar tissue, in installable16form.1718It backs the rules stated in [`../commands/`](../commands/) (`bug-fixer.md`, `feature-builder.md`,19`triage-review.md`, `test.md`) — those files say things like "gsd-test MUST exit 0 before any git20push" and "NEVER run `node --test` locally"; the hooks in this folder are what make those true21regardless of whether the agent remembers reading them.2223## The three failure classes it guards against24251. **Self-attestation.** "I ran the tests" / "the review passed" is cheap to claim and expensive to26 check by hand. `pre-pr-gate.sh` + `record-gsd-verdict.sh` bind a push/PR action to a machine-read27 verdict line for the *exact commit sha being shipped* — not "tests passed at some point," but28 "tests passed for this sha, recorded by the one sanctioned writer, checked at the moment of push."292. **Prose that goes stale mid-session.** A directive is read once, at the start, then hours of30 tool calls go by with nothing re-presenting it. `gsd-phase-gate.cjs` is the original,31 project-specific incident behind this project's separately-shipped [`../artifact-gates/`](../artifact-gates/)32 engine — see the note in this folder's `README.md` before installing both.333. **Harness ceiling false-negatives.** A backgrounded or foregrounded long-running dispatch dies at34 the tool harness's own timeout (commonly 600000ms) with no distinguishing signal from a real35 failure — and a `pgrep -f "<cmd>"` waiter can match its *own* argv and spin forever even after the36 watched job finished. `gsd-async-poll-guard.sh`, `gsd-foreground-poll-guard.sh`,37 `gsd-pgrep-waiter-guard.sh`, `gsd-bench-pin-guard.sh`, and `gsd-test-single-flight-guard.sh` each38 close one verified instance of this.3940## Read this before installing4142**None of this is generic.** Every script assumes gsd-core's specific toolchain: a remote dockerized43test runner called `gsd-test` that emits a `{"type":"verdict","outcome":"passed"}` line, a per-sha44pass-marker file under `<git-common-dir>/gsd-passes/`, Memtrace as the code-discovery MCP server, and45(in `emitted-cjs-read-guard.cjs`) a `.cts` → `.cjs` build pipeline specific to that repo. Installing46these unedited against a different project will produce confident denials referencing tools you do47not have.4849Full per-hook breakdown, what each one assumes, and what to repoint: [`README.md`](README.md).5051## When to reach for this vs. `artifact-gates/`5253- Want the *general* "blocking step needs a produced artifact" engine, config-driven, with tests and54 two example contracts? Use [`../artifact-gates/`](../artifact-gates/).55- Want to see the concrete, incident-driven version that engine was generalized from, or want the56 rest of this bundle (test-verdict gating, no-local-test, Memtrace-first, poll-loop guards) as a57 starting shape for your own project's hooks? Use this folder.5859Installing both leaves two independent engines answering the same "was the artifact produced"60question for `Edit|Write|Bash` — pick one, or scope them to disjoint tool matchers.