Glass Box Terrarium, explained from the inside

Last updated · Latest source on GitHub ↗

VerificationLesson

Merge gate

When agents write most of the changes, nobody can read every one closely. A gate is a fixed set of machine checks that every change must pass before it merges, so a change can be trusted without trusting whoever wrote it.

Know merge gates already? Skip to In Terrarium.

Outside the box · General

The concept

A merge gate runs the same checks on every change: the code lints, type-checks and builds, the tests pass, the content is valid. A change merges only when every check is green. Because each check asks a question a machine can answer, the gate's verdict means the same thing whoever opened the change, a person or an agent.

The same checks run in two places. The author runs them before pushing, to find a failure early, and CI runs them on the pull request, where the result decides the merge. A gate is only as good as what it checks: it can't see intent, so a clean build can still do harm when it runs.

Slow checks tempt you to skip them. A safe skip needs proof that the change can't affect the check, not a guess that it probably doesn't.

Full referenceGitHub Docs: Status checks

Looking inside

In Terrarium

Terrarium's is pnpm gate in package.json. Its steps split into two tiers, which scripts/gate/gate.ts names:

scripts/gate/gate.ts ↗17–27as of
17export const FLOOR = ['verify:skills-lock', 'verify:mermaid', 'lint', 'typecheck', 'validate:content'] as const
18export const HEAVY = ['test', 'build', 'test:e2e'] as const
⋯// …
25export function isInert(path: string): boolean {
26  return (path.endsWith('.md') && !path.startsWith('layers/')) || path.startsWith('.claude/skills/')
27}

The is the quick checks, including the type check and content validation, and always runs. The tier is the slow checks: the unit tests, the build and the browser tests. pnpm gate:scoped diffs the branch against main and skips Heavy only when every changed path is : a Markdown file outside layers/, or a link in .claude/skills/. The comments beside isInert give the reason for each.

A change of only Inert paths and Skill files (.agents/skills/, .claude/workflows/) skips just the build and the browser tests, and anything the script can't settle, such as a missing diff base, runs every step.

CI runs the same steps from .github/actions/gate/action.yml, and its run is the one that decides. It doesn't copy the Inert rule into YAML; it asks scripts/gate/gate.ts --decide and guards the Heavy steps on the answer. If that step fails, Heavy runs anyway, so a broken shortcut costs time and never skips a check. tests/unit/gate-parity.spec.ts fails when package.json, scripts/gate/gate.ts and the action disagree on the steps.

Before a branch's first push, the author also reviews its diff and runs pnpm gate:scoped on the final commit (Pull requests).

A green Gate is not always enough to merge: some PRs wait for a person. An agent merges with scripts/github/merge-pr.ts, which polls the PR's checks, merges on green, and first refuses the PRs it can recognise, such as one that changes a file. It reads that list, the paths of .claude/rules/adr-0004-human-merge.md, from main (Protected paths).

Why it's built this way

  • Checks, not judgement. Agents open too many PRs for a person to read, so machine checks decide the merge. Checks can't see intent, and a clean build can still do harm, so a fixed list of exceptions waits for a person: a list is checkable, where rating risk rests on judgement. The cost: some exceptions, such as untested behaviour, rest on the agent noticing them. ADR-0004 (docs/adr/0004-objective-safety-gate.md).
  • Skip only on proof. Slow checks make docs-only edits wait, but skipping on a guess would weaken green. So the Gate skips them only for paths, proven unread by any check; any doubt runs everything. The cost: over-caution, since a comment-only script edit forced full reruns, so Skill files got the narrower skip above.
  • One skip rule. Two copies of the skip rule would drift apart unnoticed. So CI asks the script for its answer instead of restating it in YAML, and treats a failed answer as "don't skip": a broken shortcut costs time, never an unrun check. .github/actions/gate/action.yml.
  • Steps in an action file. Agents can't edit GitHub's workflow files, so a new check reached the local gate while its CI step waited for a person, and CI checked less. The steps now live in an action file agents can edit, and a test fails when the copies disagree. The cost: that test sees which steps run, not what each does. ADR-0026 (docs/adr/0026-gate-workflow-thin-shell.md).

What to watch for

  • Green means the checks passed, not that the change is right. A PR can pass it and still wait for a person; ADR-0004 lists when.
  • Inert is about the path, not the size of the change. A rewrite of a whole doc outside layers/ is Inert; a one-character change to a page under layers/ is not, because the build renders it.
  • One run per checkout. A second pnpm gate:scoped in the same checkout exits at once while the first is alive, since two would race on the build output. Each linked worktree has its own lock, and a run that was killed leaves a dead pid the next run takes over.
  • A pass covers the tree it started on. If HEAD or the working tree changes while pnpm gate:scoped runs, it aborts before its next step, or warns after the last one; re-run it on the final tree.
  • Changing the Gate needs a person. scripts/gate/gate.ts, the action and the workflow file are on the list. A change to what the Gate checks waits for a person to merge it.

Try it in your project

Write down the one command that decides whether a change may merge in your project, make CI run exactly that command, and run it yourself before every push. If you add a skip, make it fail closed: run every check unless you can prove a change can't reach it.

Where next

Walkthroughs through this Lesson

Last updated · Latest source on GitHub ↗

Something went wrong

This content couldn’t be loaded. Reloading the page usually fixes it.

Technical details
route: /t/glassbox/guide/verification/gate