Claude Graph Runs: Why You Get a Queue Ticket15 min read2,964 words

Claude Graph Runs: Why You Get a Queue Ticket

Learn why Claude gets a queue ticket for an osFoundry Graph, how desktop relays affect latency, and how to handle polling, retries, and results.

Claude Graph runsosFoundry MCPagent tool orchestrationidempotency keysdesktop relay
Claude Graph Runs: Why You Get a Queue Ticket

Why Claude Gets a Queue Ticket Instead of a Graph Result

When Claude starts a Graph in osFoundry, it doesn't get the finished output right away. Instead, the tool returns a command ID - a ticket that says the run has been queued. At first that can feel like busywork, until you see where the Graph actually executes. The engine, local models, files, and tool-approval settings are on the owner's desktop, not on the server that receives Claude's request.

The public MCP tool description spells this out: run_graph_session queues the run, and get_graph_run is what you call later to check progress and pull the result. In a tool listing observed on November 8, 2026, the service also exposed tools for listing shared sessions and cancelling a run. That's a handoff model - not a "call returns when it's done" model.

And it matters beyond comfort. This setup changes wait times, how often you poll, what your retries look like, how cancellation behaves, how big results can be, and who is allowed to access a run. In the sections below, we'll follow the request from Claude to the desktop relay, what timing a simulation can (and can't) tell you, and the practical rules for agents and Graphs built around a queue-based workflow.

What does the queue ticket tell you?

A queue ticket means the service accepted your request to run a Graph. It does not mean the Graph has started, and it certainly doesn't mean the result is ready. The agent has to keep the returned command ID and use it to check status and then retrieve the outcome.

The exposed run_graph_session schema requires a graph_session_id and an idempotency_key. Its description says the run is queued on the owner's online desktop in a connected project. It also notes that the desktop can go offline, and that local tool approvals still apply. Those aren't just word choices - they're operational constraints.

A session is a reference to local capability

A Graph is a saved JavaScript program run by the desktop's fleet engine. It can include conditional routes, parallel agents, bounded retries, and calls to other Graphs. In the source, a session is described as the durable identity of a saved Graph on a device: an ID that starts with gs_ and is followed by 32 hexadecimal characters.

That session ID is not the same thing as an execution. The session points to a Graph the desktop can run. Each request creates a separate command, and once picked up, a separate execution. This distinction shows up when an agent lists available sessions, starts a run, and later checks the command's status.

Why a successful start can return no result

The server can record the request and return a ticket even though it doesn't have access to the desktop runtime. So the boundary is intentionally asynchronous: start the work, hold onto the ID, then retrieve the result via a separate read operation.

The tool listing also separates permissions. Listing sessions and reading a run use osfoundry:read, while starting or cancelling a run uses osfoundry:run.

A real account's empty session list fits this model. The source reports that sessions show up after the owner shares them from the desktop. An empty list doesn't automatically mean the feature is broken - it can just mean there aren't any shared sessions available to that project.

Practical implication: the agent should describe the outcome as "queued," not "running" or "complete." Treating acceptance as completion leads to false status updates and fragile workflows.

Why must a desktop pick up the Graph?

The desktop has to be the worker because it's where the execution environment lives. The source identifies the Graph engine, launched agents, local models, folders, and per-tool approval configuration as resources in the desktop process. For a server-side runner to do the same thing, it would need equivalent copies of those resources and permissions - which is a different product and a different security model.

So instead of the server opening a connection into a user's machine, the desktop asks the server for work. That outbound relay pattern shows up in systems that need to operate behind home/office routers, corporate firewalls, or changing network addresses. And importantly: the desktop doesn't need an inbound port exposed to the internet.

The relay lifecycle

The described desktop relay syncs on roughly a 20-second cadence. It skips a new pass if the previous pass is still busy, and it sends metadata for sessions the owner has shared with each enabled project. The reported maximum is 100 sessions per project sync.

The server can respond with queued commands. Before executing one, the desktop checks the project is still enabled, the session is still shared, and the session still matches the expected slug. If those checks fail, the command is marked cancelled rather than run. If they pass, the desktop starts the local Graph runner, monitors for cancellation, and returns a bounded result.

That second permission check matters because sharing can change after a command enters the queue. If the server only checked at submission time, there would be a window where a job could run even after the owner revoked access.

The cost of polling from the desktop

A 20-second pickup interval gives predictable waiting behavior. In a simulation with 2,000 runs and a random phase between the request and the next relay poll, the average pickup wait was 9.8 seconds, the median was 9.8 seconds, and the 95th percentile was 18.9 seconds. The maximum was 20 seconds.

These are simulation results, not a production latency guarantee. What they do show is the trade-off: the relay can run a simple outbound polling loop, but a request that arrives just after a poll could wait nearly the full interval.

There's also an obvious downside: if the laptop is asleep or offline, there's no worker to claim the job, so you wait longer.

The relay also processes commands serially and pauses its timer while it's busy. In the same simulation, five queued runs had a median time of 75 seconds to the first result and 399 seconds to the last. The desktop behaves like one worker, not like several independent workers. If you want parallel execution, put it inside a Graph where the engine can manage concurrency, instead of assuming queued requests will automatically fan out.

How should an agent poll for completion?

There's no push channel described here. After the agent receives a command ID, it has to call get_graph_run repeatedly until the run reaches a terminal state. Polling more often can make the result appear sooner - but it also burns tool calls and adds overhead in the agent loop.

The source includes a simulation of 2,000 runs using synthetic Graph durations (lognormal distribution, a 60-second median and a five-second minimum). Those figures help compare polling strategies under stated assumptions. They're not measurements of real customer workloads, and you shouldn't treat them as a universal schedule.

Client polling strategy Mean calls per run 95th-percentile calls Mean detection delay 95th-percentile delay
Every 2 seconds 47.1 118 1.0 seconds 1.9 seconds
Every 5 seconds 19.1 48 2.5 seconds 4.8 seconds
Every 15 seconds 6.7 16 7.5 seconds 14.2 seconds
Every 30 seconds 3.6 8 14.9 seconds 28.4 seconds
Backoff from 2 seconds to a 30-second cap 6.6 11 14.8 seconds 28.4 seconds
Wait 20 seconds, then poll every 10 seconds 8.8 23 5.0 seconds 9.6 seconds

Why exponential backoff can be the wrong default

Backoff is helpful when a service is overloaded or when lots of clients spamming status checks would hurt reliability. But it's not automatically the best choice for a short, queued task.

In this simulation, backoff from two seconds to a 30-second cap ended up with about the same number of calls as fixed 15-second polling, while detecting completion about twice as late on average.

The reason is the shape of the work. A typical Graph lasts about a minute. With backoff, you tend to reach the long polling interval right around when runs finish. Then you wait up to 30 seconds for a detection that you could have seen sooner with a steadier cadence.

Fixed polling can be easier to reason about when run durations aren't wildly variable.

A practical polling rule

Start from the known pickup interval, then pick a polling cadence that matches how costly delayed results are for your workflow. If the relay polls every 20 seconds, waiting about one interval before the first status check is reasonable - you can't start the run until a relay pass picks it up.

After that, a fixed interval like 10 seconds can balance responsiveness and tool usage. Tune it to the task: if a few seconds changes the user experience, poll more. If the job runs in the background and can tolerate delays, poll less. If tool calls are expensive in your agent loop, measure total call count as well as how quickly the final result becomes visible.

What do retries, cancellation, and disconnects change?

Queue-based execution has multiple failure points: the start request might be accepted while the response is lost; a queued run might be cancelled before pickup; a running Graph might take time to stop; or the desktop might finish but fail to deliver the result back. Treating all of these as a single generic "failed" state makes recovery harder and can create duplicate side effects.

Idempotency prevents duplicate starts

The start tool requires an idempotency key between 8 and 128 characters. That key lets a client retry after a lost response without creating another run - as long as it reuses the same key.

The source says this follows osFoundry's CLI guidance, but the retry comparison is simulated rather than measured from production traffic.

In a simulation where 30% of responses were lost, using a new key on each retry produced 266-302 queued executions across repeated 200-run trials. Reusing the same key produced exactly 200 queued executions in each trial, despite a similar number of sent requests.

For a Graph that sends email, edits files, or triggers external actions, duplicates aren't just wasted compute - they can repeat irreversible effects. The safe pattern is: generate one key per intended operation, persist it during retries, and only create a new key when the user is actually asking for a new run.

Cancellation depends on whether work has started

A queued command can be cancelled at the server before the desktop sees it. In the relay described here, cancelling a running task is different: the desktop checks for a control signal every three seconds and then asks the local fleet engine to stop the execution.

In the simulation, queued work became terminal immediately, while running work took a mean of two seconds and a 95th percentile of 3.3 seconds to reach a terminal state.

Those numbers reflect the simulated control loop, not a promise that every Graph will stop cleanly. A tool call already in progress may not be instantly reversible, and external side effects may already have happened. So cancellation should mean "stop further work where possible," not "undo everything."

Offline desktops and durable completion delivery

Queued work can sit on the server while a desktop is offline. In the source's simulation, a desktop offline for 600 seconds executed nothing during that period; once it reconnected, it picked up work and completed five runs. That's a durability benefit - but it doesn't magically make the end-to-end workflow available while the worker is disconnected.

You also need reliability for the return path. If a Graph finishes but the completion POST is lost, an in-memory-only relay could leave the server showing running indefinitely.

The described implementation writes completion data to a local outbox first, then removes it only after a successful response. In the simulation, a lost completion was delivered on a later relay pass with the outbox enabled; without it, the server never received the result.

What should teams build around this execution model?

The best design move is to treat the workflow like a small distributed system. The server accepts a command, the desktop polls and executes it, and the desktop reports the result back over a network that can fail in either direction. That means you need clear state, durable identifiers, authorization checks, and an explicit contract for what your agent tells the user.

Design the tool contract around the actual workflow

A start tool should say plainly that it queues work and point the caller to the tool used for status checks and result retrieval. A model can't reliably infer an asynchronous lifecycle from a name like run_graph_session. Return the command ID in a structured response and require the agent to carry it through every later read or cancellation request.

Keep permissions aligned with operations. In the exposed schema, reading sessions and runs is separate from running or cancelling them. The source also reports that a run is accessible to the connected agent that started it; another agent using the same command ID can receive a not-found-or-access-removed error. If you blur "doesn't exist" and "isn't yours," you reduce the chance of leaking whether a particular ID is valid.

Keep the worker authoritative at execution time

The desktop should re-check consent and configuration right before running a queued command. It should also persist completion data before trying to send it.

These checks add implementation complexity, but skipping them causes worse failure modes: a revoked share can still execute, or a completed run can become permanently invisible after a single lost network response.

Also keep command handling serial unless the desktop runtime has a deliberate concurrency model. If you need parallelism, define where it lives and how it's bounded: inside the Graph engine, not as an accidental pile-up of queued commands. Concurrency needs a budget too, especially with local models and finite token limits.

Make results useful to the agent

The relay allows a result payload only when the serialized JSON is no more than 100,000 characters. Oversized output gets replaced with a truncation marker, and errors are cut off at 2,000 characters. The limit is in characters, not bytes.

The source also gives an example: 99,899 Japanese characters correspond to 299,697 UTF-8 bytes and still fit under the character limit.

Even if a result fits the limit, it can still be too large to present to a model. Design agent-facing Graphs to return a concise summary, key values, and a reference to the full execution history. Don't make the "normal success result" be a raw log dump - otherwise the most important details get buried or truncated.

Implementation checklist:

  • Make queueing explicit in the start tool's description and response.
  • Require a stable idempotency key for retries of the same intended run.
  • Use a polling interval that balances tool-call cost against result latency.
  • Revalidate sharing and project authorization immediately before execution.
  • Persist completions locally until the server acknowledges delivery.
  • Keep agent-facing results compact; store detailed logs with the execution.
  • Explain that cancellation may stop future work without undoing completed side effects.

Conclusion

Claude gets a queue ticket because the server is coordinating work that only the owner's desktop can execute. That separation avoids needing a server copy of local tools, models, files, and approvals - but it introduces real trade-offs: pickup latency, polling, retry behavior, cancellation semantics, and result-delivery reliability.

Instead of hiding those realities, build around them. Treat "start" as acceptance, carry the command ID and idempotency key forward, poll on a cadence that matches the work, and make the desktop's return path durable. For Graph authors, the goal is a bounded execution and a compact, useful result - not a black box that leaves the agent guessing.

Frequently Asked Questions

The request is accepted by a server, but the Graph runs on the owner’s desktop, where its engine and local resources are available. The server returns a command ID so the agent can check progress and retrieve the result after the desktop picks up and completes the work.
There is no single best interval. If the desktop relay polls every 20 seconds, waiting roughly that long before the first check avoids polling before the run can start. Then choose a fixed cadence based on how costly delayed results and extra tool calls are for your workflow.
The request can remain queued, but the Graph cannot execute until an available desktop relay picks it up. Queued work waits during the offline period and can start after the desktop reconnects; this is deferred execution, not server-side execution.
Not necessarily. A queued run can be cancelled before execution, while a running task needs the desktop to receive and act on the cancellation signal. Cancellation can stop further work, but it cannot reliably reverse external side effects that have already occurred.
If the start request succeeds but its response is lost, the client may retry without knowing whether a run was created. Reusing the same key lets the service recognize the retry as the same intended operation. A new key can create a second run and repeat side effects.

Related articles