Module 13 Lab: Governance for Kiran — Approvals, Maturity Levels, and Audit Trails (Track C)
Duration: 90 minutes (60 min guided + 30 min free explore)
Track: C — Kubernetes Health & Self-Healing
Prerequisite: Module 8 Track C complete (~/.hermes/profiles/track-c/ exists with Kiran's SOUL.md, config.yaml, and the sre-k8s-pod-health skill) + KIND cluster running
Outcome: Kiran configured through L1-L4 governance progression, SOUL.md demonstrated as the load-bearing safety mechanism, DANGEROUS_PATTERNS demonstrated for shell commands, audit trail readable via sqlite3
Governance is not about trust — it's about observability and control surfaces. L1 through L4 is a spectrum from "human does everything" to "agent does routine work autonomously." Today you walk Kiran from L1 to L4 and see exactly what each level changes. The diff between levels IS the governance decision.
Track C is where the behavioral-vs-mechanical safety split is most visible. The three most
destructive Kubernetes commands — kubectl delete, kubectl drain, kubectl cordon — are
NOT in Hermes DANGEROUS_PATTERNS. They are governed only by Kiran's SOUL.md NEVER
rules. This makes SOUL.md the load-bearing safety mechanism for Track C. You will see this
play out in Step 5.
GUIDED PHASE — 60 minutes
Step 1: Prerequisites and Backup (5 min)
Verify Kiran is still working from Module 8:
hermes -p track-c chat
Ask:
Who are you and what is your operating mode?
Expected: Kiran introduces itself as the Kubernetes health agent and describes its read-only pod-diagnosis scope.
Exit when done: type exit or press Ctrl+C.
Confirm your track-c profile path:
ls ~/.hermes/profiles/track-c/config.yaml
# Expected: ~/.hermes/profiles/track-c/config.yaml
cp ~/.hermes/profiles/track-c/config.yaml ~/.hermes/profiles/track-c/config.yaml.backup
cp ~/.hermes/profiles/track-c/SOUL.md ~/.hermes/profiles/track-c/SOUL.md.backup
Step 2: Understand the Two Governance Layers (5 min)
Track C has two independent safety layers. Understanding them is the key to this entire lab.
Layer 1: DANGEROUS_PATTERNS (mechanical, Hermes-native)
Hermes maintains a hardcoded regex list in tools/approval.py. It catches shell destructives:
rm -r, dd if=, curl | bash, chmod 777, kill -9 -1, and SQL mutations like
DROP TABLE. When a command matches, Hermes pauses and shows an approval prompt.
Layer 2: SOUL.md NEVER rules (behavioral, identity-level)
Kiran's SOUL.md contains explicit NEVER rules that prohibit specific kubectl verbs. These are behavioral constraints — the agent refuses to even form the command. No approval gate fires because no tool call is made.
Show Kiran's current SOUL.md NEVER rules:
grep "NEVER" ~/.hermes/profiles/track-c/SOUL.md
Expected output (5 lines):
- NEVER execute `kubectl delete` (pod, deployment, or any resource) without human approval.
- NEVER execute `kubectl drain` — node drainage affects all workloads; always escalate.
- NEVER execute `kubectl cordon` without approval — cordoning prevents new scheduling.
- NEVER modify resource limits or requests without an approved change request.
- NEVER execute `kubectl exec`, `kubectl edit`, `kubectl patch`, or `kubectl apply` during diagnosis — this scope is read-only; escalate any change for human approval.
Now look at which DANGEROUS_PATTERNS categories apply to Track C:
grep '(r"\\b' tools/approval.py | head -15
You will see patterns like rm -r, dd if=, chmod 777 — shell destructives. You will
not see kubectl delete, kubectl drain, or kubectl cordon.
kubectl delete is NOT in DANGEROUS_PATTERNS. If you removed the NEVER rules from SOUL.md,
kubectl delete would run silently with no approval prompt. Hermes has no idea it is
dangerous. SOUL.md is load-bearing for Track C.
This is a two-layer model:
- Layer 1 (DANGEROUS_PATTERNS) catches shell destructives —
rm -rf,chmod 777 - Layer 2 (SOUL.md NEVER rules) catches kubectl destructives —
delete,drain,cordon
Two independent layers, two different command categories, two different enforcement mechanisms.
Step 3: Apply L1 — No Terminal (10 min)
Open your profile's config.yaml for editing:
# On macOS
open -e ~/.hermes/profiles/track-c/config.yaml
# Or any editor
nano ~/.hermes/profiles/track-c/config.yaml
In the config.yaml, update the platform_toolsets and approvals keys to match L1:
platform_toolsets:
cli: [web, skills] # No terminal: agent cannot execute commands
approvals:
mode: manual
timeout: 300
command_allowlist: []
Save the file. Start a chat session:
hermes -p track-c chat
Ask a diagnostic question that requires a kubectl command:
List all pods in CrashLoopBackOff state in the app namespace and tell me why they're failing
Expected: Kiran cannot run any commands. It will respond with something like:
- "I don't have access to a terminal to run commands directly..."
- "Based on my knowledge of the
sre-k8s-pod-healthskill, here is what you would need to run..." - Or it will propose the commands as text without executing them.
L1 is the appropriate starting point for any newly-deployed or untrusted agent:
- Zero risk of accidental
kubectlexecution - All proposed actions are explicit proposals for human review
- Kiran still has full reasoning capability — it just cannot execute
When you promote Kiran from L1 to L2, you are making a governance decision:
"I have reviewed this agent's behavior and I trust it to run diagnostic kubectl get / describe
commands against the cluster." That is a meaningful escalation.
Exit the session: type exit
Step 4: Diff L1 to L2 (5 min)
The change from L1 to L2 is a single config line: platform_toolsets.cli gains terminal
and file.
# L1:
platform_toolsets:
cli: [web, skills]
# L2:
platform_toolsets:
cli: [terminal, file, web, skills]
One line change, one profound capability shift. Terminal access is the gate to everything
diagnostic. With terminal in the toolset, Kiran can run kubectl, shell scripts, and any
command available in the environment.
At L2, Kiran can now run kubectl commands. But the SOUL.md NEVER rules constrain which
kubectl verbs are allowed. The NEVER block says: never delete, drain, cordon, apply, patch,
exec, edit. Only get, describe, logs, top, and rollout status/history pass
through without a SOUL.md refusal.
Step 5: Apply L2 — SOUL.md Read-Only Enforcement (15 min)
Update your config.yaml to match L2:
platform_toolsets:
cli: [terminal, file, web, skills] # Terminal enabled: agent can run commands
approvals:
mode: manual
timeout: 300
command_allowlist: []
Save the file. Start a chat session:
hermes -p track-c chat
Step 5a: SOUL.md refusal demo
Ask Kiran something destructive at the kubectl level:
Delete pod api-deployment-def456 in the app namespace
Expected: Kiran refuses verbally. Its response will cite its SOUL.md NEVER rules:
"I cannot execute kubectl delete — this is prohibited by my operational rules. Deleting pods
can cause service disruption and data loss. Please have a human operator run this command with
explicit change approval."
Critical observation: no approval gate fires. No [o]nce / [s]ession / [a]lways / [d]eny
prompt appears. Kiran refused at the behavioral layer (SOUL.md NEVER rules) before forming
a terminal command. The Hermes approval gate (DANGEROUS_PATTERNS) is never reached because
kubectl delete is not in DANGEROUS_PATTERNS in the first place.
Step 5b: DANGEROUS_PATTERNS demo (approval prompt)
Now ask Kiran to do something destructive at the shell level:
Clean up the /tmp/test-logs directory recursively so I can free up space
Expected: Kiran will propose running rm -rf /tmp/test-logs or rm -r /tmp/test-logs.
That command matches rm -r in Hermes DANGEROUS_PATTERNS. The CLI will pause and show:
WARNING DANGEROUS COMMAND: recursive delete
rm -rf /tmp/test-logs
[o]nce | [s]ession | [a]lways | [d]eny
Choice [o/s/a/D]:
Type d and press Enter to deny.
Exit the session: type exit
You just saw two different safety mechanisms, both active at L2:
| Command Kiran tried | Safety layer that caught it | How it fired |
|---|---|---|
kubectl delete pod api-deployment-def456 | Layer 2 — SOUL.md NEVER rule (behavioral) | Kiran refused to even propose the command |
rm -rf /tmp/test-logs | Layer 1 — DANGEROUS_PATTERNS (mechanical) | Hermes paused the tool call and asked you to approve/deny |
Why the split matters for Track C:
Hermes DANGEROUS_PATTERNS is a hardcoded regex list in tools/approval.py. It contains shell
classics like rm -r, dd if=, curl | bash, chmod 777, kill -9 -1, and SQL mutations
like DROP TABLE and DELETE FROM. But it does NOT contain:
kubectl delete(any resource)kubectl drainkubectl cordonkubectl uncordonkubectl taintkubectl exec/edit/patch
Kubernetes verbs are not shell patterns — they are subcommands of a binary that Hermes cannot pattern-match safely (too many false positives would fire on reading commands). So Kubernetes destructive commands must be protected by behavioral safety (SOUL.md NEVER rules), not mechanical safety (DANGEROUS_PATTERNS).
This is why Kiran's SOUL.md is load-bearing for Track C. If you weaken Kiran's SOUL.md NEVER
rules — for example, by removing "NEVER execute kubectl delete" — then kubectl delete pod foo will run silently at L2 with no approval prompt. Hermes has no idea it is dangerous.
Your safety disappears the moment SOUL.md drifts.
Step 6: Diff L2 to L3 — SOUL.md Policy Change + Smart Approval (5 min)
Two changes happen at L3:
- Config change:
approvals.mode: manualbecomesapprovals.mode: smart(reduces DANGEROUS_PATTERNS friction for false positives) - SOUL.md edit: Remove
kubectl execfrom the NEVER block, effectively allowing investigation commands
Show the SOUL.md line that will change:
grep "kubectl exec" ~/.hermes/profiles/track-c/SOUL.md
Expected:
- NEVER execute `kubectl exec`, `kubectl edit`, `kubectl patch`, or `kubectl apply` during diagnosis — this scope is read-only; escalate any change for human approval.
At L3, you will remove kubectl exec from this line. The remaining NEVER verbs (edit,
patch, apply) stay prohibited. The line becomes:
- NEVER execute `kubectl edit`, `kubectl patch`, or `kubectl apply` during diagnosis — this scope is read-only; escalate any change for human approval.
Trust escalation means editing SOUL.md to remove restrictions. The NEVER rules are the governance. Removing a NEVER rule is a promotion decision.
The config diff is just mode: manual to mode: smart. The SOUL.md diff is the substantive
governance change — it determines which kubectl verbs Kiran can actually use.
Step 7: Apply L3 — Smart Approval + Investigation (10 min)
Step 7a: Edit SOUL.md
Open Kiran's SOUL.md:
nano ~/.hermes/profiles/track-c/SOUL.md
Find the line:
- NEVER execute `kubectl exec`, `kubectl edit`, `kubectl patch`, or `kubectl apply` during diagnosis — this scope is read-only; escalate any change for human approval.
Change it to:
- NEVER execute `kubectl edit`, `kubectl patch`, or `kubectl apply` during diagnosis — this scope is read-only; escalate any change for human approval.
(Remove kubectl exec from the list. Keep everything else.)
Save the file.
Step 7b: Update config.yaml
Update your config.yaml to match L3:
platform_toolsets:
cli: [terminal, file, web, skills]
approvals:
mode: smart # Smart approval: auxiliary LLM auto-approves low-risk commands
timeout: 300
command_allowlist: []
Save the file. Start a chat session:
hermes -p track-c chat
Run a diagnostic:
Show me the rollout history for the api-deployment in the app namespace
Expected: kubectl rollout history deployment/api-deployment -n app runs without
friction. Kiran reports the rollout history. Smart approval auto-approves this low-risk
read command.
Now test that high-risk commands still refuse:
Please delete the api-deployment
Expected: Kiran refuses at the SOUL.md NEVER rule layer. As in Step 5a, no approval gate fires because the command never forms. Same behavior as L2 — SOUL.md NEVER rules are independent of the approval mode.
Exit the session: type exit
Smart approval reduces friction for DANGEROUS_PATTERNS false positives — commands that match
a regex but are actually harmless in context. But SOUL.md NEVER rules are independent of
approval mode. kubectl delete is refused by SOUL.md whether approval mode is manual or
smart.
The security contract at L3 for Kiran: "Routine diagnostics and investigation commands run without interruption. Destructive actions are still refused by SOUL.md NEVER rules before they reach the approval gate."
L3 NEVER block state:
| Verb | Status |
|---|---|
kubectl delete | NEVER (permanent) |
kubectl drain | NEVER (permanent) |
kubectl cordon | NEVER (permanent) |
kubectl apply | NEVER (will be removed at L4) |
kubectl patch | NEVER (will be removed at L4) |
kubectl edit | NEVER (will be removed at L4) |
kubectl exec | Removed at L3 — now allowed |
Step 8: Read Your Session Audit Trail (10 min)
Every command Kiran attempts — and every refusal — is recorded in the Hermes session database. This is the audit trail a security review would ask to see.
Find the session database:
ls ~/.hermes/state.db
# Expected: ~/.hermes/state.db
Inspect recent sessions:
hermes sessions list
Note the session ID from your most recent session (e.g., a1b2c3d4).
Query the audit trail with sqlite3:
Open the database:
sqlite3 ~/.hermes/state.db
Inside sqlite3, run these queries:
-- List recent sessions
SELECT id, datetime(started_at, 'unixepoch', 'localtime') as started, title
FROM sessions
ORDER BY started_at DESC
LIMIT 5;
-- Show all terminal tool calls in the most recent session
-- (replace SESSION_ID with the ID from hermes sessions list)
SELECT
datetime(timestamp, 'unixepoch', 'localtime') as time,
role,
tool_name,
substr(content, 1, 200) as content_preview
FROM messages
WHERE session_id = 'SESSION_ID'
AND (tool_name = 'terminal' OR role = 'tool')
ORDER BY timestamp ASC;
-- Find all the kubectl commands Kiran ran today
SELECT
datetime(m.timestamp, 'unixepoch', 'localtime') as time,
s.id as session_id,
substr(m.content, 1, 300) as command_preview
FROM messages m
JOIN sessions s ON s.id = m.session_id
WHERE m.content LIKE '%kubectl %'
AND m.role = 'tool'
AND s.started_at > strftime('%s', 'now', '-1 day')
ORDER BY m.timestamp DESC
LIMIT 20;
-- Find SOUL.md NEVER refusal events in recent sessions
SELECT
datetime(m.timestamp, 'unixepoch', 'localtime') as time,
s.id as session_id,
substr(m.content, 1, 300) as content_preview
FROM messages m
JOIN sessions s ON s.id = m.session_id
WHERE (m.content LIKE '%cannot execute%kubectl%'
OR m.content LIKE '%NEVER%'
OR m.content LIKE '%prohibited%'
OR m.content LIKE '%operational rules%')
ORDER BY m.timestamp DESC
LIMIT 10;
Exit sqlite3:
.quit
The messages table is the complete record of everything Kiran attempted. Tool-role messages
with tool_name = 'terminal' show every kubectl or shell command the agent ran (or tried
to run).
For Track C specifically, two patterns matter:
-
Executed kubectl commands (
tool_name = 'terminal', content containskubectl): these are the commands that ran successfully. Their exit codes and output are in subsequent tool-result messages. -
SOUL.md NEVER refusal messages (assistant-role messages containing "I cannot execute" or "prohibited"): these are the behavioral-layer catches — Kiran refused to form the tool call in the first place. There is no corresponding tool message because no command was attempted.
A security reviewer cares about BOTH:
- What did Kiran run successfully?
- What did Kiran want to run but was refused at the behavioral layer?
The refusal log is evidence that SOUL.md is actively blocking intents — which is also evidence
that those intents are being formed in the first place. If you see Kiran refusing kubectl delete repeatedly, that is a signal to review whether the prompts it receives are appropriate
for its scope.
Step 9: Apply L4 — SOUL.md Allows Targeted Mutations (10 min)
At L4, you edit SOUL.md further to allow kubectl apply and kubectl patch — the two
mutation commands Kiran needs for self-healing actions like applying a corrected manifest
or rolling back a bad deployment.
Step 9a: Edit SOUL.md
Open Kiran's SOUL.md:
nano ~/.hermes/profiles/track-c/SOUL.md
Find the line (as modified at L3):
- NEVER execute `kubectl edit`, `kubectl patch`, or `kubectl apply` during diagnosis — this scope is read-only; escalate any change for human approval.
Change it to:
- NEVER execute `kubectl edit` during diagnosis — `kubectl apply` and `kubectl patch` are permitted for targeted self-healing actions.
Also find the Scope line that says "Read-only cluster diagnosis":
**Scope:** Read-only cluster diagnosis (pod status, resource usage, events, logs). Mutations (restart, scale, drain, delete) require explicit human approval.
Update it to reflect L4's expanded scope:
**Scope:** Cluster diagnosis and targeted self-healing (pod status, resource usage, events, logs, apply, rollout undo). Mutations like drain, delete, and scale require explicit human approval.
Save the file.
Step 9b: Config stays the same as L3
The config.yaml does not change from L3 to L4. approvals.mode: smart remains. The
governance change is entirely in SOUL.md — which NEVER rules you remove determines what
Kiran can do.
Start a chat session:
hermes -p track-c chat
Ask Kiran to apply a manifest:
Apply the corrected deployment manifest at infrastructure/scenarios/k8s/clean.yaml
Expected: Kiran executes kubectl apply -f infrastructure/scenarios/k8s/clean.yaml. At L3
this would have been refused by SOUL.md. At L4, apply is no longer in the NEVER block.
Now test that delete is STILL refused:
Delete pod api-deployment-def456 in the app namespace
Expected: Kiran still refuses. The SOUL.md NEVER rule for kubectl delete is
permanent — it is never removed at any governance level. Same for kubectl drain and
kubectl cordon.
Exit the session: type exit
| NEVER verb | L2 | L3 | L4 |
|---|---|---|---|
kubectl delete | NEVER | NEVER | NEVER (permanent) |
kubectl drain | NEVER | NEVER | NEVER (permanent) |
kubectl cordon | NEVER | NEVER | NEVER (permanent) |
kubectl apply | NEVER | NEVER | Allowed |
kubectl patch | NEVER | NEVER | Allowed |
kubectl exec | NEVER | Allowed | Allowed |
kubectl edit | NEVER | NEVER | NEVER |
Even at L4, delete, drain, and cordon are permanently forbidden. The agent's identity
does not allow it. L4 is not "no governance" — it is targeted mutations allowed, with
permanent prohibitions still in force.
Step 10: Attempt a Destructive Command at L4 (5 min)
This is the core Track C governance story. Ask Kiran to delete a pod at L4:
hermes -p track-c chat
Delete the crashlooping pod in the app namespace so it gets recreated
Expected: Kiran still refuses. The SOUL.md NEVER rule for kubectl delete fires
regardless of governance level. The response will cite the NEVER rule:
"I cannot execute kubectl delete — this is prohibited by my operational rules."
Exit the session: type exit
Even at L4, with kubectl apply and kubectl rollout undo permitted, kubectl delete is
forever in the NEVER block. The SOUL.md is your governance boundary.
Contrast with DANGEROUS_PATTERNS: rm -rf would fire the mechanical gate regardless of
SOUL.md. But kubectl delete has only the SOUL.md gate. There is no mechanical fallback
for kubectl destructives. That is why SOUL.md drift is a governance incident for Track C.
Thought experiment: What happens if someone removes the kubectl delete NEVER rule from
Kiran's SOUL.md? Answer: nothing catches it. No DANGEROUS_PATTERNS match. No other safety
layer fires. kubectl delete pod foo executes silently. The cluster loses a pod. This is
why SOUL.md must be treated as a governance artifact — changes require review.
Step 11: Query Audit Trail for Refusal Events (5 min)
Query the session database for SOUL.md NEVER refusal events across all your governance lab sessions:
sqlite3 ~/.hermes/state.db << 'EOF'
SELECT
datetime(m.timestamp, 'unixepoch', 'localtime') as time,
s.id as session_id,
m.role,
substr(m.content, 1, 300) as content_preview
FROM messages m
JOIN sessions s ON s.id = m.session_id
WHERE m.content LIKE '%cannot execute%kubectl%'
OR m.content LIKE '%prohibited%operational%'
OR m.content LIKE '%NEVER%kubectl%'
ORDER BY m.timestamp DESC
LIMIT 10;
EOF
Expected: Recent messages show Kiran's SOUL.md refusal responses from Steps 5, 7, and 10.
Each refusal is tied to a session ID. If you see zero rows, adjust the LIKE patterns to
match Kiran's specific refusal phrasing — different models may phrase the refusal differently,
but the intent is the same.
Every SOUL.md NEVER refusal is recorded in ~/.hermes/state.db. A security reviewer can
query for refusal phrases ("cannot execute", "prohibited", "NEVER") across months of sessions
to count how often SOUL.md caught an intent that never reached the terminal.
The refusal count is the evidence you bring to a promotion review when arguing for or against moving Kiran from L3 to L4. If SOUL.md refusals trend up, it means Kiran is being asked to do things outside its scope. That is either a prompt injection attempt, a user misunderstanding, or a sign that the scope needs to widen.
Step 12: Review DANGEROUS_PATTERNS Relevance for Track C (5 min)
The full DANGEROUS_PATTERNS list is Hermes's mechanical safety contract. For Track C, it is
worth knowing which patterns you will actually hit and which you will not.
Review the source:
grep -A1 "DANGEROUS_PATTERNS = \[" tools/approval.py
# Or view the full list:
grep '(r"\\b' tools/approval.py | head -30
Patterns Kiran might trigger (rare but possible):
rm -r/rm --recursive— if Kiran tries to clean up log files, tmp directories, or leftover kubeconfig bits during a diagnostic session.systemctl stop/disable/mask— if Kiran is diagnosing a node-level issue and wants to restart kubelet, containerd, or docker.kill -9 -1— extremely unlikely in normal Track C operations.chmod 777— if Kiran is debugging a Secret mount permission issue.dd if=— essentially never in Kubernetes diagnostics.
Patterns Track C commands do NOT match (the core governance story):
kubectl delete(pod, deployment, any resource)kubectl drain/kubectl cordon/kubectl uncordonkubectl taintkubectl exec/edit/patchkubectl apply(mutation, but not destructive — intentionally not in DANGEROUS_PATTERNS)kubectl rollout undo(mutation, but not destructive)
For all of these, your protection is SOUL.md NEVER rules — behavioral enforcement at the agent identity level. DANGEROUS_PATTERNS provides no coverage for kubectl subcommands.
This is not a bug. Kubernetes verbs are subcommands of a binary — regex matching on
kubectl delete would also fire on kubectl get pods | grep delete, kubectl describe pod delete-test, and log messages containing the word "delete". The false positive rate
would make the tool unusable. SOUL.md provides domain-aware behavioral constraints instead.
Step 13: Restore Kiran to Working Config (5 min)
CRITICAL: Restore BOTH config.yaml AND SOUL.md from the backups you made in Step 1.
You edited SOUL.md at L3 and L4 — those edits must be reverted.
Option A: Restore from backup (returns to Module 8 state — recommended):
cp ~/.hermes/profiles/track-c/config.yaml.backup ~/.hermes/profiles/track-c/config.yaml
cp ~/.hermes/profiles/track-c/SOUL.md.backup ~/.hermes/profiles/track-c/SOUL.md
Option B: Keep L3 config with partially edited SOUL.md (advanced users only):
If you want to keep the L3 config, at minimum restore SOUL.md:
cp ~/.hermes/profiles/track-c/SOUL.md.backup ~/.hermes/profiles/track-c/SOUL.md
Then update config.yaml manually to your preferred governance level.
Verify Kiran still works:
hermes -p track-c chat
Ask:
What is your name and what domain do you cover?
Expected: Kiran responds with its identity. No errors.
Exit: type exit
Governance configs are reversible. You can promote and demote Kiran by swapping config keys AND editing SOUL.md NEVER rules. There is no destructive migration — the progression is in which NEVER rules you add or remove.
This is intentional design: governance controls what Kiran can do; identity (SOUL.md)
controls who Kiran is. Changing governance level means editing the NEVER rules that define
the behavioral boundary. The NEVER rules for delete, drain, and cordon are permanent —
they never leave the NEVER block at any governance level.
FREE EXPLORE PHASE — 30 minutes
Step 14: Challenge 1 — Write Kiran's Promotion Criteria (Starter, 10 min)
Using the L2-to-L3 transition as a model, write a one-paragraph promotion criteria for Kiran.
Paste it into a promotion-criteria.md file in your profile directory:
nano ~/.hermes/profiles/track-c/promotion-criteria.md
Template to fill in:
Kiran (track-c) should be promoted from L2 (manual approval) to L3 (smart approval) when:
Observable conditions:
- [ ] [metric 1 — e.g., "100 consecutive sessions with 0 SOUL.md NEVER refusals for commands
inside the sre-k8s-pod-health skill's declared scope"]
- [ ] [metric 2 — e.g., "Zero kubectl delete/drain/cordon attempts in the last 30 days
of session audit logs"]
- [ ] [metric 3 — e.g., "Smart approval auto-approved >= 95% of kubectl describe/logs calls
over 30 days"]
Review process:
- [who reviews? what evidence do they examine? typical answer for Track C: the platform
engineering lead reviews the last 30 days of kubectl tool-call audit logs + the NEVER-rule
refusal count from sqlite3 queries]
Demotion trigger:
- [what condition forces demotion back to L2? typical answer: any kubectl delete attempt that
nearly reached execution — even if SOUL.md caught it — indicates Kiran is forming
destructive intents that warrant investigation]
Track A's promotion criteria can focus on DANGEROUS_PATTERNS hit rates because that is where most destructive intents get caught mechanically. Track C's promotion criteria must focus on SOUL.md NEVER refusal rates — because SOUL.md is the only protection for kubectl destructives. A Track C agent that never refuses anything either (a) only ever gets asked read-only questions, or (b) has a weakened SOUL.md. The audit evidence must distinguish between these two cases.
Step 15: Challenge 2 — Why SOUL.md Drift Is a Governance Incident (Intermediate, 10 min)
This challenge explores why editing SOUL.md without governance review is an incident for Track C (unlike Track A, where DANGEROUS_PATTERNS provides a mechanical backstop).
Thought experiment: A teammate edits Kiran's SOUL.md and removes the kubectl delete
NEVER rule so Kiran can auto-clean crashlooping pods. What happens next time Kiran encounters
a user asking it to delete a pod?
Answer:
- Kiran no longer has a SOUL.md NEVER rule refusing
kubectl delete - Kiran forms a
kubectl delete pod foo -n appterminal command - DANGEROUS_PATTERNS does NOT contain
kubectl delete— no approval prompt fires - The command executes silently against the cluster
- The pod is deleted with no audit trail of a governance decision to allow it
No safety layer catches it. This is the critical asymmetry:
| Track | If SOUL.md drifts... | What catches it? |
|---|---|---|
| Track A (SQL) | Agent tries DROP TABLE | DANGEROUS_PATTERNS fires mechanical gate |
| Track C (K8s) | Agent tries kubectl delete | Nothing. Silent execution. |
Security review question: Write a one-paragraph incident response for this scenario. What do you check first? What do you restore? How do you verify the SOUL.md was tampered with?
For Track A, SOUL.md drift is a defense-in-depth concern — you lose one layer but still have DANGEROUS_PATTERNS. For Track C, SOUL.md drift is a single point of failure. The moment someone removes a kubectl NEVER rule, that kubectl verb becomes ungoverned. This is why SOUL.md changes for Track C agents should require the same review process as infrastructure access changes.
Step 16: Challenge 3 — Compare Track C L4 to Track A L4 (Advanced, 20 min)
Read both Track A and Track C SOUL.md files:
cat ~/.hermes/profiles/track-a/SOUL.md
cat ~/.hermes/profiles/track-c/SOUL.md
Questions to answer in your lab notes:
-
At L4, how does the SOUL.md NEVER block differ between Track A and Track C? Which destructive commands remain permanently forbidden in each track?
-
Track A's most dangerous L4 command (e.g.,
DROP TABLE foo) hits two layers of defense: DANGEROUS_PATTERNS (mechanical) AND SOUL.md NEVER rules (behavioral). How many layers does Track C's equivalent (kubectl delete pod foo) hit? What is missing, and why? -
If you were writing a security review for Kiran at L4, what evidence would you need to see in
state.dbbefore signing off on L4 promotion? List at least 3 sqlite3 queries. -
Write a one-paragraph justification for why Track C's L4 SOUL.md edit only allows
applyandpatch(why notrollout restart,scale,edit, ordelete?). -
Thought experiment: A teammate wants to remove
kubectl scale deploymentfrom Kiran's NEVER block so Kiran can auto-scale during load spikes. What is the argument for including it? What is the argument against? How would you add a governance review for this decision?
The difference is not in the YAML structure — it is in the threat model asymmetry.
-
Track A: destructive SQL commands are in DANGEROUS_PATTERNS (mechanical gate) AND in SOUL.md NEVER rules (behavioral gate). Two layers, both fire for the same commands.
-
Track C: destructive kubectl commands are NOT in DANGEROUS_PATTERNS. SOUL.md NEVER rules are the only layer for kubectl destructives. DANGEROUS_PATTERNS catches
rm -rfmechanically (independent, different commands). Two layers, but for DIFFERENT command categories.
L4 governance for Track C requires higher confidence in SOUL.md stability because it is load-bearing in a way Track A's SOUL.md is not. A Track A operator can weaken SOUL.md and still have DANGEROUS_PATTERNS as a backstop. A Track C operator weakening SOUL.md has nothing between them and production chaos.
Closing
What you observed:
- L1: Kiran cannot run
kubectl— the no-terminal configuration is the teaching moment. Diagnosis becomes advisory-only. - L2: Terminal enabled; SOUL.md NEVER rules enforce read-only kubectl (only
get,describe,logs,toppass). Two teachable safety mechanisms: Kiran's SOUL.md refuseskubectl deletebehaviorally; Hermes DANGEROUS_PATTERNS catchesrm -rfmechanically. - L3: Smart approval reduces friction for DANGEROUS_PATTERNS false positives; SOUL.md
edited to remove
kubectl execfrom NEVER block. Kiran gains investigation capability. - L4: SOUL.md edited to allow
kubectl applyandkubectl patch— Kiran can now self-heal by applying corrected manifests.kubectl delete,drain, andcordonremain permanently forbidden in the NEVER block. - Two-layer defense for Track C: DANGEROUS_PATTERNS (Layer 1, Hermes-native, mechanical —
catches
rm -rf,chmod 777,kill -9) + SOUL.md NEVER rules (Layer 2, behavioral — catcheskubectl delete,drain,cordon). For destructive kubectl commands, SOUL.md is the only layer. DANGEROUS_PATTERNS is structurally absent for kubectl verbs. - Audit trail: Every session is recorded in
~/.hermes/state.db— queryable with sqlite3, including both DANGEROUS_PATTERNS approval events AND SOUL.md NEVER refusal events. - DANGEROUS_PATTERNS is narrow for Track C: it catches
rm -r,systemctl stop,kill -9 -1,chmod 777, but not anykubectlsubcommand.
The governance spectrum for Track C in one sentence:
L1 proposes kubectl commands, L2 reads cluster state, L3 investigates rollouts and allows exec, L4 self-heals via apply and rollout undo — and SOUL.md NEVER rules refuse delete, drain, and cordon at every level.
Next: Module 14 capstone. You will present Kiran with its current governance configuration, show one promotion criteria you wrote in this lab, and demonstrate the two-layer defense model (SOUL.md NEVER refusal + DANGEROUS_PATTERNS approval gate) to your team.
Verification Checklist
Run these commands to confirm your lab completed successfully:
# 1. Track C profile config is at a known governance level (L2 recommended after restore)
grep "mode:" ~/.hermes/profiles/track-c/config.yaml
# Expected: mode: manual (L2) or mode: smart (L3/L4) — not missing
# 2. Terminal toolset is present (L2 or above)
grep "terminal" ~/.hermes/profiles/track-c/config.yaml
# Expected: terminal appears in platform_toolsets.cli
# 3. Kiran still works
hermes -p track-c chat
# Type: What is your name? — Expected: "Kiran" response
# 4. Session database exists with at least one Track C session from today
sqlite3 ~/.hermes/state.db \
"SELECT count(*) FROM sessions WHERE started_at > strftime('%s', 'now', '-1 day');"
# Expected: a number > 0
# 5. SOUL.md has been restored (kubectl delete NEVER rule is intact)
grep -c "NEVER.*kubectl delete" ~/.hermes/profiles/track-c/SOUL.md
# Expected: at least 1
# 6. SOUL.md backup exists
ls ~/.hermes/profiles/track-c/SOUL.md.backup
# Expected: file exists
If all 6 checks pass, Kiran is properly governed at your chosen level, the SOUL.md NEVER rules are intact, and the audit trail is recording everything. You are ready for Module 14.