Behavioral Governance

Claude Code guardrails: practical controls for tool use, logging, and policy enforcement

For developers running autonomous coding agents in real repos

If you are looking for claude code guardrails, the problem is usually not prompt quality. The real issue is execution control: what commands the agent can run, where it can write, and how you inspect what happened after the fact. Claude Code is useful, but once it starts invoking tools against a live repository, you need a policy layer outside the model.

A workable approach is to wrap each Claude process with a supervisor that can inspect tool calls, log them, and block actions that violate your rules. That is what agentcheck does. It adds hook-based guardrails around Claude Code, writes every tool call to JSONL, and loads YAML rule packs that define what is allowed or denied.

What guardrails should actually cover?

For a developer workflow, useful Claude Code guardrails are concrete and narrow. They should answer questions like:

That last point matters. Without logs, a blocked or failed run is hard to debug, and a successful run is hard to trust. You want enforcement and observability together, not as separate systems.

Install and run agentcheck

The install path is simple. AgentCheck is distributed directly from GitHub:

npm install -g github:paprika-org/agentcheck

From there, the typical workflow is:

# Example: run Claude Code behind agentcheck with a rules file
agentcheck --rules ./agentcheck.rules.yml -- claude

The exact invocation depends on how you launch Claude Code in your environment, but the pattern is the same: agentcheck sits in front, loads a rule pack, and intercepts tool activity.

Start with a minimal rule pack

The easiest mistake is writing a giant policy before you know what the agent actually does. Start with a short rule pack that blocks obviously risky behavior and lets normal coding work continue.

version: 1

rules:
  - id: deny-destructive-shell
    tool: shell
    match:
      command_regex: '(rm -rf|git reset --hard|sudo )'
    action: deny
    message: 'Destructive or privileged shell command blocked'

  - id: deny-secrets
    tool: read
    match:
      path_regex: '(\.env|id_rsa|credentials\.json)'
    action: deny
    message: 'Secret material is off limits'

  - id: restrict-writes
    tool: write
    match:
      path_regex: '^(/workspace/my-repo/|src/|tests/)'
    action: allow

  - id: deny-other-writes
    tool: write
    action: deny
    message: 'Writes outside approved paths are blocked'

This does not make the agent safe in every context, but it closes off common failure modes. It also forces you to be explicit about scope. If the agent only needs src/ and tests/, do not leave the whole filesystem available.

Use the logs as part of development

One reason people ask for Claude Code guardrails is that they want post-run accountability, not just prevention. AgentCheck logs every tool call to JSONL, which is much easier to query than free-form terminal output.

{"ts":"2026-04-23T09:10:12Z","tool":"read","path":"src/parser.ts","result":"ok"}
{"ts":"2026-04-23T09:10:14Z","tool":"shell","command":"npm test","result":"ok","exit_code":0}
{"ts":"2026-04-23T09:10:18Z","tool":"write","path":"src/parser.ts","result":"denied","rule_id":"deny-other-writes"}

With logs like that, you can answer practical questions quickly:

You can process the JSONL with standard tools:

# Show denied actions
jq 'select(.result == "denied")' .agentcheck/log.jsonl

# Count shell commands by exit code
jq -r 'select(.tool == "shell") | [.exit_code] | @tsv' .agentcheck/log.jsonl | sort | uniq -c

Good guardrails are iterative

A useful pattern is to begin in a mostly permissive mode, inspect the logs, and then tighten the rules based on observed behavior. For example, if the agent keeps trying to read your entire home directory to find config files, add a deny rule. If it only needs test execution, restrict shell to a small command set.

version: 1

rules:
  - id: allow-safe-shell
    tool: shell
    match:
      command_regex: '^(npm test|npm run lint|pnpm test|pytest)$'
    action: allow

  - id: deny-all-other-shell
    tool: shell
    action: deny
    message: 'Shell access is limited to test and lint commands'

This is more reliable than asking the model to “be careful.” Model instructions are advisory. External hooks are enforcement.

Honest limitation: guardrails depend on the tools you can actually intercept. If an agent can escape the wrapped execution path, your policy will not see that behavior. Keep the wrapper on the critical path and verify your launch method.

Where these controls help most

In practice, Claude Code guardrails are most valuable in a few specific situations:

They are less useful if your environment is already fully sandboxed and disposable, or if you only run the agent manually for tiny one-off edits. Even then, logging is still valuable because it gives you a real execution trace instead of guesswork.

The practical goal is not “perfect safety.” It is tighter operational control: narrower permissions, reproducible logs, and explicit policy at the process boundary.

What agentcheck does and does not do

AgentCheck is a governance layer, not a replacement for OS sandboxing, container isolation, or human review. It helps constrain agent behavior and record it. You should still use normal engineering controls: separate credentials, least privilege, branch protections, and isolated environments for risky work.

That said, if your current setup is “run Claude Code and hope the prompt is enough,” adding hook-based rules and JSONL audit logs is a substantial improvement. It gives you something concrete to enforce and something concrete to inspect.

If you need claude code guardrails that are explicit, inspectable, and easy to version with your repo, start with AgentCheck. Install it with npm install -g github:paprika-org/agentcheck and review the source at github.com/paprika-org/agentcheck.