VI EN ZH JA
Inside ClaudeKit · No. 03claudekit-engineer@2.19.1
Inside ClaudeKit · Guard rails deep dives

Guard rails: the brake layer between model and action

When an agent has permission to read files, run shell commands, edit code, and ship PRs, a single line of instruction in the prompt is not enough. You need a check layer before the tool actually runs.

4guard layers
7guard rail groups
2config layers
8known gaps
13sections
Case

A small task, far too many permissions

You give the agent a small task: fix validation in a module. To be safe it scans a few extra directories, opens a sensitive config file, then conveniently bundles some unrelated cleanup into the same PR.

At first glance everything looks fine. Tests still pass. But context has been polluted with junk files, secrets may have entered the transcript (the complete record of the conversation between you and the agent), and the review is diluted because the intended change and the "convenient" additions sit in the same PR.

Without guard rails

  • Glob too broad — dirty context
  • cat .env — secret enters transcript
  • Cleanup out of scope — review diluted
  • Tests pass so work continues — no scope check step

With guard rails

  • Broad glob — scout-block exit 2
  • .env — privacy-block asks user
  • Scope drift — rule/hard-gate redirects back to main task
  • Before shipping — simplify/review gate re-checks diff

The agent doesn't naturally become more careful. It's just that before it does anything, there is a layer that asks: should this action be allowed to run?

Key point

Hooks are the real blocking layer.

Guard rails don't make the model smarter. They check actions before the tool runs. Rules, hard-gates, and guard skills are additional orientation layers — useful, but still dependent on whether the model or user invokes them.

I. Concepts

Section 01

What are guard rails in agentic coding

A guard rail is the layer that stands between the agent and an action: blocking risky operations, warning on suspicious ones. It is like a railing on the edge of a staircase: it doesn't make you walk better, but it reduces the chance that a misstep becomes an accident. Unlike instructions in the prompt, i.e. prompt instructions, guard rails run at the tool-orchestration layer and do not depend on the model "remembering" them.

Guard rail vs. instruction

Guard railInstruction
Runs whereHarness, outside the modelIn context, model reads it
EnforcementCode blocks for realModel voluntarily complies
Long contextStill runsEasy to forget / slip
Model misreadsStill blocks (if no crash)Drifts with the model
ModificationTouch code/configEdit text and done
Section 02

How guard rails work

Harness is the runtime layer wrapping the model. The model only decides "I want to read this file" or "I want to run this command"; it is the harness that receives the tool call, manages permissions, invokes hooks, lets the tool run or blocks it, then returns the result to the model. Hooks are small scripts the harness calls at fixed points in a tool call's lifecycle — on prompt receipt, before a tool, after a tool — to intervene and check. Because every real action goes through the harness, this is where guard rails attach.

For conceptual background, read: What is Harness Engineering? by Duy /zuey/.

ClaudeKit Hooks Map

For more detail on ClaudeKit Hooks, see VividKit Guides.

CLAUDE CODE LIFECYCLE User prompt task / intent Model wants to call tool UserPromptSubmit simplify-gate · dev-rules-reminder PreToolUse scout-block · privacy-block Tool actually runs Read · Bash · Edit · Write PostToolUse / Stop plan-format · session-state allow exit 0 · tool runs block exit 2 · returns reason inject additionalContext remember logs / state settings.json wire event .ck.json runtime flag ENV override payload: event · tool · input · cwd

Hooks do not live inside the model. They live in Claude Code's lifecycle: settings.json decides which hooks are called, .ck.json/ENV decides runtime behavior, and hook output can allow, block, inject context, or write state/artifacts after a tool or session ends.

Flow of a tool call through guard rails

HARNESS RUNTIME User prompt Prompt gate UserPromptSubmit · block? pass Model decides to call tool tool call: Read / Bash / Edit Pre-tool hook PreToolUse · scout / privacy exit 0 (allow) Tool exec read / write / run for real result Post-tool hook PostToolUse · validate, scan Result back to Model block Block prompt user to fix prompt exit 2 Tool does NOT run returns reason to model

The key point: a pre-tool hook returns an exit code. Exit 0 lets the tool run; exit 2 blocks and pushes the reason back to the model. The three main injection points in the flow have corresponding technical names: UserPromptSubmit (on prompt receipt), PreToolUse (before a tool), PostToolUse (after a tool). Stop/SubagentStop run when a session or subagent ends, so they appear on the lifecycle map above.

What exit codes mean

An exit code is the number a process returns when it exits. Claude Code reads it to decide whether a tool runs:

  • exit 0 — OK, tool runs. stdout is read as JSON output.
  • exit 2block. Discard stdout, push stderr back to the model as an error message.
  • exit 1 (or other codes) — error but does NOT block. Reports the error and the tool still runs.

Only exit 2 actually blocks. A hook that wants to enforce policy must use exit 2 precisely — accidentally using exit 1 out of Unix habit means the guard appears to be on but is actually open.

Two kinds of blocking

Hard · code

Hook exits 2, tool does not run. But if the hook crashes, Claude Code lets it through (fail-open). A bug in a hook silently disables that guard.

Soft · text

Adds text to context (rule, hard-gate). The model must comply on its own. Strength depends on the model.

Benefits
  • Keeps context clean · keeps secrets out of transcript
  • Keeps the rhythm: plan → simplify → review
  • Blocks early before risk spreads — agent about to read .env, scan too broadly, or pull out-of-scope cleanup into a PR
  • Multiple layers compensate for each other — a hook can fail-open, a rule can be ignored by the model, a guard skill may not be called; splitting into multiple layers reduces dependence on a single blocking point

II. Reality in CK

Section 03

Seven guard rail groups in CK

Important note

After a fresh CK install, file-access guards live in PreToolUse hooks such as scout-block and privacy-block. If the Claude Code session is running with permission prompts suppressed, these hooks are still CK's primary blocking layer. But CK hooks are fail-open: if they crash or are disabled, the tool call may proceed.

GroupMechanismExampleEnforcement
Block file/pathPreToolUsescout-blockcode · fail-open
Block wrong stepUserPromptSubmitsimplify-gatecode
Inject contextUserPromptSubmitdev-rules-remindercode
Keep names cleanPre/Post/Stopdescriptive-namecode · warn
Hard-gate skillXML markdown<HARD-GATE>instruction
Instruction ruleCLAUDE.mdreview-auditinstruction
Guard skillUser invokesck:security-scanuser

Where each group is documented

Block file/pathscout-block, privacy-block · Section 05-06
Block wrong stepsimplify-gate, workflow gate · Section 07-08
Inject contextdev-rules-reminder injects text into prompt · Section 10
Keep names cleandescriptive-name · hook grid in Section 04
Hard-gate skill<HARD-GATE> · Section 09
Instruction ruleCLAUDE.md rules are the injected content · Section 10
Guard skillck:security-scan, ck:ship · Section 11

9 familiar situations

SituationHandling layer
Read node_modules/react/scout-block, exit 2
Read .env to check a keyprivacy-block, exit 2 + approval prompt
Glob **/*.ts at rootscout-block broad-pattern
Ship prompt when diff has balloonedsimplify-gate, if gate.enabled=true
Start coding before a plan/review existsck:cook HARD-GATE
Change a threshold the user has already confirmedreview-audit rule: ask before changing
New file with an ambiguous namedescriptive-name, PreToolUse(Write)
Plan uses wrong format for links/textplan-format-kanban, PostToolUse(Edit/Write/MultiEdit)
Two teammates touching the same fileteam-coordination rule, Agent Team only
Section 04
Config map · where to verify a hook is really running

Settings and config layers

Two config layers determine which guards run: settings.json attaches hooks to Claude Code's lifecycle; .ck.json enables/disables individual hooks and adjusts thresholds.

# Global scope
~/.claude/settings.json
~/.claude/.ck.json
~/.claude/hooks/*.cjs
# Project scope
.claude/settings.json
.claude/.ck.json
.claude/hooks/*.cjs

settings.json: which hook runs at which point

The snippet below illustrates just one slice of PreToolUse from a fresh CK install: CK provides scout-block and privacy-block, then wires them into the lifecycle so Claude Code calls them before a tool runs. The full hook config on an installed machine has more lifecycle events; the place to check is Claude Code's settings.json.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Glob|Grep|Read|Edit|Write",
        "hooks": [
          { "command": "node \".claude/hooks/scout-block.cjs\"" },
          { "command": "node \".claude/hooks/privacy-block.cjs\"" }
        ]
      }
    ]
  }
}

settings.json is the lifecycle map: which hook runs when the user submits a prompt, which is called before a tool runs, which handles things after a tool finishes. For a global install the CLI rewrites relative commands in the template to the form node "$HOME/.claude/hooks/scout-block.cjs"; for a project install, paths usually stay as .claude/hooks/....

Reading the hook table with 3 labels

Don't collapse everything into "on by default / off by default". The state of a hook has three distinct layers:

LabelMeaning during audit
script fileThe .cjs file exists in .claude/hooks/. Having the file does not mean the hook is running.
wiredsettings.json has attached the script to a Claude Code lifecycle event. Without wiring, Claude Code never calls that hook.
runtime flagOnce the hook is called, the code inside reads .ck.json/DEFAULT_CONFIG/ENV to decide whether to proceed. Some guards also have sub-switches such as privacyBlock or simplify.gate.enabled.

The table below is a snapshot of the hook list for stable claudekit-engineer@2.19.1. Update 2026-06-09: claudekit-engineer@2.19.2-beta is preparing to remove task-completed-handler and teammate-idle-handler; when upstream ClaudeKit releases a new version, this list may become 14 hooks.

session-initwired
SessionStart · runtime flag true

Project/env setup on startup, resume, clear, compact.

usage-quota-cache-refreshwired
SessionStart · UserPromptSubmit · PostToolUse · no flag

Does not read its own hook flag; runs when wired.

simplify-gategate off
UserPromptSubmit · runtime flag true

Script is called, but simplify.gate.enabled defaults to false.

dev-rules-reminderwired
UserPromptSubmit · runtime flag true

Injects dev rules/context.

subagent-initwired
SubagentStart · runtime flag true

Injects context for subagent.

descriptive-namewired
PreToolUse(Write) · runtime flag true

Prompts meaningful file/script naming.

scout-blockwired
PreToolUse(Bash/Glob/Grep/Read/Edit/Write) · runtime flag true

Blocks heavy dirs and overly broad globs.

privacy-blockwired
PreToolUse(Bash/Glob/Grep/Read/Edit/Write) · runtime flag true · privacyBlock true

Blocks secret paths, requests approval.

plan-format-kanbanwired
PostToolUse(Edit/Write/MultiEdit) · no flag

Does not read its own hook flag; warns on plan format.

session-statewired
PostToolUse · SubagentStop · Stop · runtime flag true

Writes session/task state.

cook-after-plan-reminderwired
SubagentStop(Plan) · missing key = enabled

No dedicated key in DEFAULT_CONFIG.hooks, but isHookEnabled() only disables when the flag is false.

workflow-artifact-gatenot wired
runtime flag false

Opt-in for artifact gate: hook must be wired and flag/gate config must be enabled.

task-completed-handlerremoving
TaskCompleted · preparing removal in 2.19.2-beta

Agent Teams task completed; do not treat it as durable after 2.19.1.

teammate-idle-handlerremoving
TeammateIdle · preparing removal in 2.19.2-beta

Agent Teams teammate idle; do not treat it as durable after 2.19.1.

team-context-injectnot wired
missing key = enabled

No dedicated key in DEFAULT_CONFIG.hooks; only meaningful when the workflow/team layer calls it.

usage-context-awarenessnot wired
runtime flag true

Usage/context injection hook; not in settings.json template.

Easy to miss: isHookEnabled() only disables when hooks.<name> is false. A missing key is usually treated as enabled. But some guards also have their own switches: privacy-block still reads the legacy key privacyBlock=false, while simplify-gate has simplify.gate.enabled and ENV CK_SIMPLIFY_DISABLED=1.

Manually enabling/disabling and self-testing hooks

For quick adjustments, edit .ck.json at the scope you want to affect. Project config takes priority over global when a key is explicitly set; missing keys continue to inherit/default. For hooks already wired in settings.json, setting false turns them off; setting true re-enables them after a higher scope has disabled them. The hook name in the Hook column is also the key used in hooks.<name>.

// .claude/.ck.json or ~/.claude/.ck.json
{
  "hooks": {
    "scout-block": false,
    "privacy-block": true
  },
  "privacyBlock": true,
  "simplify": {
    "gate": {
      "enabled": true
    }
  }
}
GoalWhere to editNote
Disable a wired hook
.ck.json{"hooks":{"scout-block":false}}
Hook script remains, but runtime lets it through.
Re-enable a hook disabled globally
project .ck.json{"hooks":{"scout-block":true}}
Explicit local key overrides global.
Disable privacy guard
.ck.json{"hooks":{"privacy-block":false}}
.ck.json (legacy key){"privacyBlock":false}
privacyBlock=false is the legacy key but still takes effect.
Enable simplify-gate to actually block
.ck.json{ "hooks": { "simplify-gate": true }, "simplify": { "gate": { "enabled": true } } }
Without simplify.gate.enabled=true, the gate's blocking mode is not active.
Disable simplify-gate for a session/scope
settings.json{ "env": { "CK_SIMPLIFY_DISABLED": "1" } }
Env override is stronger than config gate.
Enable workflow-artifact-gate
settings.jsonwire UserPromptSubmit / PreToolUse(Bash)
.ck.json{"hooks":{"workflow-artifact-gate":true}}
Fresh install does not wire it by default, so editing <code>.ck.json</code> alone is not enough.

To know whether a hook gets triggered, look at the lifecycle event, not the file name. The same hook script only runs when the event/matcher in settings.json matches the current action. The table below is a quick-check for each hook; not wired hooks need to be wired first, or called via manual CLI if the script supports it.

HookManual triggerNotes
session-initOpen, resume, clear, or compact a Claude Code session.SessionStart(startup|resume|clear|compact).
usage-quota-cache-refreshOpen a session, send a new prompt, or update a Task/Todo.Cache usage; does not read its own hook flag.
simplify-gateSend a prompt with ship/merge/pr/deploy/publish intent when diff is large enough.Requires simplify.gate.enabled=true; does not block by default.
dev-rules-reminderSend a new prompt.UserPromptSubmit; injects rules with TTL.
subagent-initStart a subagent via Task/agent flow.SubagentStart.
descriptive-nameLet Claude attempt to create a file with Write.PreToolUse(Write); prompts for a clear file name.
scout-blockLet Claude read node_modules, dist, or a too-broad glob.PreToolUse on Bash/Glob/Grep/Read/Edit/Write.
privacy-blockLet Claude read .env, a key file, or a secret path.PreToolUse; also checks privacyBlock.
plan-format-kanbanLet Claude Edit/Write/MultiEdit a plan file.PostToolUse; warns on format, does not read its own hook flag.
session-stateUpdate a Task/Todo, end a subagent, or end a turn.PostToolUse, SubagentStop, Stop.
cook-after-plan-reminderLet a Plan subagent finish.SubagentStop(Plan); missing key is still enabled.
workflow-artifact-gateWire into UserPromptSubmit/PreToolUse(Bash), or run the script with --stage.Fresh install does not auto-trigger hook mode.
task-completed-handlerComplete a task in Agent Teams.TaskCompleted; preparing removal in claudekit-engineer@2.19.2-beta.
teammate-idle-handlerLet a teammate in Agent Teams finish their work and go idle.TeammateIdle; preparing removal in claudekit-engineer@2.19.2-beta.
team-context-injectWire into SubagentStart, then start a team subagent.Script is meaningful when the agent id belongs to a team.
usage-context-awarenessWire into the desired event, or use the hook cache refresh already wired.Legacy wrapper around usage quota cache refresh.
// ~/.claude/settings.json or .claude/settings.json
{
  "env": {
    "CK_SIMPLIFY_DISABLED": "1"
  }
}
Section 05
Guard group · Block file/path

scout-block — block reads of junk directories

Agents often read node_modules/, glob **/*.ts at root (glob is a path-matching pattern; **/*.ts means every .ts file in every subdirectory), or cat dist/index.js. Each time that's tens of thousands of tokens stuffed into context, driving up cost and degrading the quality of subsequent turns. scout-block.cjs registers PreToolUse and runs before every Read/Bash/Glob/Grep.

# Baseline .ckignore — processed by pattern-matcher.cjs
node_modules
dist
build
.next
.nuxt
__pycache__
.venv
venv
vendor
target
.git
coverage
Important allowlist

Build commands are allowed through. Commands such as npm build, go build, make, docker build still run, even when the build process touches node_modules or dist. Blocking this group would cause CK's ship/test flows to stumble at the build step.

Gap #1 — when the hook crashes

Fail-open: parse errors all result in exit 0, letting the tool through. A bug in the hook silently disables that guard layer.

Section 06
Guard group · Block sensitive file/path

privacy-block — block secrets, require explicit user approval

Agent touches .env, id_rsa, *.pem, credentials.yamlsecrets leak into the transcript. Once in context, they can be logged, quoted in reviewer output, or pasted into a PR.

@@PRIVACY_PROMPT_START@@
{ "type": "PRIVACY_PROMPT", "question": {...}, "options": [...] }
@@PRIVACY_PROMPT_END@@

Claude parses JSON and calls AskUserQuestion. If the user approves, the hook message instructs reading the file through an approved path, e.g. cat ".env" after asking; the code also supports the APPROVED: prefix. The key point: explicit approval is required — the model must not be allowed to self-interpret consent.

Gap #2 — Bash exception

privacy-block lets Bash through, only warning. Anyone with permission to run Bash can read secrets without going through the approval flow.

Warning: if a leak has already occurred

Privacy-block only blocks READs. Rotate the credential immediately, audit conversation logs, check git history to see whether the secret was committed.

Section 07
Guard group · Block wrong step

simplify-gate — block shipping when diff has ballooned past scope

A small task can balloon into a PR touching too many files: fix validation, add cleanup, change formatting, tweak a few nearby helpers. When the agent says "OK ship", simplify-gate.cjs registers UserPromptSubmit, reads git diff HEAD, and only acts when the prompt contains: ship merge pr deploy publish.

400total diff LOC >
8files touched >
200added LOC in one file >

Defaults from simplify.threshold: total added+removed lines, touched-file count, and the largest added-line count in one file. The gate only blocks when simplify.gate.enabled=true; these values can be overridden in .ck.json.

Gap #3 — default is easy to misread

hooks.simplify-gate = true only tells Claude Code to call the script. To have the script actually block PRs that have ballooned past scope, you must also enable simplify.gate.enabled = true. A fresh install does not set this flag, so the gate only does a light check and does not block.

// project .ck.json — actually enable
{ "simplify": { "gate": { "enabled": true } } }

matchedSeverity() skips "don't ship" and "ship on""ship on Friday" also passes through. To disable the gate for all sessions at that scope, set CK_SIMPLIFY_DISABLED=1 in settings.json under the env key.

Section 08
Guard group · Workflow / artifact gate

workflow-artifact-gate — 5 artifact JSONs

Full pipeline → each phase leaves a JSON file recording its decisions, like a receipt after every step. This hook is designed to attach to UserPromptSubmit + PreToolUse(Bash), but is not in the hook set that runs out of the box after a fresh install; you must opt in to use it.

ArtifactPhaseContent
context-snippets.jsonscout/planCode snippets read
risk-gate.jsonpredictHigh-risk flag, auto-stop
verification.jsonfix/cook5-point checklist
review-decision.jsoncode-reviewReviewer verdict
adversarial-validation.jsonadversarialAdversarial pass

This gate has two processing levels. At high-risk steps such as ship, push, PR, or deploy, the hook can stop the flow immediately and return the reason to the model (emitBlock()). At lighter steps such as finalize or commit, the hook lets the flow continue but injects a warning into context for the model to self-correct (emitSoft()).

Gap #4 — workflow gate requires opt-in

workflow-artifact-gate is the strongest artifact-checking layer, but a fresh install does not call it automatically. To use it, wire the hook in settings.json and enable the config in .ck.json; without this step, ship/push/PR/deploy is not gated.

Section 09
Guard group · Hard-gate skill

Hard-gate XML in skill markdown

Hooks only block tool calls. Some failures are not tool calls but sequence errors: coding before planning, fixing before scouting. CK embeds <HARD-GATE> in skill markdown.

<HARD-GATE>
Do NOT write implementation code until a plan exists
and has been reviewed. Exception: --fast skips research
but still requires a plan step. User override: if user
explicitly says 'just code it', respect their instruction.
</HARD-GATE>

ck:cook and ck:fix each have 4 hard-gates (plan/scout-first, exact-req or root-cause, no-side-effects).

Why call it "hard"

The word "hard" is easy to misread — it does not block via code. It is an instruction in the prompt. If the model ignores it, there is no exit 2 to block. The XML wrapping creates a stronger signal than a plain rule, making it harder for the model to rationalize taking a shortcut.

Section 10
Guard group · Inject context

dev-rules-reminder — bring rules into context

dev-rules-reminder.cjs registers UserPromptSubmit and injects rules text into every prompt. Two layers need to be distinguished here: the hook is the context-injection mechanism, while the rules in CLAUDE.md are the content being injected. TTL is 5 minutes keyed on (sessionId, baseDir) to avoid burning tokens.

FileGuards againstCharacteristic
review-audit-self-decisionAudit reversing a confirmed decisionverified sticky
development-rulesSkip tests, fake dataYAGNI/KISS/DRY
team-coordination-rulesTwo agents editing the same fileownership glob
commit-messagesLong commit bodysingle-line
orchestration-protocolPass full historycontext isolation
Common characteristic

Rules are text in context, not code. Claude can drift if the user's prompt overrides them. On the other hand, they are very easy to add and edit. Better suited to style conventions than security guards.

Section 11
Guard group · Guard skill invoked by user

Guard skills — guard layers the user invokes

SkillWhen to useWhat it catches
ck:security-scanPre-releaseSecret, CVE, SQLi, XSS, path traversal
ck:predictBefore risky feature5 personas, GO/CAUTION/STOP
ck:scenarioPre-impl12-dimension edge case sweep
ck:shipPre-PRStop on test fail, never force-push

code-reviewer agent runs a 9-item checklist: concurrency, error boundary, API contract, backwards compat, input validation, auth/authz, N+1, data leak, fact-check. Final guard before merge.

III. Limits & practices

Section 12

Known gaps

Don't misread guard rails
  1. File-access guard lives in hooks → fresh CK install relies on scout-block/privacy-block, not a separate permissions.deny list for secrets/heavy dirs
  2. Bash is exempt from privacy-block → only warns, does not go through approval flow
  3. workflow-artifact-gate requires opt-in → the strongest gate does not run unless enabled
  4. simplify-gate does not block by default → must be explicitly enabled
  5. simplify-gate can be slipped with phrasing → "ship on Friday" is ignored
  6. .ckignore can negate node_modules → verify directly
  7. Rules are instructions → Claude can drift
  8. HARD-GATE XML is also an instruction → has "User override"
No rate-limit / quota guard

An out-of-control agent can call thousands of Read/Glob/Bash operations. CK hooks do not count tool calls and do not self-stop when quota is nearly exhausted. Quota/cost limits live in Claude Code, the provider, or account billing — not in these guard rails.

Section 13

Best practices & pitfalls

Best practices

  • Read both settings.json + .ck.json after installing
  • Enable workflow-artifact-gate for production-shipping repos
  • To have simplify-gate actually block: set gate.enabled = true
  • Need node_modules: override with a project .ckignore
  • .env approval: read carefully, don't auto-yes

Common mistakes

  • Editing global config while thinking you're editing the repo config
  • Assuming simplify-gate is blocking, when the blocking mode is off by default
  • Assuming a hook error means the tool will be blocked; in practice a failing hook usually lets it through
  • Assuming a rule in the prompt blocks the same way a code hook does
  • Relying on privacy-block to save you after a secret has already entered a commit

6 questions to ask before trusting a guard rail

Glossary

Quick terms

TermShort definition
harnessThe orchestration layer between model and machine: sends tool commands, manages permissions, runs hooks. Where guard rails attach.
hookA small script the harness calls at fixed points in the prompt/tool/session lifecycle to intercept, check, or write state.
lifecycle eventThe points where hooks are called: UserPromptSubmit (prompt received), PreToolUse (before tool), PostToolUse (after tool), Stop/SubagentStop (session/subagent ends).
exit codeThe number a process returns on exit. 0 allows, 2 blocks, 1 reports an error but does not block.
fail-openWhen a hook errors/crashes, the harness lets the tool run instead of blocking. Guard silently disables.
transcriptThe complete record of the conversation between the user and the agent in a session.
contextThe information space the model "sees" when reasoning. Full of junk files → higher cost, lower quality.
globA path-matching pattern. **/*.ts = every .ts file in every subdirectory.
secretSensitive information: API keys, private keys, credentials. Leaking into the transcript is a risk.
LOCLines of code — line count, used to measure diff size.
artifactJSON file each pipeline phase leaves behind, recording its decisions for the next phase to check.

Read the right layer, trust to the right degree

When an agent has broad permissions, don't conclude "CK is installed so we're safe". Before trusting a guard rail, check two places: which hooks settings.json is wiring, and what .ck.json is enabling or disabling.

Each layer protects against a different kind of risk: hooks block tool calls, rules nudge behavior, hard-gates hold the workflow, guard skills only run when invoked. Knowing which layer is running and which is just an instruction helps you audit the right place — before a bug slips into a PR and makes it to PROD.

New to ClaudeKit?

If you are building a guardrails system, ClaudeKit is a useful set of applied patterns to study. If you are considering buying ClaudeKit so you can use the hooks, skills, and workflow guard rails as a ready kit instead of assembling each layer by hand, this referral link gives you 20% off.

Get 20% off
Inside ClaudeKit · Guard rails deep dives · claudekit-engineer@2.19.1