Define What Done Means for an Agent Workflow
Published · 6 min read
By Cong Le · Pocket Grove
Original research brief: . Sources reviewed: .
Quick answer: Define completion through recorded outcomes: every required task has a known result, its artifact exists, acceptance checks refer to that artifact, and the requested delivery is confirmed. Keep worker execution, accepted work, and delivery separate so a restart can reconcile them before repeating anything.
A coding agent can finish its message while a build continues. A worker can exit successfully after producing an incomplete file. A reviewed patch can exist locally while the requested delivery remains unfinished.
Each event tells you something useful. A workflow needs to specify which combination satisfies the user's request. Otherwise the word “done” becomes a different promise depending on which component says it.
The example here is a fictional documentation update with two parallel workers. The records are a design exercise; no workflow was executed to produce them.
Write the completion contract before dispatch
Suppose the assignment is to update an installation guide and its command reference, reconcile their shared examples, and deliver a reviewable candidate.
Define three logical tasks: installation, command-reference, and integration. The first two may run in parallel. Integration depends on both accepted inputs. Completion requires a recorded candidate containing the combined changes and the agreed review evidence.
That definition deliberately names the requested delivery. If the user asked for a local candidate, publication is outside this contract. If publication is authorized and requested, the workflow needs another step with evidence from the destination.
Make the definition short enough to consult during recovery. A useful contract names the expected tasks, dependencies, required artifacts, acceptance criteria, delivery target, and the person or component responsible for accepting each result.
Keep three state dimensions
A single status field can conceal too much. For each task, record execution separately from acceptance and delivery:
| Dimension | Example states | Question it answers |
|---|---|---|
| Execution | Pending, running, succeeded, failed, cancelled, unknown | What happened to the attempt? |
| Acceptance | Pending, accepted, rejected, invalidated | Does the recorded result satisfy the task? |
| Delivery | Not required, pending, confirmed, unknown | Did the required destination receive it? |
These are proposed application states, not a standard every agent runtime implements. An execution can succeed while acceptance is rejected. Delivery can remain unknown after a connection drops, even if the destination applied the change.
The distinction is familiar outside agents. HTTP 202 Accepted means processing was accepted but has not completed; its status alone does not establish the eventual outcome. RFC 9110 makes that boundary explicit.
Join the work you started
When dispatching parallel work, retain an inventory of task identities and execution handles. Before integration, account for every required task. A timeout belongs in the inventory as an unresolved or failed observation; it must not quietly disappear from the expected set.
Use the runtime's joining mechanism where one exists. For example, Python's asyncio.TaskGroup waits for its tasks when the context exits and propagates failures according to its documented rules. It gives a program a concrete boundary for related execution. Python's task-group documentation describes that behavior.
An execution join still needs application acceptance. Check that the installation worker produced the requested file and that the command-reference worker covered the intended commands. “Both workers returned” is an intermediate fact in this workflow.
For external workers, define how their status is queried and how long the coordinator waits. If one becomes unreachable, preserve its task identity and last observation. Starting a replacement while the original can still write needs a coordination rule that prevents competing writes.
Give accepted work a durable receipt
Here is a compact illustrative record. Names such as candidate-A are placeholders for actual immutable artifact identifiers:
{
"task_id": "installation",
"attempt_id": "installation-attempt-2",
"input_artifact": "brief-revision-3",
"result_artifact": "candidate-A",
"execution": "succeeded",
"acceptance": {
"state": "accepted",
"criteria_revision": "guide-checklist-2",
"evidence": "review-record-A"
},
"delivery": "not_required"
}
The logical task stays stable across retries. The attempt identifies one execution. The artifact identifies what was produced, while the evidence explains why it was accepted.
Store the receipt where the next coordinator can retrieve it independently of the conversation. Save the artifact before marking its receipt accepted, and confirm that the stored reference resolves. If the artifact and receipt use different storage systems, record intermediate states and reconcile partial writes; do not assume they commit together.
Keep enough detail to detect stale acceptance. If the input brief changes, or the artifact is replaced, the earlier review may no longer apply. Our guide to verifying the exact code an agent will ship develops that identity check for source and build evidence.
Reconcile before resuming
Imagine the coordinator restarts with these observations:
| Logical task | Recorded evidence | Next decision |
|---|---|---|
| Installation | Accepted artifact and matching input revision | Reuse that accepted result |
| Command reference | Attempt started; no final receipt | Query the worker and artifact store |
| Integration | Never started | Wait for both accepted dependencies |
The missing receipt does not prove the command-reference worker did nothing. It may have saved a result before losing contact. Reconcile the artifact and execution status before dispatching again.
If its result exists, run the defined acceptance step and record the outcome. If the attempt is conclusively absent or failed, create a new attempt for the same logical task. If the outcome remains unknown, retain that state and choose an operation-specific recovery path.
Use stable task identities instead of positions such as “second worker launched.” Concurrent execution order is a poor definition of which work a result belongs to. A resumed coordinator should compare expected tasks with durable records, including their dependency revisions.
Handle repeated external effects separately
A completion receipt records what the workflow knows. Preventing a duplicate external effect requires support where that effect is applied.
For an API with an idempotency contract, reuse the operation's identifier on a retry and honor its scope, retention, and parameter rules. Amazon's discussion of idempotent APIs explains why caller-provided request identity matters and why recording identity must be coordinated with the mutation.
Do not assume identical arguments express the same intent: someone may legitimately request two identical resources. Conversely, a new network attempt should not automatically become a new business operation.
Where the destination offers no suitable deduplication or read-back contract, an uncertain outcome needs explicit handling. The recovery guide for stopped agents explains how to preserve that uncertainty before retrying or undoing work.
Review the final completion claim
Before reporting completion, check that:
- Every required logical task has an accounted-for outcome.
- All dependencies used by the final candidate are identified.
- Accepted artifacts exist and match their recorded review evidence.
- Replaced inputs or artifacts have invalidated stale acceptance.
- Required delivery has a destination receipt or an explicit unresolved state.
- The final message describes the outcome and any remaining limitation.
A small workflow can keep this information in a short file. A larger service may need a transactional job store. Start with the smallest record that lets a fresh coordinator answer: what is accepted, what remains uncertain, and what may run next?
Apps from the studio
All appsThese practices come from shipping Pocket Grove's active apps. If you came here looking for something to install, start with one of these.
Related guides
Google's BERT to Gemini 4: What Actually Changed, in Plain English
Google's AI models from the Transformer to Gemini 4 explained simply: Bard, native multimodal Gemini, 1M context, agents, thinking levels and Argon.
Claude 1 to Claude 5: What Actually Changed, in Plain English
Anthropic's Claude from 2023 to 2026 explained simply: 100K context, vision, computer use, extended thinking, Claude Code, 1M context and effort.
GPT-2 to GPT-6: What Actually Changed, in Plain English
GPT-2 to GPT-6 Astra explained simply: how OpenAI models went from finishing text to reasoning, using tools and taking corrections mid-task.