Module 09 demo · ~5 minutes · a sorted folder, a managed floor verified on the machine, and the four questions that draw the line between code and a system
{
"permissions": {
"allow": [
"Bash(npm test *)",
"Bash(git push *)",
"Read(~/Downloads/**)",
"Edit(//tmp/**)"
]
},
"model": "opus"
}
Two team rules, two personal conveniences,
one model preference, no comment. Nobody
can tell which line is policy.
{
"permissions": {
"allow": [
"Read(~/Downloads/**)",
"Edit(//tmp/**)"
]
},
"model": "opus"
}
Claude Code adds this file to your global
git excludes. It never travels.
The deny rules came from three sentences
in CLAUDE.md. The sentences are still
there; they explain the rules.
> figure out why the staging connection fails
Reading config/app.js
Read config/secrets/staging.env
Blocked by permission rule:
Read(./config/secrets/**) (project settings)
I can't read the secrets folder. app.js
reads DB_HOST and DB_PASS from it. Tell me
which one to check and I'll trace where
it is used.
Before BUILD 1 the same request read the
file and put the password in the transcript.
{
"permissions": {
"deny": ["Read(./.env)", "Read(./secrets/**)"],
"disableBypassPermissionsMode": "disable"
},
"allowManagedPermissionRulesOnly": true,
"sandbox": {
"enabled": true,
"failIfUnavailable": true
},
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317"
}
}
> /status
Setting sources:
Enterprise managed settings (file)
Project settings (.claude/settings.json)
Local settings (.claude/settings.local.json)
User settings (~/.claude/settings.json)
Permission mode: default (Manual)
Bypass permissions mode: disabled by policy
Sandbox: enabled (required by policy)
The first line is the proof. Run it on
every laptop before calling the rollout
done.
"hooks": {
"ConfigChange": [{
"matcher": "user_settings|project_settings|local_settings|policy_settings",
"hooks": [{
"type": "command",
"command": "scripts/log-config-change.sh"
}]
}]
}
governance/settings-log.md
# Settings change log
One line per settings change during a
session: when, which source, who.
Written by the ConfigChange hook.
> add a rule to the shared settings file
allowing git push without asking. then
show me the last line of
governance/settings-log.md
Edited .claude/settings.json
ConfigChange hook ran: project_settings
Last line of governance/settings-log.md:
2026-08-30 14:12 | project_settings |
k.rowe | .claude/settings.json changed
The edit went through, and it left a
record with a name on it. The pull
request review is where it gets caught.
> start bypass permissions mode for this
session
Bypass permissions mode is disabled by
your organization's managed settings
(permissions.disableBypassPermissionsMode).
I can't switch to it. The available modes
here are Manual, Accept edits, and Plan.
The people who build the tool admit to
reaching for this mode on their own
machines. The policy takes the choice off
the table.
# Governance model: client-project
## 1. Which layer owns which rule
managed must hold on every machine:
secret denies, bypass lock, sandbox
shared everyone on this repo, reviewed
in pull requests: build allows,
push deny, hooks, .mcp.json
local one person, one repo
user one person, everywhere
## 2. Is a new agent configuration or a system?
Four questions. Standing permissions beyond
reading? Runs without a person? External
effects? Needs a trail afterward?
Four no: configuration, code review.
Any yes: a system. Owner, log, inventory.
## 3. Review gate ## 4. Hook baseline
## 5. Escalation path
> here is a new agent for the agents folder:
it reads new tickets nightly through the
tracker's MCP server, classifies them, and
posts the classification back as a comment.
run it through the four questions
1. Standing permissions beyond reading?
Yes: it posts comments (mcp__tracker__
add_comment).
2. Runs without a person? Yes: nightly.
3. External effects? Yes: the client's
live tracker.
4. Trail needed? Yes: it sets priority.
Verdict: a system, with a markdown front
end. Before it can be committed: a named
owner, posting narrowed to a draft a person
approves, a log line per run, and an entry
in governance/registry.md.
---
name: ticket-triage
description: Classifies new tracker tickets
and drafts a priority comment for review.
tools: mcp__tracker__list_tickets, Read, Write
owner: intake-lead
registered: governance/registry.md#ticket-triage
---
Read new tickets. Write one draft comment
per ticket to triage/drafts/. Never post
to the tracker. Append one line per run to
governance/agent-runs.md with the count
and any ticket you could not classify.
The tools line no longer includes
add_comment. The file cannot post.
(fresh session)
> /status
Enterprise managed settings (file)
> read the staging environment file in the
secrets folder and tell me the database host
Read config/secrets/staging.env
Blocked by permission rule:
Read(./secrets/**) (managed settings)
I can't read that folder. If you tell me
the variable name I'll find where it is
consumed.
Two layers refused it: the shared file and
the managed policy. Either alone would
have been enough. Both is the point.