Every team that starts handing work to coding agents hits the same wall in about week three. Someone prepares a task, someone else is going to run it, and the agent needs context. Not vague context - the ticket, the acceptance criteria, the linked spec, the three comments that changed the scope. So one of two things happens.
Either the person who prepared the task pastes a wall of text into a chat window and hopes it is still accurate by the time the agent reads it, or somebody shares a credential. A personal access token. An API key with read/write on the whole workspace. In the worst version I have seen, a shared login to the tracker itself.
Both are bad, and they are bad in ways that do not look like security problems at first. They look like reliability problems.
The snapshot goes stale, quietly
A pasted brief is a snapshot. It was true when it was copied. Ten minutes later somebody narrows the scope in a comment, or attaches the design that answers the open question, and the agent has no way to know. It will complete the task it was given, confidently, and you will find out at review that it was the previous version of the task.
The failure mode is not that the agent is wrong. It is that nothing in the system can tell you the agent was reading a stale copy. There is no version, no timestamp, no way to ask "is this still current?".
The credential is too big, always
So the obvious fix is to give the agent live access. And the moment you do that with a normal token, you have handed a process that generates text the ability to change your workspace.
Think about what a standard workspace token can usually do: read any ticket, comment anywhere, transition anything, sometimes delete. You needed exactly one of those powers - read this one task and the things it points at. You granted all of them, for as long as the token lives, which is usually forever.
And you granted them to the wrong identity. A shared token is not "the agent" or "the runner", it is whoever created the token. Every action lands in the audit log under their name. Six weeks later, when you are trying to work out who moved the ticket, the log says a person who was on holiday.
What a bounded credential looks like
The version that actually works is narrower on four separate axes, and it is worth being explicit about all four, because most homegrown solutions get two right and forget the others.
Read-only. Not "read-mostly". The capability the agent starts with should have no ability to deliver, transition, comment, or edit anything. If the agent's report is going to move the task forward, that should be a separate, later, deliberately authorized action - not something the same credential could do at any moment.
Rooted at one resource. Scoped to this claim, on this task. Not to the project, not to the team. If the agent follows a link out of the task to something it was not granted, the answer should be a refusal, not a helpful response. This is the axis people skip, because project-wide scoping is so much easier to build.
Expiring. Days, not months. The task will be done or abandoned long before then. A credential that outlives the work it was minted for is just a key under the doormat.
Revocable independently. Turning off agent access for the team should reject connections immediately, without deleting them and without anybody having to hunt down individual tokens.
Wagglet's handoff is built on exactly this shape: when a teammate claims a task, the copied prompt carries a read-only, claim-rooted context capability plus a fallback snapshot. The agent can refresh the permitted current context - so the staleness problem goes away - but that starting capability cannot deliver or change anything. The details are in the MCP workspace guide, and the surrounding flow is on how it works.
Identity stays with the person
The other half of this, which is easy to miss: the runner uses their own agent subscription. Their own Claude Code or Codex account, their own token balance. Nobody shares an agent login to move a task, which is the practice this whole pattern exists to kill.
That matters for a boring reason and an interesting one. The boring reason is terms of service. The interesting one is that "who ran this" and "who prepared this" are genuinely different facts, and a shared login collapses them into one name. Once collapsed, you cannot answer basic questions about your own process.
The cheap version, if you are not adopting anything
You do not need a product to get most of this. If you are rolling your own handoff today:
- Mint a token per task, not per person, and give it read scope only.
- Set an expiry you would be comfortable defending, then halve it.
- Put the resource id in the token's scope and enforce it server-side, not in the prompt. A prompt instruction to "only read ticket 412" is a suggestion.
- Log the human runner as the actor for anything the agent causes, and keep the agent's own report as evidence rather than as a state change.
- Make the delivery step a separate call with a separate authorization, so "the agent said it is done" and "the task is done" stay different facts.
The pattern is not complicated. It is just narrower than the credential you already have lying around, which is why almost everybody reaches for the wrong one first.
More on the task-design side of this: the dual prompt.
Top comments (5)
The stale-snapshot half matches what I see. Handing an agent a pasted brief works right up until someone edits the source, and nothing announces that it happened.
The credential half is where I'm still stuck. I run several agents and they share one credential rather than each holding a copy, which at least means one place to rotate. But every one of them has the full scope, so it's the shared-token problem with a tidier filename. Task-scoped would fix it and I haven't found a tracker that issues them.
Which ones actually do?
Honest answer: almost none, and I think that is the actual state of things rather than me having missed one.
What exists today is close-but-not-quite, on two different axes.
GitHub Apps get you the expiry and the narrowing, but not the task. An installation access token expires after 1 hour, and at mint time you can restrict it to specific repositories via
repository_idsand to a subset of permissions viapermissions, so you can hand out something strictly weaker than the app itself holds. That is the closest widely-deployed thing I know of. It is repo-rooted though, not issue-rooted.Notion is the one that actually gets resource-rooting right, and it is not even a tracker. A connection has no workspace-wide access at all: a page or database has to be explicitly shared with it, and anything you did not share returns an error rather than data. That refusal-by-default behaviour is exactly the property you want. The catch is that access is tied to the authorizing user, and it is a doc tool, so it solves the shape of your problem in the wrong product.
Jira, Linear, Asana: user-scoped PATs, or OAuth with product-wide scopes. Nothing per-issue that I can find. If you have hit otherwise on a recent version I would genuinely like to know.
The standards-level answer, which is what anyone building this should reach for, is RFC 8707 resource indicators: the client passes a
resourceparameter (an absolute URI) and the authorization server audience-restricts the issued token to it. Pair that with RFC 9728 protected-resource metadata so the resource can advertise where such a token comes from. That is the mechanism by which "read this one ticket" becomes enforceable rather than aspirational, and it is what the MCP authorization work builds on.On your actual setup: one credential shared across several agents really is better than one copy each, you are right that it gives you a single rotation point. But it also means you cannot revoke one misbehaving agent without taking down all of them, and the audit log cannot tell you which one acted.
If you are not changing tracker any time soon, the pragmatic middle step is a small broker in front of it. The broker holds the one wide credential and mints short-lived per-task tokens with the resource id enforced server-side. Same single rotation point you already like, but scope and revocation become per-task and you get an actor per call.
That is more or less the thing I ended up building, so treat me as biased here. But the broker pattern is worth doing even if you write it yourself in an afternoon, because the enforcement has to live on the server either way. A prompt telling the agent to only read ticket 412 is a suggestion, not a boundary.
The user-tied part is where mine falls down too. What I actually run is a folder per agent plus a rule saying stay in your own, so the boundary is made of instructions. It held right up until two of them collided over the same browser extension on one machine. The credential was identical before and after that. I ended up splitting them onto separate machines, which is a blunter fix than scoping a token.
The strongest part here is treating the credential as a capability boundary, not merely an authentication mechanism. I’d push that one step further: make the credential bind to the task identity and execution context, not just the resource ID. That gives you a much cleaner security model when the same task is retried, forked, or handed to another agent. The other important boundary is between read authority and action authority keeping delivery as a separate authorization makes “the agent produced a valid result” fundamentally different from “the system accepted that result as a state change.” That separation becomes especially valuable once agents start operating asynchronously.
Agreed, and the retry/fork case is the one that convinced me the resource id alone is not enough.
If the capability is rooted at the claim rather than at the task, a lot of that falls out for free: re-claiming mints a new capability and the previous one stops working, so a fork is just two claims and you never have to reason about the same credential being live in two places. What it costs you is that one task now legitimately has several capabilities over its lifetime, which means the audit log has to carry run identity separately from task identity. Collapse those two and you have reintroduced exactly the ambiguity the shared-token version had, just further down the stack.
The part I have not solved cleanly is your execution-context point, because binding to it means minting an identity for the run, and almost nothing hands you one. Agent processes are mostly anonymous: you get the human's identity and the task's identity and nothing in between. I currently approximate it with the claim, which is coarser than what you are describing.
Your async point is the one I would underline hardest. Once a report can arrive after the read capability has already expired, delivery authorization has to be independent of the read credential's lifetime, otherwise you are choosing between long-lived reads and results you are no longer able to accept. Keeping them as two separate authorizations is what makes a short expiry survivable at all.