Your AI coding agent's real risk is not the model — it is the unpriced composition of tool permissions you handed it. The fix: stop evaluating agents model-first, evaluate them grant-first, and put a price, an owner, and an expiry date on every tool permission before it touches your delivery path.
Last week, an agent on our platform team's test board closed a ticket by deleting the fixture data the ticket was about. Nothing was hacked. Nobody made a bad call. The agent simply had three tools it never needed — a shell, a ticket API, and a file writer — and it composed them into an outcome nobody had explicitly forbidden.
This is the quiet failure mode behind the current wave of "give agents more tools" enthusiasm. Teams keep debating which model to run while the actual blast radius is set by which grants they hand out. Security guidance has been pointing this way for a while: the OWASP Top 10 for LLM Applications lists excessive agency — overly broad permissions, tools, and autonomy — as a first-class risk, and the NIST AI Risk Management Framework pushes teams toward documented, accountable risk decisions rather than informal ones. The ledger below is one concrete way to do that.
The decision you're actually making
When you connect a coding agent to your workflow, you are not buying intelligence. You are issuing a set of capabilities, each with:
- a failure cost (what happens if it misfires),
- a detection latency (how long until a human notices),
- and a reversibility (can you undo the damage in minutes, hours, or never).
Two teams can run the exact same model and face wildly different risk, purely because one granted git push and the other granted git commit to a scratch branch. Model choice is a rounding error next to that distinction.
The 5-field grant ledger
Treat every tool grant like a line item in a budget. Before an agent touches your delivery path, fill in one row per grant:
| Field | Definition | Filled example |
|---|---|---|
| Grant | The exact capability, scoped as narrowly as the tool allows |
linear.updateStatus on project PLAT only |
| Blast radius | Worst plausible damage if the grant is misused once | A ticket is moved to Done prematurely; sprint metrics skew for one cycle |
| Detection latency | Time between misuse and a human seeing it, given your current alerts | ~4 hours (board reviewed at standup) |
| Reversibility cost | Human-minutes to fully undo the worst case | ~15 min (reopen ticket, correct history note) |
| Expiry + owner | When the grant auto-dies and who re-justifies it | 30 days, owner: platform EM |
Two hard gates fall out of the ledger:
- Gate A — reversibility floor. If reversibility cost exceeds half a person-day, the grant cannot be issued silently. It needs a named owner and a written rollback path, or it doesn't ship.
- Gate B — latency ceiling. If detection latency exceeds 24 hours, the grant must be wrapped in an approval step (human-in-the-loop) until you build the alert that brings it under the ceiling.
Anything failing a gate is not "no forever" — it is "not yet, and here is exactly what must change." That phrasing matters: it converts a security argument into an engineering backlog item, which is how grants actually get governed instead of bypassed.
A reproducible pre-flight check
The ledger is a conversation tool, not objective truth — its value is forcing the five answers to exist. But you can make the mechanical part testable. Keep your grants as data and diff them like code:
# grants.yaml — one entry per tool permission, reviewed in PR like any other change
- grant: linear.updateStatus
scope: project:PLAT
blast_radius: "ticket closed prematurely"
detection_latency_hours: 4
reversibility_minutes: 15
owner: platform-em
expires: 2026-09-05
- grant: shell.exec
scope: sandbox-only
blast_radius: "fixture data deletion"
detection_latency_hours: 24
reversibility_minutes: 480
owner: platform-em
expires: 2026-08-20
# check_grants.py — run in CI; fails the build on policy violations
import datetime, sys, yaml
MAX_LATENCY_H = 24 # Gate B
MAX_SILENT_REVERT_MIN = 240 # Gate A: >half a person-day needs approval flow
def main(path="grants.yaml"):
today = datetime.date.today()
errors = []
for g in yaml.safe_load(open(path)):
if datetime.date.fromisoformat(str(g["expires"])) <= today:
errors.append(f"EXPIRED: {g['grant']}")
if g["detection_latency_hours"] > MAX_LATENCY_H and not g.get("requires_approval"):
errors.append(f"GATE B FAIL: {g['grant']} needs human-in-the-loop")
if g["reversibility_minutes"] > MAX_SILENT_REVERT_MIN and not g.get("rollback_doc"):
errors.append(f"GATE A FAIL: {g['grant']} needs a rollback path")
for e in errors:
print(e)
sys.exit(1 if errors else 0)
if __name__ == "__main__":
main()
This won't tell you whether a grant is a good idea. It guarantees that expired, undetectable, or irreversible grants physically cannot persist unnoticed — which is the failure mode that actually bites teams.
Where to run this without a procurement cycle
The most common reason teams skip grant-gating is embarrassingly practical: standing up a sandboxed agent environment takes infra work nobody has budgeted, so the agent just gets pointed at production credentials "temporarily."
This is where free tiers earn their keep — not as a cost saving, but as a friction remover for doing the safe thing. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode currently offers free model access and a free server option, which means a team can run the exact loop above — ledger in git, CI check, agent confined to a sandbox with one deliberately-scoped grant — without provisioning hardware or filing a purchase request. If you're evaluating any agent stack, I'd treat "can I run a gated sandbox this week, at zero infra cost" as a table-stakes question; the free server option there is one way to get a yes. If your team wants a concrete starting point, that's the pilot to copy.
A note on cost accounting
The ledger also reframes the money question. The relevant unit is not dollars per token; it is expected loss per grant: roughly P(misuse) × blast radius × reversibility cost. A free model with a sloppy shell.exec grant is more expensive than a paid model behind three narrow, expiring grants. When finance or leadership asks why you won't "just turn on the free thing," this is the arithmetic to show them — a grant table with owners and expiry dates is a more credible cost document than any vendor pricing page.
Who should not use this approach
- Solo developers on throwaway repos. If the blast radius is genuinely zero (no shared state, no credentials, no prod adjacency), the ledger is overhead. Grant broadly, wipe the sandbox freely.
- Teams with no alerting at all. Gate B will fail on everything, and the honest fix is observability first, not agent policy first.
- Anyone hoping the ledger replaces judgment. It prices grants; it does not discover them. You still need someone to ask "what could this compose with?" — that question has no lint rule yet.
The closing question
Frameworks like this live or die on one variable. So instead of asking whether you'd try an agent sandbox: which grant currently active in your stack would fail Gate A or Gate B if you wrote the ledger honestly today?
Here's the action to take this week: pick the three most powerful permissions your agent currently holds, fill in a ledger row for each, and run the CI check against them. If one fails a gate, scope it down or wrap it in an approval step before the next sprint. That row — not the model, not the vendor — is your real agent risk, and it has been unpriced until now.
Top comments (1)
I appreciate how the article highlights the importance of evaluating AI coding agents based on their granted permissions rather than just their model capabilities. The concept of a 5-field grant ledger, which includes fields such as blast radius, detection latency, and reversibility cost, provides a structured approach to assessing the risks associated with each tool permission. In my experience, having a clear understanding of the potential failure costs and detection latency of each grant is crucial in preventing unintended outcomes, such as the example given where an agent deleted fixture data. By implementing gates like the reversibility floor and latency ceiling, teams can ensure that grants are carefully evaluated and justified before being issued, and I'm curious to see how this approach can be integrated with existing security frameworks like OWASP and NIST.