Imagine an AI coding agent assigned to modify files inside src/**.
Its next task requests a change to secrets/production.txt.
The agent has received instructions, but the task conflicts with its declared scope.
Can we catch this contradiction before execution?
That's one of the problems we're working on at Netsujo.
Introducing Agent Role Contracts
Agent Role Contracts (ARC) is an MIT-licensed, open-source tool for deterministic pre-execution checks of AI-agent workflows.
ARC can detect contradictions involving:
- Declared permissions and task scopes
- Implementation and independent review roles
- Stale handoffs and task bindings
- Inconsistent action declarations
The checks run offline and produce reproducible diagnostics.
Try it in three minutes
With Git and Node.js 22.5 or newer:
git clone https://github.com/suirindo/agent-role-contracts.git
npm --prefix agent-role-contracts run demo --silent
The demo shows three outcomes:
PASS → FAIL → PASS
- A task within its declared scope passes.
- An out-of-scope task is rejected.
- Repairing the task declaration restores a passing result.
ARC validates declared contracts. Runtime identity, actual permissions, enforcement, and execution approval remain separate responsibilities.
We're looking for real-world feedback
ARC v1.0.0 is available now.
We're especially interested in developers working with coding agents and multi-agent systems.
Where do responsibilities, permissions, or review boundaries become difficult to manage?
Which checks are already covered by your runtime or CI infrastructure?
We welcome both positive and negative evaluation results.
Try ARC: https://github.com/suirindo/agent-role-contracts
Evaluate a real workflow: https://github.com/suirindo/agent-role-contracts/issues/37
If you find the project useful and want to support its continued development:
GitHub Sponsors: https://github.com/sponsors/netsujo-inc
Developed by Netsujo in Kyoto, Japan.
This article was prepared with AI writing assistance and reviewed by the Netsujo team before publication.
Top comments (13)
Nice framing: catching the contradiction before execution is much cheaper than catching it after.
The case I keep running into is the one that passes a scope check: the call is inside the declared scope but it can't be undone (deleting a volume in a project the agent is allowed to touch, a force push to a branch it owns). Do you see ARC staying purely declarative, or pairing with a runtime hold for irreversible calls that are technically in scope?
Thanks, Gautam. You’re right: being inside the allowed scope does not make a deletion or force-push safe.
ARC currently checks declarations. It does not classify those operations by reversibility or provide a runtime hold. I see it as one check before execution, alongside a runtime layer that can pause the specific operation and verify the required approval. An ARC PASS alone should never be treated as permission to perform a destructive action.
In your current workflow, how do you decide which in-scope operations require an explicit human approval? A non-confidential example would help clarify where the declaration check ends and the runtime safeguard needs to begin.
Good question. I decide by reversibility, not by scope. Reads and normal writes go straight through. Anything that can't be taken back waits for a human: deleting a repo, branch or database, a force push to main, a refund or a payout.
A non-confidential example from what I'm building (Counterseal): an agent can open a PR and push to its own branch freely, but a force push to main gets held, and my passkey approval covers that one exact request, once.
So I'd draw the line where you did. The declaration check answers "is this in scope?" The runtime check answers "can this be undone, and did a human say yes to exactly this?"
That's a useful distinction, especially the passkey approval bound to one exact operation.
I think reversibility is a strong signal, but I'd also consider effects that can't truly be undone, like exposing sensitive data or triggering an external action.
We're exploring this boundary in ARC's runtime adapter work: checking the actual operation against its authorized scope, intended effects, and approval before execution.
Curious how Counterseal handles cases where an operation is technically reversible but its consequences aren't.
Good point, and I agree reversibility alone isn't enough. A sent email, a published release or a repo made public can't really be undone, even if the API has an undo.
In Counterseal that's a policy decision per token. The built-in map holds calls that can't be taken back. A
holdrule adds anything whose consequences you care about (for examplePOST /repos/*/*/releases),denyrefuses it outright, and an allow list orreadOnlykeeps the agent away from data it shouldn't touch. Every hold still needs a passkey for that one exact request.The honest limit: it decides per API call, so it can't see consequences that happen downstream. Someone has to name those calls. I'd be curious whether ARC's intended effects check could help fill that gap.
Thanks, Gautam. I should clarify my earlier wording: the intended-effects check I mentioned is a direction we’re exploring, not a capability I can claim ARC already provides. ARC’s current core checks explicit declarations.
The possible complement is narrower: checking whether a declared proposed action matches the approved task. For example, “draft a release” versus “publish a release.” That depends on accurate declarations; it wouldn’t discover hidden downstream consequences or replace Counterseal’s runtime authorization.
Have you encountered a request that token policy permits, but that should still be held because it exceeds the approved task? A public or synthetic example would help us see whether another check adds value. Thanks for explaining the boundary so clearly.
Yes, two.
Public: the PocketOS case (AI Incident Database, report 7311). The agent was working in staging, found an unrelated Railway token in a file, and used it to delete a volume. The token allowed the call. The task never did. Counterseal holds that one because a Railway volume delete is on the map, but what made it wrong was the task, not the token.
Synthetic, and one Counterseal would let through: the task is "fix the failing test on my branch", and the agent turns CI green by editing the workflow so the test is skipped. That's a normal, reversible write to its own branch, so token policy allows it. A check that compares the declared action ("change the CI workflow") with the approved task ("fix a test") would flag it with no extra rule. That looks like exactly the gap your narrower check could fill.
Thanks, Gautam. These are two excellent examples, especially the CI case.
I agree that a reversible, authorized write can still violate the task's intent. That's precisely the boundary I'm interested in exploring with Agent Role Contracts.
One important challenge is that comparing declared actions with the approved task may not be sufficient on its own. The agent could describe the change as "fixing CI" while actually disabling the failing test.
I think the contract needs verifiable acceptance conditions, such as keeping required tests enabled and checking the actual diff against those conditions.
The PocketOS case illustrates task-scoped authority at the API boundary, while the CI example illustrates semantic constraints on otherwise permitted writes.
I'd be interested in turning both into concrete test cases and comparing where ARC and Counterseal could complement each other.
Agreed, the declaration can't be trusted on its own, which is why acceptance conditions beat descriptions. For the CI case I'd write the contract as checks on the diff, not words: no file under .github/workflows changes unless the task says so, the set of tests that run doesn't shrink, and no test gains a skip marker. Those are cheap to verify and the agent can't talk its way around them.
Happy to turn both into concrete cases. As a sketch, each case lists the task, the token's permissions, what the agent actually sent or changed, and the expected outcome at each layer. PocketOS: the contract check flags the out-of-task target, and the runtime hold stops the volume delete. CI skip: the contract check fails the acceptance condition, while the runtime hold lets the write through. A table like that shows exactly where each layer earns its keep.
Thanks, Gautam. I think a two-case comparison matrix is exactly the right way to make these boundaries testable.
I'd start with:
One detail I'd add to the CI case: compare the actual set of required tests before and after the change, including workflow configuration, test exclusions, and skip markers. Simply checking the number of tests could miss substitutions.
ARC currently validates explicit declarations. Diff-based acceptance checks would be a proposed extension, so I'd label those expected results accordingly.
I'd like to turn these into reproducible regression fixtures, with both allowed and rejected variants.
Would you be willing to review the Counterseal expectations against its actual policy behavior? That would make the comparison much more useful than a purely conceptual example.
Yes, happy to. Here is how Counterseal should behave on both cases today, so the fixtures can label it honestly:
So each layer catches what the other misses, which is the point of the matrix. Agreed on comparing the set of required tests rather than the count; a swapped test keeps the count the same.
I'll check these three rows against the current policy code and post the result here.
Checked against Counterseal's provider rules (server/src/providers.js on main):
volumeDeleteis held.So for the CI case the two layers split cleanly by path, which seems like a useful pair of rows for the matrix.
Thank you so much, Gautam.
This is exactly the distinction we needed, and I appreciate you checking the provider rules.
I'll split the CI case into two fixtures: ordinary workflow/test changes and branch-protection API mutations.
For each case, I'd like to record the action path, what Counterseal can observe, what it holds, and what the task contract should detect.
One important detail for the CI fixture: we'll compare the identities and execution status of required tests, not just the total count, so replacing or skipping a required test produces a failure.
I'll also keep the raw-token Railway case explicit as a visibility gap, rather than claiming the approval layer protects it.
Once we have reproducible cases, we can compare the results against ARC's current behavior and identify any gaps.
Happy to cross-check the fixtures with you.