Skip to content

Gates

A gate is any check on a command, a commit, a pull request, or a merge. Together, GAIA’s gates hold every change to the process a well-run engineering team follows: work happens on its own branch, code is checked before it is committed, every change is reviewed before it merges, and secrets and other sessions’ work stay safe. The gates are on from your first session with no setup.

Most gates steer Claude. GAIA’s Claude Code hooks and permission rules act only on Claude’s own tool calls, never on a command you type in your own terminal. When one fires, the refusal goes to Claude, usually with the allowed form of the command or the next step, and Claude takes that route and carries on. There is nothing for you to do.

A few gates need you: an approval only you can give, a sign-in or a password, or a step Claude is kept from by design. A few others can reach you directly, but only when you act by hand: the git hooks when you commit yourself, pnpm’s install rules when you install a package yourself, and the GAIA-Audit check when you merge on github.com. Each gate’s Who acts line says which kind it is.

A gate is not a judgment of you or of Claude. Each one exists because the problem it prevents happens quietly and costs more to find later than to stop now.

GroupWhat its gates do
Commits and branchesKeep Claude’s work off the default branch, keep Claude from skipping the git hooks or dropping commits, and check each commit’s code, tests, and message before it lands.
Pull requests and mergeClaim the issue, open every pull request as a draft with a checked title, leave pull requests from forks to you, and allow a merge only after the Code Audit Team approves the change.
Secrets and protected filesKeep secrets out of what Claude reads and writes, protect the files GAIA generates and the records of audit approvals, and keep Claude from destructive deletes.
Worktrees and parallel workKeep each session in its own checkout and on its own ports, keep maintenance commands in the main checkout, and pause new work while your GAIA version is behind.
DependenciesKeep out package versions that are too new or less trusted than earlier ones, and keep the lockfile machine-written.
Updates and setupCheck each GAIA update against its checksum, keep two updates from running at once, run setup only where it belongs, and hold Claude’s guarded calls until jq is installed.
Tests and lintRequire the audit’s list of members, a reason on every lint rule that is turned off, and an accessibility check on every story.

The rest of this page lists every gate: what it stops, why, when it runs, who acts, and how it clears. Who acts reads “Claude” when there is nothing for you to do, “You” when the step is yours, and names the one case that reaches you when it is both. Clear it says what Claude does and spells out only the steps that are yours. A name in parentheses after When is the hook that enforces the gate; see Hooks.

  • Stops: git commit on main or master, a plain git push from them, a push that targets main, master, or HEAD, and force-pushes that name them (--force, --force-with-lease, or -f).
  • Why:
    • Commit: work reaches the default branch only through a pull request, where the audit reviews it before it merges.
    • Plain push: it would land your local commits on the shared branch without the pull request and audit every change goes through.
    • Force-push: the branch’s history is shared, so rewriting it pulls commits out from under every clone and branch built on it.
    • A push that targets main, master, or HEAD: stopped from any branch. A main or master source would land that branch’s commits on the remote without the pull request and audit. A bare HEAD pushes whatever happens to be checked out, which the guard cannot vouch for. The refusal tells Claude to push the feature branch by its own name and open a pull request.
  • When: before the git command runs (block-main-destructive-git). The hook checks the branch names main and master themselves, so a default branch with any other name is not covered.
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude creates a feature branch, pushes it by name, and opens a pull request as part of the work lifecycle.
  • Stops: Claude running git reset --hard to any commit other than the one the branch is on, on any branch and whatever the spelling: HEAD~1, HEAD^, @~1, a commit hash, or a branch name. A plain git reset --hard, or one to HEAD, discards only uncommitted changes and is allowed.
  • Why: the reset moves the branch to that commit, so every commit after it is dropped, and uncommitted work is thrown away with it. Dropping commits is your decision, not Claude’s.
  • When: before the git command runs (block-main-destructive-git). A permission deny on git reset --hard HEAD~* in .claude/settings.json stays as a backstop in case the hook does not run.
  • Who acts: You, and only when you really mean to drop commits.
  • Clear it: the refusal tells Claude the reset is yours, so Claude asks you instead of running it. To undo a commit and keep the history, Claude can use git revert <commit>. If you do want to drop the commits, run the reset yourself in a terminal or with the ! prefix, since the guard acts only on Claude.
  • Stops: a git checkout or git switch that would move the main checkout off a branch that another Claude session is using there, with an open pull request.
  • Why: the other session is working on that branch in the same folder. Switching branches swaps its files out from under it in the middle of its task, and it loses its audit round.
  • When: before the git command runs.
  • Who acts: Claude, and you only when the branch is your own and you really want to switch.
  • Clear it: after a merge, Claude uses the worktree cleanup, which never runs git checkout. If the branch is your own, run the command yourself with the ! prefix, since the guard acts only on Claude.
  • Stops: git commit or git push with --no-verify, -n on a commit, or -c core.hooksPath=<path>. Each of these skips the git hooks.
  • Why: the pre-commit hook is the minimum check that keeps code that fails typecheck, lint, or its tests out of history. If Claude could skip it, it would only be a suggestion.
  • When: before the git command runs (block-no-verify).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude fixes what the pre-commit hook reports and commits again. If the flag only appears inside the commit message text, Claude rewords the message.
  • Stops: a commit whose staged files fail typecheck, lint-staged (opens in a new tab), or their tests. It also stops a commit when the package registry cannot be read, when GAIA’s generated settings no longer match what they should be, or when a commit that changes the Code Audit Team roster or a member’s agent file fails the roster check.

  • Why: it is the fixed, scripted check that keeps code failing typecheck, lint, or its own tests out of history, no matter what the Claude session decided.

  • When: at git commit, through the native git hook .githooks/pre-commit.

  • Who acts: Claude, and you only when you commit by hand.

  • Clear it: Claude fixes the failures it prints and commits again. If you commit by hand and it stops you, ask Claude to fix what it printed. If no hook runs at all, the clone’s git hooks may be switched off, and nothing reports that at commit time. /setup-gaia checks for this on every run and offers to turn them on. Between runs, ask Claude:

    Check that this clone's git hooks are active, and turn them on if they are not.

    See Checking a clone.

  • Stops: a commit message that is not a Conventional Commit (<type>(<scope>): <subject>), and any commit when commitlint (opens in a new tab) cannot run.
  • Why: commit messages, branch names, and pull request titles all use the same list of Conventional Commits types. With a squash merge, the pull request title becomes the commit message on the default branch. A type GAIA does not know cannot be checked later on.
  • When: at git commit, through .githooks/commit-msg.
  • Who acts: Claude, and you only when you commit by hand.
  • Clear it: Claude rewrites the message with one of the types in .gaia/conventional-commits.json. When commitlint is missing, Claude runs pnpm install and commits again.
  • Stops: a commit, pull request, or issue whose text cites something only one machine has: a SPEC, UAT, or plan number, or a concrete path under .notebook/ or .gaia/local/. It reads commit messages, the lines a commit adds (prose in full, and code comments that take a whole line), and the title and body of a gh pr create, gh pr edit, gh issue create, or gh issue edit.
  • Why: nobody else can open a document that exists on one machine, and another machine’s SPEC with the same number is a different document. A merged commit or pull request keeps the reference for good.
  • When: at git commit, through .githooks/commit-msg and .githooks/pre-commit, and before the gh command runs (block-machine-local-ref-gh-text).
  • Who acts: Claude, and you only when you commit by hand.
  • Clear it: Claude writes out what the reference stood for: the behavior, decision, or evidence. A placeholder such as SPEC-NNN passes. If you commit by hand and the hook catches one, do the same, or ask Claude to reword the message. See the Notebook.
  • Stops: Claude committing source changes before every step of the gate passes with zero warnings.
  • Why: the commit hook checks only the files you staged. The full gate catches what breaks elsewhere, including warnings that would otherwise ship.
  • When: before any commit that touches source code or config that affects these checks. This is a rule Claude follows (.claude/rules/quality-gate.md), not a hook. The pre-commit floor is the part that runs no matter what.
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude fixes what it reports and runs it again. See Quality Gate for its steps and when it runs.
  • Stops: git commit of a new test for code with predictable output (utilities, parsers, hooks) when that test has never been seen failing in its current form.
  • Why: a test that passed from the start may not test anything. See Proving a test can fail.
  • When: before git commit (red-verify-commit-check). Only tests that are new in the commit need a recorded failure; editing or renaming a test that already exists never does. Exempt: stories and Playwright (opens in a new tab) tests, type-only tests, tests whose title is computed (a template literal or a test.each row), tests the classifier finds have no predictable result (component interaction, async work, layout), and test files that arrive unchanged from a GAIA release. A component or page test the classifier exempts goes through the worthiness check instead.
  • Who acts: Claude. Nothing for you to do.
  • Clear it: while the test fails, Claude runs that one test file as a command of its own, so the failure is recorded, then makes it pass. A failure is recorded only when Claude runs the test. A command that also puts the implementation back records nothing.
  • Stops: Claude starting work on a GitHub issue already labelled in-progress.

  • Why: two agents or two people who start the same ticket do the work twice, then fight over the merge.

  • When: before a branch or worktree is created for work that closes an issue. This is a rule Claude follows (.claude/rules/issue-claim.md), not a hook, so nothing blocks the work mechanically.

  • Who acts: Claude, and you only to tell Claude the work is dropped.

  • Clear it: Claude tells you the issue is taken and picks different work. The label comes off when Claude merges the claiming pull request with gh pr merge, and Claude also removes it when it closes that pull request without merging. On a tech-debt issue, the next /gaia-debt run removes a claim left behind. A merge on github.com leaves the label set, and so does work you stop without saying so. Tell Claude when you drop the work:

    I'm dropping the work on issue #<n>. Release its in-progress claim.
  • Stops: a gh pr create for this repository without --draft.
  • Why: reviewers get notified when a pull request is marked ready, and in a GAIA project that should happen when the audit passes. A pull request opened as ready notifies them before any audit has run.
  • When: before gh pr create runs (block-pr-create-without-draft).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude adds --draft and runs the command again. When the audit passes, it marks the pull request ready. A private repository where GitHub does not allow drafts is allowed without it.
  • Stops: a gh pr create or gh pr edit whose title fails the commit lint once (#<n>) is added to the end, and --fill with no --title.
  • Why: a squash merge uses the title as the commit message on the default branch. Without this check, a title that passed every local check could fail CI after the pull request was already open.
  • When: before the gh command runs (block-invalid-pr-title). The advisory PR Conventions workflow repeats the check in CI.
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude passes a Conventional Commits --title that still fits in 100 characters with the (#<n>) suffix added.
  • Stops: checking out or fetching the branch of a pull request opened from a fork, and auditing or merging one. It also stops a pull request that gh cannot identify as fork or not.

  • Why: the audit runs scripts from the files in your working folder. With a fork’s branch checked out, those scripts become the fork author’s code, running with your credentials.

  • When: before gh pr checkout or a git fetch of pull/<n>/head or pull/<n>/merge (block-fork-pr-checkout), and at the start of the merge workflow.

  • Who acts: You. Bringing in a fork’s code is a trust decision, and the guard keeps Claude from fetching a fork’s branch.

  • Clear it: read what the fork changes in GAIA’s own files first. Claude can show you that without checking the branch out:

    Show me the changes pull request #<n> makes under .claude/, .gaia/, and .github/, without checking it out.

    If you accept them, bring the branch into your own repository yourself from a terminal, since the guard keeps Claude from fetching a fork’s branch. These two commands copy the fork’s commits to a branch in your repository without checking them out:

    Terminal window
    git fetch origin pull/<n>/head:<branch>
    git push origin <branch>

    Do not check the fork’s branch out (gh pr checkout or git switch) at any point: that puts the fork’s hooks and scripts in your working folder, where they run with your credentials. Then ask Claude to open a pull request from <branch> and audit it like any other.

  • Stops: gh pr merge until every Code Audit Team member responsible for the changed files has approved the current version of the change.
  • Why: unreviewed code reaching the default branch is exactly what the audit exists to prevent.
  • When: before gh pr merge runs (pr-merge-audit-check).
  • Who acts: Claude, and you only when a member refuses and you decide to accept its Major finding with a reason.
  • Clear it: Claude runs the audit loop, fixes what it finds, and merges once every member approves. Pull requests outside the audit’s scope, and chore(deps) pull requests that only change dependency manifests, are approved by the hook itself.
  • Stops: gh pr merge when the gate cannot confirm the base branch it compares the change against: GitHub is unreachable, the latest base commit has not been fetched, or the branch has more than one merge base.
  • Why: each approval is tied to the branch’s own changes compared with the base GitHub reports. If it cannot confirm that base, the gate cannot tell whether the approved change is the one being merged.
  • When: before gh pr merge runs.
  • Who acts: Claude, and you only when gh needs you to sign in again.
  • Clear it: Claude runs the step the refusal names: it fetches the base branch, merges the base into your branch, or checks the gh connection, then retries. When you retarget a pull request to a different base branch on GitHub, the gate notices and names the reset, and Claude runs it and audits again. The one step you may need to take yourself is signing gh back in with gh auth login, because it asks you to log in.
  • Stops: merging a pull request on GitHub until the GAIA-Audit status shows success for its latest commit.
  • Why: without a required check, anyone with merge rights could use the button on github.com and skip the audit entirely.
  • When: on GitHub, at merge. Set up by /setup-gaia.
  • Who acts: Claude, and you only when you merge on github.com yourself.
  • Clear it: Claude posts the status on every merge it runs, so this check meets you only when you click merge on github.com. Ask Claude to merge it instead. When no audit member needs to review a pull request, the merge hook posts the status as skipped:.
  • Stops: the next round of the audit loop.
  • Why: each round’s fixes can lead to another round, so a loop nobody watches could keep spending without limit. The decision “is another round worth it” should not be left to the session least able to judge it.
  • When: when your context reaches its checkpoint line, when rounds stop making progress, or at the hard limit on rounds (audit-loop-bound).
  • Who acts: You. Only you can answer, so Claude cannot decide this one for itself.
  • Clear it: answer the question shown, or type audit-grant <n> or audit-accept as your whole message. See the branch checkpoint.
  • Stops: gh pr merge when a test the pull request changed has no worthiness record for its current content. This covers stories, Playwright tests, and component and page tests the classifier finds have no predictable result. An exact rename is skipped.
  • Why: these tests cannot be reliably watched failing first, so a worthiness review of each one takes the place of the RED gate. The hook checks only that a record exists for the test’s current content; it does not read the verdict or prove how the review was done.
  • When: before gh pr merge runs (worthiness-presence-check).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude runs the worthiness review on the changed tests and retries the merge.
  • Stops: a shell loop that checks gh pr view or gh pr checks over and over without reading mergeable.
  • Why: a loop that only stops on MERGED never stops once the merge becomes impossible. A merge conflict turns it into a loop that runs until you notice and stop it.
  • When: before the Bash command runs (block-handrolled-pr-poll).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude uses GAIA’s merge wait instead, which also stops when the pull request can no longer merge.
  • Stops: reading .env and .env.<name> files (but not .env.example), keys, certificates, files whose name contains credential (TypeScript source files such as credentialService.ts excepted), and anything under a secrets directory, through Read, Grep, or Bash. It also stops plain env or printenv, which print every environment variable.
  • Why: anything Claude reads goes into the session transcript and is sent to the model. Once a secret has been read, it is exposed.
  • When: before the tool call runs (block-sensitive-read).
  • Who acts: Claude, and you only when a file that holds no secret has a secret-like name.
  • Clear it: the refusal tells Claude what to do instead. Claude refers to the variable by name, reads .env.example for the list of names, and asks you for any value that is not secret. To set a value for one command, Claude uses env NAME=value <cmd>. For a key, certificate, or credential file that holds no secret, Claude asks you to read it out or rename it. Keep real values out of anything Claude reads.

When you turn on Claude Code’s sandbox, its read deny list in .claude/settings.json keeps Claude’s shell commands away from the same files too.

  • Stops: writes that contain an AWS key, a GitHub token, a private key, or a variable with a secret-sounding name set to a real-looking value. It also stops any write to a .env file except .env.example.
  • Why: a secret written into a tracked file is one commit away from the repository’s history. .env files hold local secrets Claude is kept from reading, so a write there would overwrite values it cannot see.
  • When: before Edit, Write, or MultiEdit runs (block-secrets-write).
  • Who acts: Claude for secret values in code, and you for the values in .env.
  • Clear it: Claude moves a secret value out of the code and refers to it by name. Putting the real value in your gitignored frontend/.env is yours, since Claude is kept from .env files by design. .env.example takes placeholder values.
  • Stops: writes to .gaia/manifest.json.
  • Why: the manifest lists only the files GAIA ships. /gaia-fitness reads it to tell GAIA’s files from yours, so an entry you add could mark your own file as GAIA’s and put it out of reach of fitness fixes. /update-gaia also replaces the whole file from the release, so the edit would be lost anyway.
  • When: before Edit, Write, or a Bash command that writes files runs (block-manifest-write).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude never needs to write it; your files never belong there. /update-gaia writes it through a specific exemption.
  • Stops: direct edits to frontend/.claude/settings.json, and a commit when it no longer matches what the generator produces.

  • Why: the file is generated from the root .claude/settings.json plus frontend/.claude/settings.overlay.json, so a hand edit is overwritten the next time it is generated.

  • When: before the Edit runs (a permission rule), and at git commit (the pre-commit hook).

  • Who acts: Claude. Nothing for you to do.

  • Clear it: Claude edits the root settings or the overlay instead, regenerates the frontend settings, and stages the result. The edit deny itself carries no message, but the generated-files rule loads as soon as Claude reads the file and names the source to change. If a direct edit slips through anyway, the pre-commit hook names the regenerate step, and Claude runs it and commits again. To change a setting, ask:

    Add <setting> to the root .claude/settings.json and regenerate the frontend settings.
  • Stops: Claude writing the files that record audit approvals, the audit loop’s state, or its context readings, and Claude running by hand the scripts that record your grant.
  • Why: if Claude could write them, it could fake an approval or your grant.
  • When: before the tool call runs (block-audit-loop-write and permission rules).
  • Who acts: Claude never needs these files. You act only when the audit loop’s state is corrupt.
  • Clear it: if a file in the audit loop’s state folder, .gaia/local/protected/audit-loop/, is corrupt, you move it aside from a terminal outside Claude Code, since Claude is kept from that folder by design. Claude tells you which file and prints the exact command for you to run.
  • Asks before any edit to an eslint.config.* file.
  • Why: a config edit can silence a rule just as easily as it can make an approved migration, and only you know which one you meant.
  • When: before Edit or Write runs (block-eslint-config-edit).
  • Who acts: You. Claude Code shows you a permission prompt.
  • Clear it: approve the prompt when the edit is the one you want, and deny it when it is not.
  • Stops: rm -rf of /, an absolute path outside the allowed scratch folders, $HOME, ., *, dotfile or brace globs, .git, .claude and anything in it, and node_modules, plus --no-preserve-root.
  • Why: each one can delete far more than you meant, in one command, with no undo:
    • An absolute path can reach anywhere on your machine, outside the repository. One containing .. resolves somewhere its spelling does not name.
    • $HOME or ~ holds far more than this repository, and git cannot restore what lives outside it.
    • . wipes the whole working folder, .git and .claude included. * removes every file and folder in it that is not hidden. Dotfile and brace globs remove .git and .claude.
    • .git holds the repository’s history, and a commit you have not pushed exists nowhere else.
    • .claude holds the hooks, skills, agents, and settings.
    • --no-preserve-root switches off rm’s own refusal to delete the filesystem root.
  • When: before the Bash command runs (block-rm-rf).
  • Who acts: Claude, and you only when node_modules really has to go.
  • Clear it: Claude deletes by an explicit relative path, and removes one file under .claude/ with a plain rm <file> (no flags). Claude can never run rm -r or rm -f on node_modules. For a broken or stale install, the refusal points Claude to pnpm install, which repairs node_modules in place. When it really has to go, delete it yourself in a terminal, then ask Claude to run pnpm install.
  • Stops: an edit whose path points into a different checkout than the worktree the session is working in.
  • Why: an absolute path left over from before the session switched worktrees still points to a real file, just in the wrong checkout. The edit would land there without any warning.
  • When: before Edit, Write, or MultiEdit runs (block-worktree-path-mismatch).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude redoes the edit with the path inside the session’s worktree.
  • Stops: /setup-gaia, /gaia-debt, and the maintenance commands when run from inside a linked worktree.
  • Why: a linked worktree holds another task’s work. These commands set up the clone or create their own branch, and must not write onto another task’s branch.
  • When: at the start of the command.
  • Who acts: You, since you started the command.
  • Clear it: a session cannot leave its worktree, so the refusal asks you to move it. Open Claude Code in the main checkout the refusal names and run the command there. The commands that change files then make their own worktree.
  • Stops: starting new work (a SPEC, a plan, a debt drain, a maintenance run, ad-hoc work) while your checkout’s GAIA version is behind the remote’s.
  • Why: the new work would branch from an out-of-date default branch.
  • When: at the start of the command. Resuming earlier work is never stopped.
  • Who acts: You, with one choice. In auto mode Claude makes it for you.
  • Clear it: Claude asks once; choose Pull now and restart, and Claude pulls and starts the command again. For a command only you can start, Claude prints it for you to paste. A command started in auto mode picks the pull without asking. Inside a linked worktree there is no pull option: open Claude Code in the main checkout, run /update-gaia there to pull, then start the command again.
  • Stops: pnpm dev, pnpm storybook, or pnpm pw on a port another checkout is using, and in a worktree that has no port file.

  • Why: if two servers slide onto each other’s ports, you end up testing the wrong checkout’s code.

  • When: at server start.

  • Who acts: Claude, and you only when another checkout’s server holds the port.

  • Clear it: in a worktree with no port file, Claude runs the setup step the refusal names. To start it yourself, ask:

    This worktree has no port file. Set it up again.

    Claude asks you before stopping a server that another checkout owns, or one it cannot identify.

  • Stops: recording a Code Audit Team member’s clearance in a worktree that was never set up.

  • Why: the clearance would land inside the worktree, where the merge gate never reads it, so the audit round would end with no verdict.

  • When: when the audit loop records a member’s result.

  • Who acts: Claude. Nothing for you to do.

  • Clear it: Claude runs the setup step the refusal names, then runs the member again. To start it yourself, ask:

    Set this worktree up again, then re-run the audit.
  • Stops: installing a package version published less than 3 days ago (minimumReleaseAge: 4320, strict).

  • Why: publicized npm compromises have been pulled within hours, mostly within a day. Three days covers that range, with extra room for a release published just before a weekend.

  • When: at pnpm install or pnpm add.

  • Who acts: Claude, and you only when you need a version sooner.

  • Clear it: Claude installs an older, mature version instead, and /update-deps skips versions inside the window on its own and picks them up on a later run. Make an exception only when you need a version sooner and you have checked where it came from. Then ask Claude to add it to the exception list in pnpm-workspace.yaml:

    I have checked where <package>@<version> came from. Add that exact version to the release-age exception list in pnpm-workspace.yaml, with a comment saying who checked it and why.
  • Stops: installing a package version with a lower trust level than earlier releases of the same package (trustPolicy: no-downgrade). The trust level comes from npm provenance: a signed record of how and where a version was built and published.

  • Why: a package that used to ship with provenance and suddenly does not is a sign of a possible account takeover.

  • When: at every pnpm install or pnpm add, checked against the whole lockfile, including --frozen-lockfile runs in CI.

  • Who acts: Claude, and you only to approve an exception for a known-good older release.

  • Clear it: Claude picks a version with equal or better provenance. When the flagged version is a known-good old release from before the package added provenance, ask Claude to add it to the trust exception list in pnpm-workspace.yaml:

    <package>@<version> is a known-good release from before the package added provenance. Add that exact version to the trust-policy exception list in pnpm-workspace.yaml, with a comment naming the package that requires it.
  • Stops: Claude editing pnpm-lock.yaml by hand.
  • Why: hand edits often leave the lockfile broken or out of step with itself. pnpm (opens in a new tab) rewrites it correctly.
  • When: before the edit runs (a permission deny in .claude/settings.json).
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude changes package.json or pnpm-workspace.yaml, then runs pnpm install, pnpm add, or pnpm remove and lets pnpm rewrite the lockfile. The generated-files rule loads when Claude reads the lockfile, so Claude has this step before it tries an edit.
  • Stops: an update whose release bundle does not match its published .sha256 checksum, or has no checksum.
  • Why: a damaged or tampered bundle would be merged straight into your Claude setup and hooks.
  • When: before the update question, in /update-gaia.
  • Who acts: You. The run stops and deletes the bad download.
  • Clear it: run /update-gaia again for a fresh download. If that one also fails the checksum, the release is damaged or has been tampered with; report it to GAIA.
  • Stops: /update-gaia from a GAIA 1.x project.
  • Why: 2.0.0 moved the app into frontend/. Merging that into a 1.x project would make every app file look deleted by GAIA, and the update would offer to delete your app.
  • When: before any download or change.
  • Who acts: You.
  • Clear it: follow Upgrading from 1.x.
  • Stops: the spec and plan commands on a machine that still has the 1.x specs or plans records and has not run the one-time move into .notebook/.

  • Why: a session must not hand out new SPEC or plan numbers while the move is pending, or work from a half-moved notebook.

  • When: at the start of the command. Claude stops, reports the one-line reason, and runs nothing else.

  • Who acts: You, to say go.

  • Clear it: from the main checkout, ask Claude to run the move once:

    Run GAIA's one-time notebook move and show me the report.

    The move renames the files into place, merges the old records into the new catalog, keeps the old records as backups, and deletes nothing. If the move itself stops, Claude follows the cause and fix it prints. See Upgrading from 1.x.

  • Stops: your /update-gaia while a teammate’s update is in progress.
  • Why: two people updating at once produce two conflicting update pull requests.
  • When: before any download, branch, or worktree.
  • Who acts: You.
  • Clear it: wait for the teammate’s update pull request to merge or close, or talk to them; the refusal names the pull request and its author.
  • Stops: /gaia-init anywhere but the root of a project created by npx create-gaia.
  • Why: init renames the project, removes GAIA branding, and rewrites files throughout the folder it runs in. Without GAIA’s manifest there is no GAIA project to set up, and in the GAIA source itself it would rewrite GAIA.
  • When: at /gaia-init’s first gaia init step, which runs after pnpm, the dependency install, and your answers to the setup questions.
  • Who acts: You, since you started the command.
  • Clear it: run it in a project created with npx create-gaia.
  • Stops: any Claude tool call a guard hook would check, while jq (opens in a new tab) is not installed. A call the guard can tell it does not cover, such as the command that installs jq, still goes through.
  • Why: the guard hooks read each call with jq. A guard that cannot read the call would otherwise let it through unchecked, so the guards would quietly switch off.
  • When: before the tool call runs, in the guard hooks.
  • Who acts: You. jq is a system package, and installing it can ask for your password.
  • Clear it: install jq, for example with brew install jq on macOS or your Linux package manager, then ask Claude to retry.
  • Stops: the audit when .gaia/audit-ci.yml has no auditors: block.

  • Why: with no roster, there is no way to work out which members must review the change.

  • When: when the audit works out which members to run.

  • Who acts: Claude, and you only to confirm members you added yourself.

  • Clear it: the refusal tells Claude to restore the block from the copy GAIA ships, and Claude does. If you had added members of your own, check they came back, or ask:

    .gaia/audit-ci.yml has lost its auditors: block. Restore it from the GAIA release this project is on, keeping any members I added.

Gaps in coverage do not stop the audit: a file no member covers goes to code-audit-frontend, the default member. A commit that changes the roster runs the roster check, which reports files no member covers and patterns that match no file, and Claude repairs what it finds. See Covering new code.

  • Stops: an eslint-disable comment with no -- reason after it (@gaia-react/lint 3.0.0).
  • Why: a later reader, human or agent, needs to know why the rule was turned off at that spot. Without the reason, nobody can tell whether the disable is still needed or safe to remove.
  • When: at lint, so at git commit through the pre-commit hook and in the Quality Gate.
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude fixes the code so the rule passes, or adds the reason: // eslint-disable-next-line <rule> -- <why>.
  • Stops: a story that skips its accessibility check, unless its id and a reason are recorded in frontend/test/story-a11y-opt-out.test.tsx. A story skips the check when it sets parameters.a11y.test to 'todo' or 'off', sets parameters.a11y.disable, turns on the a11y.manual global, has a !test tag, or uses a11y context, config, or options that differ from .storybook/a11y.ts.
  • Why: this check is the only light-theme accessibility scan a story gets; the Playwright scan covers dark. A story that quietly opted out would have no light-theme check at all.
  • When: in the Storybook (opens in a new tab) test run, so at git commit for staged stories and in the Quality Gate.
  • Who acts: Claude. Nothing for you to do.
  • Clear it: Claude fixes the markup so the story passes. If the opt-out is truly needed, Claude adds the story id and its reason to the test’s opt-out list.