What Does an Agent Action Actually Mean, and Who Governs It?
Sentience Governor 0.3.2 resolves policy by agent identity and keeps it fixed for the session, then classifies Claude Code shell actions by what they actually do. Two questions, one runtime model, and a governor allowed to say unknown.
For a long time, a shell action in the Sentience Agent Execution Record could look like this:
tool_id: Bash
operation_type: EXECUTE
target_system: shell
It is accurate. It is also not enough. git status, rm -rf build and aws ec2 terminate-instances all begin from essentially the same observation: the agent executed a shell command. From a governance perspective, those actions mean very different things.
At the same time, there was another question we could not answer precisely enough: which policy was governing this particular agent when the action happened?
Those two questions became the center of our latest work on Sentience Governor, released as 0.3.2 on 16 September. What did the agent actually do? And who governed it while it did it? The more we worked on them, the more they started to look like two sides of the same problem.
There is a debt behind the first question. Two weeks ago we wrote that our retrospective reviewer had counted 5,967 shell tool calls in one session and evaluated none of them, because a shell command can carry anything. This release is the live half of an answer: Claude Code shell actions are now classified as they happen. The retrospective review still does not classify them.
Who governs this agent?
A governance policy sitting somewhere on a machine is not the same thing as knowing which policy governed an execution. That distinction starts to matter as soon as multiple agents share the same environment. A coding agent may need one policy, a customer-support agent another, a research agent a third.
So Sentience now resolves policy by agent identity. A machine can define bindings from an agent pattern to a governance profile, and when a session opens, resolution is deterministic:
binding → default → none
The first matching binding wins. If nothing matches, the machine default can apply. If there is no default, the session runs without a profile.
There is an important failure rule here too. If the first matching binding is broken, Sentience does not quietly move to the next matching rule and give the agent somebody else’s policy. The resolution becomes degraded. If a valid default exists, the session falls back to it, and the evidence records which binding failed. A misconfiguration becomes visible instead of silently changing the governance decision.
A registration, and every event after it, can now carry evidence such as:
profile_resolution: bound
profile_binding: claude-code-*
profile_fingerprint: c6d259f6dc9f
Now the record can answer: this is the policy that governed this agent. Today that is true for Claude Code, for the MCP server and for the LangChain adapter; the Pydantic AI companion adopts the same resolution in its next release.
One session, one policy
Resolving the policy is only half the problem. The policy also has to remain stable while the agent is working.
Imagine an agent begins a long-running task under policy A. Halfway through, somebody edits the machine configuration and points that agent at policy B. Should the next action suddenly be governed differently? We decided no. A session should not silently change constitutional rules halfway through its execution. The property we wanted was simple: one session, one governing policy.
In a single-process runtime that is relatively straightforward: resolve once and retain the profile. Claude Code made it more interesting, because each of its hooks executes as a separate process. For that environment the resolved policy is materialized into an immutable, content-addressed snapshot, and later hook processes reload that same snapshot rather than resolving the policy again.
We tested this fairly aggressively. We opened a session, changed the binding, changed the original profile file, and continued the session across new processes. The existing session kept the original policy fingerprint. The next new session picked up the new configuration. That is the behavior we wanted: configuration can evolve without rewriting the governance history of an execution already underway.
A policy’s identity is its posture, not its schema
Once a policy is bound to a session, the record has to be able to say which policy that was, long after the configuration that produced it is gone, in a way that nobody can quietly rewrite. That sounds simple until the software changes underneath it.
We were adding new optional policy capabilities. If adding an unused field changed every existing policy fingerprint, the same effective policy would suddenly appear to be a different policy simply because the software had learned a new feature. That did not make sense. So we tightened the rule: the identity of a policy changes when its governance posture changes, not because the schema gained an unused capability.
There is also a distinction between identity and integrity. The short fingerprint is useful in execution evidence; a durable snapshot uses the complete content hash for integrity. That gives two different answers to two different questions. Which policy was this? And is this exactly the policy content that was originally bound? The distinction matters when governance evidence has to survive longer than the configuration that produced it.
Now the harder question: what did the action actually mean?
Consider Bash again. Previously we could reliably say Bash, EXECUTE, shell. A runtime governor needs more than that. git status, rm -rf build, aws ec2 describe-instances and aws ec2 terminate-instances are all executions, and semantically they are very different.
So rather than replacing the existing execution contract, we added another layer beside it. The old fields remain intact. A shell action can now also carry a semantic classification:
tool_id: Bash
operation_type: EXECUTE
target_system: shell/filesystem
operation_classification:
complete: true
destructive: true
segments:
- executable: rm
effects:
- domain: filesystem
action: delete
destructive: true
Now the record says something much more useful: the agent performed a filesystem delete, and we have strong evidence that it was destructive.
Today this classification covers Claude Code’s Bash tool. Other integrations keep recording the operation their tool declared, exactly as before.
One command may contain several actions
One of the more important design lessons was that a shell command cannot always be compressed into one semantic label. Take:
curl -o report.json https://example.com/report
There are at least two effects: a network read and a filesystem modification. If we forced the entire command into one domain and one action, we would lose information. Worse, if we selected a domain from one part of the command and an action from another, we could manufacture an event that never happened.
So the classification model is effect-based. A command can contain several segments, a segment can contain several effects, and policy evaluates those actual effects individually. That gave us a principle we have come to care about: evidence should say only what the runtime can support. The governor should not invent a cleaner story than the execution actually gives it.
Knowing what you don’t know is good governance
This work also changed the way we think about uncertainty. A governance system is often tempted to produce an answer because the schema has a field that expects one. Sometimes the correct answer is unknown, and that is useful governance information.
The classifier uses a deliberately small vocabulary. The domains are filesystem, version control, packages, network, cloud infrastructure, process and unknown. The actions are read, create, modify, delete, execute and unknown. And destructiveness is not binary: it can be true, false or null, where null means the classifier does not have enough evidence to decide.
Consider an infrastructure apply operation. It may be consequential. But if the real changes are contained in an unseen plan, the command string alone cannot tell us whether something destructive will actually happen. Calling it destructive with certainty would be stronger than our evidence, so the governor says it does not know. That is not weak governance. It is better governance than false confidence.
The same applies to incomplete commands. For git status && ./deploy.sh we understand the first segment as a version-control read and we do not know what the script does. The record says complete: false and preserves the known effect without pretending to understand the unknown one. A runtime governor should be allowed to say: this is what I know, and this is what I do not know.
Small shell details become governance details
Once actions are interpreted semantically, seemingly minor shell constructs become meaningful. Redirection is one example. echo hello > file.txt has a filesystem effect even though echo itself is not a filesystem tool. Appending with >> does not mean exactly the same thing as truncating with >. And > /dev/null should not create the same filesystem signal as writing a real file: /dev/null is a sink. That distinction sounds tiny. For governance, it matters.
The same conservative principle applies to shell syntax we cannot completely interpret. Known effects remain known, unknown effects remain unknown, and the classification becomes incomplete rather than confidently wrong.
Where policy meets action
Classification becomes governance when policy can consume it. A profile can now describe high-consequence operations semantically:
high_consequence:
operations:
- domain: cloud_infrastructure
destructive: true
That rule does not ask whether the agent used Bash. It asks whether the agent performed a destructive cloud-infrastructure action, and matching happens against individual effects.
Suppose a compound command performs a cloud-infrastructure read and a filesystem delete. The policy above does not fire, because there was no destructive cloud-infrastructure effect. We deliberately do not combine cloud_infrastructure from one effect with destructive: true from another. A policy match has to correspond to something the agent actually did. Again: evidence should say only what the runtime can support.
This agent, under this policy, attempted these actions
Policy resolution answers the first question: who governs this agent? Action classification answers the second: what is this agent actually doing? Neither is the point on its own. A policy fingerprint on an event whose action reads Bash, EXECUTE, shell governs nothing in particular, and a precise classification with no bound policy is a description with no rules to answer to. Put together, the record can say the sentence this release exists for: this agent, governed by this policy, attempted these actions. And the policy can evaluate the action by what it means rather than by which tool produced it.
That is a different foundation from traditional tracing. A trace might tell us:
Agent → Bash → command
A governance record should increasingly be able to tell us:
Agent
→ governed by policy X
→ attempted effect Y
→ policy X considers Y consequential
→ and here is where the evidence ends
Sentience Governor evaluates agent actions against declared governance context and records governance evidence without blocking execution. The command still executes. But the record is becoming much clearer about what happened and under which rules it happened, and that is a much stronger foundation for whatever comes next.
The questions we now ask
We began this work thinking about features. We ended up with a set of questions that increasingly define runtime governance for us.
Which policy governs this session? Does that policy remain stable while the agent is executing? Can we prove later which policy governed the action? What is the agent actually attempting to do? Is that one action or several effects? How certain are we about our interpretation? Can the governor say unknown instead of guessing? Can a policy match only things that actually happened? Can we add governance capability without changing the identity of policies whose behavior did not change?
And, most recently: can governance itself fail safely?
That last question emerged while taking the same policy-resolution model into another agent runtime. We found a core edge case where malformed governance configuration could itself become a source of runtime failure. We paused the integration work and opened the issue against core, where the invariant belongs. A governor should not destabilize the system it is governing. We will write more about that once the fix is complete.
Where this leaves us
The important change is not a release number, and it is not the classifier or the resolution file individually. It is getting closer to a runtime model where the evidence can say: this agent was governed by this policy; it attempted these actions; this is what we know about those actions; this is what the policy concluded; and this is where our certainty ends.
Policy gives the execution its governance context. Action classification gives the Governor enough semantic meaning to reason about what happens inside that context. Bringing those two together is the real work.
Install Sentience Governor and try it yourself
Install Sentience Governor, connect it to Claude Code, and inspect a real session on your own machine. Both questions are now answerable: which policy governed the session, and what its shell commands actually did.
pipx install "sentience-governor[mcp]"
sentience init claude-code --mcp
Run a Claude Code session, then ask what governed it:
sentience profile resolve --session-id <session>
If you run more than one agent, write a resolution.yaml binding each one to its own profile, and ask what a new session would get before it starts:
sentience profile resolve --agent-id <agent>
Then open the trace and read the Bash events: the agent, the policy that governed it, and what each command did, in one record. The changelog has the full list of what 0.3.2 changed.