AgentLabs
Alle inzichten

Claude Code · 23 min lezen

Stop Collecting Claude Skills and Plugins. Build a Pipeline.

What I install, in what order, what each layer catches, and what it costs, with the config files and the receipts from real pull requests.

Bram van Gestel · Gepubliceerd 2026-09-07

If you use Claude Code every day and want what it writes to survive production, the model plus a local review is not enough. This is the setup that lets an agent write most of the code in a production SaaS without me reading every line. Each piece comes with the reason it is there, so you can copy the parts that fit your codebase. Mine is a multi-tenant product: several companies' private data sits in one database, and a bug that shows one company another company's rows would end the business. That is the level of risk this pipeline is built for. It is a pipeline rather than a plugin list, and every layer exists because the layer above it let something through.

Title card reading 'Stop collecting Claude skills and plugins. Build a pipeline.' beside a funnel of eight layers, process skills through after merge, narrowing toward a green merge marker, with the reviewers-and-gate layer in solid colour.
Eight layers between a prompt and a merge. Each one exists because the one before it let something through.
  1. Before any code. Superpowers, a rigor tier in my global instructions, Supabase Postgres best-practices, Matt Pocock's codebase-design and domain-modeling skills. Catches: building the wrong thing, or the right thing without a spec on a risky path.
  2. Context before searching. CodeGraph, postgres-mcp (read-only), Context7. Catches: guesses about call paths, schema, and library APIs.
  3. While the model writes. A PreToolUse hook, a PostToolUse hook, a firing log, an allow/ask permission split. Catches: destructive commands, forged approvals, type and lint errors in the file just written.
  4. Architecture invariants. ast-grep rules with self-tests, eslint-plugin-sonarjs ceilings. Catches: a second implementation of something that must have one, oversized files and functions.
  5. Before the PR. Three repo subagents (code review, security review, test writer), beads, git worktrees. Catches: correctness and tenant-isolation bugs before anyone else sees the branch.
  6. On the PR. Five GitHub Actions jobs: gitleaks, Semgrep, ast-grep, zizmor, coverage ratchet, drift checks, RLS coverage, pnpm audit, Dependabot, and a repository ruleset (GitHub Pro) that makes them binding. Catches: secrets, static-analysis findings, schema drift, a new table without row-level security, unsafe workflow files, a merge around the gates.
  7. Review and merge. A custom Claude reviewer in Actions (Opus or Sonnet by path, one full review per PR plus primed incremental reviews), Greptile, the triaged commit status, /pr-triage. Catches: findings nobody dispositioned, a push nobody reviewed, a review that never happened.
  8. After merge. /deploy-watch, sweep.sh on Railway. Catches: a green deploy whose migration never ran.

Before any code: Superpowers, a rigor tier, and two supporting skills

Superpowers is the process layer. It does not make the model smarter. It makes it disciplined, which is the part I was missing. It lives in the official marketplace, which Claude Code registers on its own the first time you start it interactively:

/plugin install superpowers@claude-plugins-official

Seven of its skills do the daily work: brainstorming, writing-plans, executing-plans, test-driven-development, systematic-debugging, verification-before-completion, and requesting-code-review. My global instructions (~/.claude/CLAUDE.md, loaded into every session) make the routing explicit. Superpowers owns the process layer, and every other skill runs inside one of its phases or does not run at all.

  • "let's build X", new feature, changing behaviour: superpowers:brainstorming, then superpowers:writing-plans, then superpowers:executing-plans
  • bug, test failure, unexpected behaviour, "why is this slow": superpowers:systematic-debugging
  • writing a feature or fix: superpowers:test-driven-development
  • reviewing work, before merging: superpowers:requesting-code-review
  • claiming something is done, fixed, or passing: superpowers:verification-before-completion
  • authoring or editing a skill: superpowers:writing-skills
  • isolating feature work: superpowers:using-git-worktrees

The most useful part of that file is the rigor tier. It is plain text with no hook behind it, and it decides how much of the pipeline a change goes through. Risk decides, not the size of the diff. Name the paths where a missed defect hurts most in your codebase; in mine, anything touching tenant isolation or RLS, auth, migrations, connection roles, redaction or logging of partner data, CI and security gates, or money takes the full path (brainstorm, spec, plan, execute, review), even for a one-line diff.

An ordinary feature or fix with an obvious approach inside one package takes the standard path: TDD plus code review, no spec, no plan. A diff you can describe in one sentence takes the fast path, a direct fix with tests, and the moment a fast change starts growing it stops and gets promoted.

Specs on the full path get a "Prior art & best-practice check" section before they are final: one row per non-obvious decision, the source it was checked against, and a status. The spec that folded a paid reviewer into my merge gate has eight rows, and one of them is marked UNVERIFIED and designed around rather than relied on. High-risk specs also get an adversarial pass on the draft before planning starts.

Two supporting skills have a permanent slot. The Supabase Postgres best-practices skill loads before anything that lives in Postgres gets touched, a one-column change included. The skills vendored from Matt Pocock's collection are subordinate by rule: codebase-design and domain-modeling supply vocabulary during brainstorming, grilling fires only when I type "grill me", and research and handoff are standalone utilities.

claude plugin marketplace add supabase/agent-skills
claude plugin install postgres-best-practices@supabase-agent-skills

Two rules in the repo's own CLAUDE.md do more work than any skill. A plan is authoritative for intent and never for current state: when a plan and a live file disagree, the file wins and the plan gets a status banner. And "X is missing" is a factual claim about absence, so it gets checked against the file that would hold X instead of inferred from prose. An agent once recommended Semgrep and Dependabot to me as gaps in my CI while both were already in the repo.

Context before searching: CodeGraph, postgres-mcp, Context7

CodeGraph indexes the repo into a local graph and answers one question per call: the source of the relevant functions plus the call paths between them, including calls through callbacks and event handlers that grep cannot follow. The point is that the model edits with the callers in view instead of guessing at them. It also ships a prompt hook: when a prompt names something the index knows, the hook adds what the graph holds about it before the model answers. In ~/.claude/settings.json that is one line:

"UserPromptSubmit": [
  { "hooks": [ { "type": "command", "command": "codegraph prompt-hook" } ] }
]
npm i -g @colbymchenry/codegraph
codegraph init

postgres-mcp runs through uv from the repo's .mcp.json as a read-only server pinned to the throwaway local container. In restricted mode the model can only read. The URL is localhost and the credentials are the container defaults, so it is committed:

{
  "mcpServers": {
    "postgres-ro": {
      "command": "uvx",
      "args": ["--python", "3.12", "--with", "mcp==1.9.4", "postgres-mcp==0.3.0", "--access-mode=restricted"],
      "env": {
        "DATABASE_URI": "${POSTGRES_MCP_URL:-postgresql://app:app@localhost:5433/app}"
      }
    }
  }
}

Context7 is the third, for live library docs. Training data goes stale, and my instructions say to check current docs before asserting anything about a vendor API, so the model needs somewhere to check.

claude mcp add --scope user --transport http context7 https://mcp.context7.com/mcp

While the model writes: two hooks and a permission split

The instructions file is advice. Hooks are law: gates, not guidelines. Two hooks live in the repo's .claude/settings.json:

"hooks": {
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/block_dangerous.py\"" }
      ]
    }
  ],
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/post_edit.sh\"" }
      ]
    }
  ]
}

The PreToolUse hook reads the command Claude is about to run and exits 2 on a match, which blocks the call and feeds the reason back to the model. The list is short on purpose: recursive force delete, force push, dropping or truncating a table, anything naming the production database or deploy target, and the three GitHub verbs an agent must never run on its own (gh pr merge, gh pr review, gh run rerun). Since the merge gates became binding it also blocks the API writes that would forge them: posting a commit status or submitting a PR review through gh api.

Merging, approving, and re-running CI are mine. Posting a comment is allowed, because the agent posts its own triage comment after acting on the findings. The hook sees command text and cannot see intent, so "comment only after acting on the findings" is a rule in the instructions rather than something the hook can enforce.

The docstring is blunt about its limits: it matches surface syntax and has equivalent forms it misses, so it catches accidental slips and would not stop a determined bypass.

The PostToolUse hook runs on every write to a TypeScript file: typecheck, eslint with autofix, prettier, then an ast-grep scan of that file against the architecture rules. It never blocks. Both hooks have a test file beside them that pipes crafted hook JSON through the script and checks the exit code, including the case where a grep for the word "production" must be allowed through. The hooks tutorials I have read never test their hooks.

Every check that fires also appends one JSON line to a local, gitignored firing log: which check tripped on which file, never file contents. An advisory rule that fires constantly is a candidate for a hard gate, and a hard gate that has not fired in months is dead weight and gets deleted. It is a tally, not a test set: it records which check tripped, not the prompt that caused the bad edit, so you cannot replay it against a new model.

pnpm gates:report

Permissions are split into allow and ask, and the ask list is five entries, the commands that can lose local work or put something in front of other people: git push, git reset --hard, git clean, rm, and gh pr create.

Architecture invariants as code: ast-grep and eslint-plugin-sonarjs

Every codebase has a few "exactly one implementation" rules that live in someone's head. Write each one down as an ast-grep rule the day it is created, with a test, so it holds by construction rather than by convention. That habit is why my rules directory holds fifteen rule files and fifteen matching tests. The config is two keys:

ruleDirs:
  - rules
testConfigs:
  - testDir: rule-tests

Two of mine show the shape. One says the code that verifies Slack's request signatures exists exactly once: it flags any code that builds that signature outside the one shared module, and its message explains why a second copy is dangerous. The other says nothing outside the database package imports the raw Postgres driver; its test is four lines, two valid imports and two invalid ones. In CI the rule self-tests run before the scan, because a rule that cannot fail is worse than no rule.

ESLint with eslint-plugin-sonarjs holds the complexity guardrails in two tiers. Warnings for cognitive complexity above 15, nesting deeper than 4, and more than 4 parameters, surfaced by the post-edit hook. Errors for files over 700 lines and functions over 200, enforced by pnpm lint in CI.

pnpm add -D @ast-grep/cli eslint-plugin-sonarjs
"invariants": "ast-grep scan",
"invariants:test": "ast-grep test"

Before the PR: three repo subagents, beads, worktrees

Three agent definitions live in the repo's .claude/agents/, and the requesting-code-review skill dispatches the two reviewers before a PR opens. The code-reviewer is prompted as a paranoid staff engineer that rates every finding HIGH, MEDIUM, or LOW against a verification bar: cite file and line from something it has read, name the concrete input that produces the wrong result for anything HIGH or MEDIUM, and never speculate a finding into existence from a name, a comment, or a plan document. Its frontmatter:

---
name: code-reviewer
description: Reviews the last commit or a diff as a paranoid staff engineer. Use after implementing a feature, before opening a PR.
tools: Read, Grep, Glob, Bash
model: claude-opus-5
---

The security-reviewer is prompted against the threat model of the codebase it reviews rather than a generic OWASP list; a generic checklist produces generic findings about problems you do not have. Mine opens with the question that matters most here: is every step that touches data limited to one company in the code itself, rather than trusting the database to catch a mistake? Then it lists the ways that database-level protection can silently switch itself off.

The same file is handed verbatim to the CI reviewer on security-path PRs, so the local pass and the PR pass check the same list. The test-writer is the third, and the only one with write access.

Model choice differs per agent on purpose. The two reviewers run on Opus because a missed cross-tenant read costs more than a review; the test writer runs on Sonnet because writing a focused Vitest case does not need the expensive model. The reviewers cannot write, so the worst a bad review can do is waste my minutes reading it. HIGH and MEDIUM get fixed in the branch; everything else goes into one beads record per PR, titled "Review LOWs", with the findings verbatim and their file and line, so the findings survive after the session that produced them is gone.

Feature work runs in a git worktree per branch, so a half-finished experiment cannot sit under another session's feet. Tasks live in beads, a local graph-based issue tracker with dependency edges, so bd ready lists only work whose blockers are cleared.

On the PR: five GitHub Actions jobs and a ruleset that makes them binding

CI runs on GitHub Actions: one workflow file, five jobs, each there because something got through before it existed, and every job runs on every push. Every action in it is pinned to a commit SHA rather than a tag.

build-test runs two drift checks before the usual typecheck, lint, and tests. One regenerates Drizzle migrations from the schema and fails if anything comes out. The other rebuilds a snapshot of the real database schema, permissions included, and fails if it differs from the committed one. Tests run once with coverage, where each package's coverage floor can only go up, and files with no tests count as zero instead of being left out of the percentage.

secrets runs gitleaks over the PR's own commits, and a weekly job re-scans the full history of every branch. sast runs Semgrep with the default, TypeScript, and React packs.

invariants is the long list; its six checks are in the table below. The one to copy is the shape of the row-level-security check: a check for the one database rule you can least afford to break (in mine, that every table has row-level security switched on), which fails when it cannot run and tests itself first, so a broken check is never mistaken for a passing one. workflows runs the acceptance tests for the review and gate scripts themselves, then zizmor over the Actions workflow files, then checks that the ruleset on GitHub matches the one in the repo.

  • build-test: type errors, lint ceilings, coverage regressions, schema without migration, snapshot drift (about 3 min)
  • secrets: a credential in any of this PR's commits (about 10 s)
  • sast: Semgrep findings in TS and React (about 50 s)
  • invariants: ast-grep violations, undocumented env vars, a table born without RLS, oversized instruction files, stale cooldown exclusions, high advisories (about 30 s)
  • workflows: a broken review or gate script, an unsafe Actions pattern, a ruleset edited outside the repo (about 1 min)

The jobs are binding because a repository ruleset, kept as code, says so. It requires a pull request, squash merges, linear history, resolved review threads, and seven checks: the five jobs above plus review and triaged, the reviewer job and the merge gate from the next section. Each check is pinned to the GitHub Actions app, so a status posted by hand under the same name cannot satisfy it. Nobody can bypass it, me included, and the drift check above turns red the moment GitHub's copy differs from the file in the repo.

This is a private repository on a personal account, so the ruleset needed a GitHub Pro plan. Until that upgrade every gate here was advisory, and two PRs merged with a red invariants job. Two things follow from how GitHub evaluates required checks. A skipped job counts as passed, so no job is ever skipped, not even for a push that only touches a log file. And a status and a job must never share a name, because GitHub then needs both to pass and never re-checks the status.

Dependabot runs weekly with a seven-day cooldown and groups minor and patch bumps. A Monday job runs pnpm audit at high severity and keeps one rolling issue instead of opening a fresh one each week.

Two AI reviewers, and the gate that makes them count

The first reviewer is a custom Claude reviewer: a GitHub Actions workflow that runs a shell script to fetch the diff, call the Messages API, and post the result with gh. I tried the official action first; in every mode it ran the model and then failed to post the review (anthropics/claude-code-action#1141). Anthropic's managed Code Review is Team and Enterprise only.

Routing is by path: pick the paths where a missed defect costs the most and send those to the strong model. In mine, the MCP and database packages, migrations, workflows, agent guardrails, ast-grep rules, and package manifests go to Opus; the rest goes to Sonnet. The decision is one regular expression in one tested script, and when the script cannot decide (the file list failed to fetch, came back empty, or the script broke) the diff goes to Opus, because a needless strong review costs a few dollars and a missed defect costs far more.

^packages/mcp/|^packages/db/|(^|/)migrations/|^\.github/|^\.claude/|^rules/|^sgconfig\.ya?ml|^pnpm-workspace\.ya?ml|(^|/)\.npmrc|(^|/)package\.json
Decision diagram routing a pull request diff to Opus when any changed path matches the security regex and to Sonnet otherwise, with dashed arrows for a failed file list, an empty file list, and a broken predicate all landing on Opus, and the eight guarded path families listed as chips.
Reviewer model routing by path. Dashed arrows are the failure cases, all of which land on Opus.

The diff is filtered before the model sees it. Generated snapshots and lockfiles are dropped, and what remains is cut to a 1 MB budget by leaving out whole files, never half a file, with every omission named in the posted comment. That rule dates from one PR where three quarters of a 279 KB patch was generated output and the reviewer ran out of budget before it reached the route it was asked to review. Docs stay in; specs and runbooks are exactly the prose a wrong claim hides in.

Every push gets an incremental review, and every PR pays for exactly one full review. The reviewer keeps one comment per PR, edits it in place, and records in a hidden marker which commit it reviewed last and which commit got the full review. On a push it reads only what changed since that last reviewed commit, with its own previous review in front of it so settled findings stay settled.

The full review runs when a human posts the first triage comment, using the review script from the main branch rather than the PR's copy, so a PR cannot rewrite the script that reviews it and run it with write access. Later triage comments run no model, and a comment containing <!-- full-review --> forces a fresh whole-PR pass. On database PRs the prompt also gets a snapshot of the current schema, and on security paths the threat-model checklist goes in word for word.

Before the one-review rule, every triage comment started a full review from scratch, and the PR that introduced the ruleset paid for eight. A push that touches only the triage log does not trigger a review either, and paths-ignore on the trigger cannot do this: for pull request events GitHub filters on everything the PR changed, not on the commits just pushed (actions/runner#2324). So a script asks GitHub what the push itself changed, and whenever it cannot tell (a force push, a missing commit, an API error, more than 300 files) it reviews.

Greptile is the second reviewer. It reads the full diff against its own rules file, and it caught two P1s in a spec back when the first reviewer still dropped docs. The config is committed:

{
  "strictness": 2,
  "commentTypes": ["logic", "syntax"],
  "triggerOnUpdates": false,
  "triggerOnDrafts": false,
  "statusCheck": true,
  "fixWithAI": true,
  "ignorePatterns": [
    "packages/db/drizzle/meta/",
    "**/__snapshots__/",
    ".beads/",
    "docs/ci-triage/",
    "pnpm-lock.yaml",
    "*.lock"
  ]
}

Two of those fields are the cost policy. Drafts are not reviewed, so I iterate in draft and mark ready once. Updates are not reviewed, so a fix push does not bill and I resolve the thread myself after fixing. Its own status check is not one of the ruleset's required checks; the gate below covers its threads. Its rules file is where your priorities go; mine opens with the one that matters most here and closes with the same verification bar the other reviewers get:

## 1. Tenant isolation & RLS (highest priority)
## 6. Verification bar, every comment

The gate ties both reviewers together. A required commit status named triaged, posted by a second Actions workflow, stays red until four things hold: a comment containing the literal marker <!-- triage --> post-dates the last substantive commit, every Greptile thread is resolved, the one full review is still part of the branch, and the latest commit has been reviewed, either directly or with only a log-file change on top of it. That last condition is what makes a push nobody reviewed show up as a red gate, and the status text names the push. A review that had to leave a file out for the size budget does not count as a review of that commit. The gate never reads the code. The script header says why:

Industry guidance is not to hard-gate on AI review findings: false-positive rates run 5 to 15%, up to 40% of AI alerts get ignored, and a hard gate with false positives gets routed around within weeks. "Was a disposition recorded" has no false positives. It is a deterministic fact about the PR, which is the class of rule a hard gate suits.

Whenever it cannot tell, it stays red, including when it cannot read the list of review threads. And it says of itself that it is a checklist rather than a security control. Anyone who can comment can satisfy it, and those are the same people who can merge; it defends against forgetting, not tampering.

State diagram of the triaged commit status with four red states (no triage comment after the last substantive commit, no full review yet, last push not reviewed, unresolved Greptile thread) and one green state, with the human or workflow event on each transition and a note that later triage comments run no model.
The triaged status as a state machine: four conditions and one paid full review. It gates the disposition, never the finding.

/pr-triage is the companion command: a markdown file in the repo's .claude/commands/ whose only allowed tool is the script it wraps, so the model can read the verdict and cannot fix anything on the way. It reads the review text itself rather than the green check, classifies the result as FINDINGS (my diff's problem), TOOLING (the reviewer or CI broke, never reported as a code defect), WAITING, or READY-TO-READ, and appends one row to a per-PR log, so a reviewer that intermittently returns nothing shows up as a pattern. It exists because two PRs showed every check green with nothing usable behind the checkmark: one review job failed outright, and one review was cut off mid-word by the token cap.

A green review check does not mean a review happened.
/pr-triage 133
pr-triage.sh     exit 0 READY-TO-READ · 1 FINDINGS · 2 WAITING · 3 usage · 4 TOOLING
check-triaged.sh exit 0 triaged · 1 owed · 3 usage
The review loop as it runs on a pull request: open as draft, mark ready, read findings through pr-triage, fix HIGH and MEDIUM then push and wait for the incremental review, send LOWs to a record, resolve Greptile threads, post the triage comment that fires the one full review, and reach a green triaged status.
The review loop on a real pull request: one paid full review, primed incremental reviews after every push, then a green triaged status.

The loop on a real PR runs like this. Open as draft on the first commit and iterate; nothing bills. Mark ready, and the incremental review and Greptile both post.

Four commit statuses on one pull request head in order: red triaged at 09:41:02 with no triage comment on the record, red triaged at 09:41:14 with no full review yet, green review-full at 09:41:59, and green triaged at 09:42:03 with no unresolved Greptile threads.
A mock of the real statuses on the most recent merge. One triage comment, one paid full review, green a minute later.

Read the findings through /pr-triage. HIGH and MEDIUM get verified against the cited line first, then fixed and pushed, and then I wait for the incremental review of that push to land. LOW stays posted and goes into the PR's "Review LOWs" record. Resolve every Greptile thread, fixed or answered.

Post the triage comment naming the check that was run; the agent may post it after acting on the findings, or I do. That first comment fires the one full review; the gate goes red for a moment while it runs, then the review lands, records its SHA, and refreshes the gate itself. On the most recent merge the status history reads red at 09:41:02 for "no triage comment on the record", red at 09:41:14 for "no full review yet", review-full posted at 09:41:59, green at 09:42:03. One comment and one paid full review, green within a minute.

gh pr comment 133 --body "<!-- triage -->
Fixed: HIGH-1 (verified: reproduced with input X, test added), MEDIUM-2.
LOWs left in place; bead <id>.
Greptile threads: all resolved."

After merge: deploy-watch and sweep

A green Railway deploy is not proof the migration ran: if one setting in the dashboard is missing, Railway ignores the deploy config file, the migration step never runs, and the deploy still reports success. So /deploy-watch, a read-only script, pins one commit and demands three pieces of evidence: a deployment whose commit hash matches, the deploy config Railway used for that deployment, containing the migration command, and the migrator's own "migrations applied" line in that deployment's log. Exit 0 is confirmed, 1 is failed, 2 is pending or inconclusive, and 2 is never folded into 1, because absence of evidence is not evidence of absence.

sweep.sh removes local worktrees whose PR has merged and loudly reports any merged worktree still holding uncommitted work. It takes merge state from GitHub rather than git ancestry, since everything here is squash-merged and git branch --merged cannot see that. Neither deploys, restarts, writes a variable, merges, or re-runs CI, and both redact anything they echo from a third party. If verification tooling could also fix things, I would stop trusting what it reports.

Honest costs

Money first. Greptile is a per-seat subscription with a monthly allowance of reviews and a per-review charge after it, which is why the config above refuses drafts and updates. GitHub Pro is the second: a private repository on the free plan cannot enforce a ruleset, and Actions minutes on a private repository are metered. Pro includes 3,000 minutes a month against 2,000 on the free plan, and the free allowance ran out once in late August.

Claude Code itself, and the three local subagents with it, run on a Claude Max subscription, which is the third fixed cost. The custom CI reviewer bills a fourth way, through an Anthropic API key held as a repository secret, so its tokens land on the API bill rather than on that subscription. I did not measure that spend per PR; each run is capped at 32,000 tokens covering thinking and text together.

The Greptile numbers at the time of writing: $30 a seat a month, 50 reviews included, then $1 a review.

The scanners cost nothing: gitleaks, Semgrep OSS, and zizmor run from Docker images pinned by digest, with no accounts.

For scale, Anthropic's managed Code Review documents an average per review an order of magnitude above one Greptile credit. Neither it nor its cloud sibling ultrareview is in this stack. Railway hosts the product and is not counted here.

Then time. A push costs about three minutes of CI wall clock, and the full review takes one to four minutes depending on the diff. The triage gate blocks merge until a human writes a disposition. That is the point, and it is also a tax on every PR.

The pipeline has hurt itself more than once: a review that kept triggering itself, a gate that dated itself from the wrong commit, and a file left out of a review that still looked reviewed. Each incident got a fix and a test in the same PR. What I would not do again: keep three copies of the same comment-matching rule in three scripts with a comment asking humans to keep them in sync. They drifted, it cost a real defect, and only then did a test pin them byte for byte.

What I would install first with one afternoon

  1. Superpowers, plus a rigor-tier paragraph in your global instructions naming which paths get the full path. /plugin install superpowers@claude-plugins-official
  2. A PreToolUse hook on Bash that blocks force push, recursive delete, and gh pr merge, with a test file beside it. The hooks block from the settings above, plus one Python file.
  3. ast-grep with one rule for your first "one shared implementation" invariant, and its test. pnpm add -D @ast-grep/cli, then pnpm exec ast-grep test && pnpm exec ast-grep scan in CI.
  4. A repository ruleset as code with your checks pinned to the Actions app, and one required commit status, posted by an Actions workflow that checks out the default branch, that stays red until a comment containing a marker post-dates the last code push. A JSON file and fifty lines of shell.
  5. CodeGraph, so the model reads structure before it greps. npm i -g @colbymchenry/codegraph && codegraph init

Beyond Claude Code itself, nothing on that list needs an account or a subscription, except the ruleset, which on a private personal repository needed GitHub Pro.

If you run a gate I do not, I want to know what it caught.

Bram van Gestel is the founder of AgentLabs, an agentic transformation studio, after twenty-plus years building and scaling enterprise SaaS and consumer platforms as a hands-on CTO, CDO, and Director of Engineering. He ships a production multi-tenant SaaS with coding agents doing most of the typing, and writes up what it takes so others can do the same.

Meer inzichten

Wilt u deze discipline op uw eigen systemen?

Wij maken van bedrijfsprocessen onderhoudbare, mens-geregisseerde AI-systemen, met governance vanaf dag één ingebouwd.