Guard rails: the brake layer between model and action
VIGuard rails: lớp phanh giữa model và hành động · Read in Vietnamese
When an agent can read files, run shell commands, edit code and ship PRs, one line of instruction is not enough. You need a check layer before the tool runs.
Inside ClaudeKit · Guard rails deep dives
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.
4 - guard layers · 7 - guard rail groups · 2 - config layers · 8 - known gaps · 13 - sections
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
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 rail | Instruction | |
|---|---|---|
| Runs where | Harness, outside the model | In context, model reads it |
| Enforcement | Code blocks for real | Model voluntarily complies |
| Long context | Still runs | Easy to forget / slip |
| Model misreads | Still blocks (if no crash) | Drifts with the model |
| Modification | Touch code/config | Edit text and done |
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.
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
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 2— block. 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
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.
| Group | Mechanism | Example | Enforcement |
|---|---|---|---|
| Block file/path | PreToolUse | scout-block | code · fail-open |
| Block wrong step | UserPromptSubmit | simplify-gate | code |
| Inject context | UserPromptSubmit | dev-rules-reminder | code |
| Keep names clean | Pre/Post/Stop | descriptive-name | code · warn |
| Hard-gate skill | XML markdown | <HARD-GATE> | instruction |
| Instruction rule | CLAUDE.md | review-audit | instruction |
| Guard skill | User invokes | ck:security-scan | user |
Where each group is documented
Block file/path - scout-block, privacy-block · Section 05-06 · Block wrong step - simplify-gate, workflow gate · Section 07-08 · Inject context - dev-rules-reminder injects text into prompt · Section 10 · Keep names clean - descriptive-name · hook grid in Section 04 · Hard-gate skill - <HARD-GATE> · Section 09 · Instruction rule - CLAUDE.md rules are the injected content · Section 10 · Guard skill - ck:security-scan, ck:ship · Section 11
9 familiar situations
| Situation | Handling layer |
|---|---|
Read node_modules/react/ | scout-block, exit 2 |
Read .env to check a key | privacy-block, exit 2 + approval prompt |
Glob **/*.ts at root | scout-block broad-pattern |
| Ship prompt when diff has ballooned | simplify-gate, if gate.enabled=true |
| Start coding before a plan/review exists | ck:cook HARD-GATE |
| Change a threshold the user has already confirmed | review-audit rule: ask before changing |
| New file with an ambiguous name | descriptive-name, PreToolUse(Write) |
| Plan uses wrong format for links/text | plan-format-kanban, PostToolUse(Edit/Write/MultiEdit) |
| Two teammates touching the same file | team-coordination rule, Agent Team only |
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/*.cjssettings.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:
| Label | Meaning during audit |
|---|---|
script file | The .cjs file exists in .claude/hooks/. Having the file does not mean the hook is running. |
wired | settings.json has attached the script to a Claude Code lifecycle event. Without wiring, Claude Code never calls that hook. |
runtime flag | Once 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-init · wired
SessionStart · runtime flag true
Project/env setup on startup, resume, clear, compact.
usage-quota-cache-refresh · wired
SessionStart · UserPromptSubmit · PostToolUse · no flag
Does not read its own hook flag; runs when wired.
simplify-gate · gate off
UserPromptSubmit · runtime flag true
Script is called, but simplify.gate.enabled defaults to false.
dev-rules-reminder · wired
UserPromptSubmit · runtime flag true
Injects dev rules/context.
subagent-init · wired
SubagentStart · runtime flag true
Injects context for subagent.
descriptive-name · wired
PreToolUse(Write) · runtime flag true
Prompts meaningful file/script naming.
scout-block · wired
PreToolUse(Bash/Glob/Grep/Read/Edit/Write) · runtime flag true
Blocks heavy dirs and overly broad globs.
privacy-block · wired
PreToolUse(Bash/Glob/Grep/Read/Edit/Write) · runtime flag true · privacyBlock true
Blocks secret paths, requests approval.
plan-format-kanban · wired
PostToolUse(Edit/Write/MultiEdit) · no flag
Does not read its own hook flag; warns on plan format.
session-state · wired
PostToolUse · SubagentStop · Stop · runtime flag true
Writes session/task state.
cook-after-plan-reminder · wired
SubagentStop(Plan) · missing key = enabled
No dedicated key in DEFAULT_CONFIG.hooks, but isHookEnabled() only disables when the flag is false.
workflow-artifact-gate · not wired
runtime flag false
Opt-in for artifact gate: hook must be wired and flag/gate config must be enabled.
task-completed-handler · removing
TaskCompleted · preparing removal in 2.19.2-beta
Agent Teams task completed; do not treat it as durable after 2.19.1.
teammate-idle-handler · removing
TeammateIdle · preparing removal in 2.19.2-beta
Agent Teams teammate idle; do not treat it as durable after 2.19.1.
team-context-inject · not wired
missing key = enabled
No dedicated key in DEFAULT_CONFIG.hooks; only meaningful when the workflow/team layer calls it.
usage-context-awareness · not 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
}
}
}| Goal | Where to edit | Note |
|---|---|---|
| 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.json · wire 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.
| Hook | Manual trigger | Notes |
|---|---|---|
session-init | Open, resume, clear, or compact a Claude Code session. | SessionStart(startup|resume|clear|compact). |
usage-quota-cache-refresh | Open a session, send a new prompt, or update a Task/Todo. | Cache usage; does not read its own hook flag. |
simplify-gate | Send 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-reminder | Send a new prompt. | UserPromptSubmit; injects rules with TTL. |
subagent-init | Start a subagent via Task/agent flow. | SubagentStart. |
descriptive-name | Let Claude attempt to create a file with Write. | PreToolUse(Write); prompts for a clear file name. |
scout-block | Let Claude read node_modules, dist, or a too-broad glob. | PreToolUse on Bash/Glob/Grep/Read/Edit/Write. |
privacy-block | Let Claude read .env, a key file, or a secret path. | PreToolUse; also checks privacyBlock. |
plan-format-kanban | Let Claude Edit/Write/MultiEdit a plan file. | PostToolUse; warns on format, does not read its own hook flag. |
session-state | Update a Task/Todo, end a subagent, or end a turn. | PostToolUse, SubagentStop, Stop. |
cook-after-plan-reminder | Let a Plan subagent finish. | SubagentStop(Plan); missing key is still enabled. |
workflow-artifact-gate | Wire into UserPromptSubmit/PreToolUse(Bash), or run the script with --stage. | Fresh install does not auto-trigger hook mode. |
task-completed-handler | Complete a task in Agent Teams. | TaskCompleted; preparing removal in claudekit-engineer@2.19.2-beta. |
teammate-idle-handler | Let a teammate in Agent Teams finish their work and go idle. | TeammateIdle; preparing removal in claudekit-engineer@2.19.2-beta. |
team-context-inject | Wire into SubagentStart, then start a team subagent. | Script is meaningful when the agent id belongs to a team. |
usage-context-awareness | Wire 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"
}
}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
coverageImportant 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.
Guard group · Block sensitive file/path
privacy-block — block secrets, require explicit user approval
Agent touches .env, id_rsa, *.pem, credentials.yaml — secrets 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.
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.
400 - total diff LOC \> · 8 - files touched \> · 200 - added 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.
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.
| Artifact | Phase | Content |
|---|---|---|
context-snippets.json | scout/plan | Code snippets read |
risk-gate.json | predict | High-risk flag, auto-stop |
verification.json | fix/cook | 5-point checklist |
review-decision.json | code-review | Reviewer verdict |
adversarial-validation.json | adversarial | Adversarial 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.
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.
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.
| File | Guards against | Characteristic |
|---|---|---|
review-audit-self-decision | Audit reversing a confirmed decision | verified sticky |
development-rules | Skip tests, fake data | YAGNI/KISS/DRY |
team-coordination-rules | Two agents editing the same file | ownership glob |
commit-messages | Long commit body | single-line |
orchestration-protocol | Pass full history | context 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.
Guard group · Guard skill invoked by user
Guard skills — guard layers the user invokes
| Skill | When to use | What it catches |
|---|---|---|
ck:security-scan | Pre-release | Secret, CVE, SQLi, XSS, path traversal |
ck:predict | Before risky feature | 5 personas, GO/CAUTION/STOP |
ck:scenario | Pre-impl | 12-dimension edge case sweep |
ck:ship | Pre-PR | Stop 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
Known gaps
Don't misread guard rails
- File-access guard lives in hooks → fresh CK install relies on
scout-block/privacy-block, not a separatepermissions.denylist for secrets/heavy dirs - Bash is exempt from privacy-block → only warns, does not go through approval flow
- workflow-artifact-gate requires opt-in → the strongest gate does not run unless enabled
- simplify-gate does not block by default → must be explicitly enabled
- simplify-gate can be slipped with phrasing → "ship on Friday" is ignored
- .ckignore can negate node_modules → verify directly
- Rules are instructions → Claude can drift
- 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.
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
- Which hooks is
settings.jsoncurrently attaching to which lifecycle events? - Which hooks has
.ck.jsondisabled? (outer + inner config) - Is the current Claude Code session running with permission prompts suppressed?
- Is there a project
.ckignoreoverride? Isnode_modules!-allowlisted? - Are rule files in CLAUDE.md complete?
- Has any hook crashed silently in this session (check stderr)?
Glossary
Quick terms
| Term | Short definition |
|---|---|
| harness | The orchestration layer between model and machine: sends tool commands, manages permissions, runs hooks. Where guard rails attach. |
| hook | A small script the harness calls at fixed points in the prompt/tool/session lifecycle to intercept, check, or write state. |
| lifecycle event | The points where hooks are called: UserPromptSubmit (prompt received), PreToolUse (before tool), PostToolUse (after tool), Stop/SubagentStop (session/subagent ends). |
| exit code | The number a process returns on exit. 0 allows, 2 blocks, 1 reports an error but does not block. |
| fail-open | When a hook errors/crashes, the harness lets the tool run instead of blocking. Guard silently disables. |
| transcript | The complete record of the conversation between the user and the agent in a session. |
| context | The information space the model "sees" when reasoning. Full of junk files → higher cost, lower quality. |
| glob | A path-matching pattern. **/*.ts = every .ts file in every subdirectory. |
| secret | Sensitive information: API keys, private keys, credentials. Leaking into the transcript is a risk. |
| LOC | Lines of code — line count, used to measure diff size. |
| artifact | JSON 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 30% off.
Inside ClaudeKit · Guard rails deep dives · claudekit-engineer@2.19.1
$ article.get locale=en slug=guard-rails-in-claude-code-and-claudekit # REST or MCP · use locale=vi for the Vietnamese edition
- Version
- v1.0.0
- Updated
- 24/07/2026
- Licence
- All rights reserved
- Slug
- guard-rails-in-claude-code-and-claudekit