Repository navigation
feat(task-step): derive snapshot_agent_state's artifact id per run when omitted - #302
Merged
canersoz-scaleai merged 6 commits intoOct 23, 2026
Conversation
…en omitted snapshot_agent_state took a fixed artifact_id from the task definition, so every run of one task wrote a new version of the same artifact and a reader of the latest version could get another run's agent state. artifact_id is now optional. When it is omitted the step snapshots into <instance_id>__<step id>, the same instance-scoped base SnapshotSeries derives its ids from: distinct across runs, stable across retries of one instance. An explicit artifact_id behaves as before. from_dict tolerates a missing key, so a stored step without one loads; older releases cannot load such a step. FD-4006 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…entry agent_snapshots has several writers (prompt_agent's series rows and any number of snapshot_agent_state steps), so a reader that wants one step's snapshot needs the step id on the entry rather than re-deriving the artifact id. The sibling capture records already carry capture_step_id. Also tests the derived id without an instance id, and keeps the resolver private. FD-4006 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kaihackney-scale
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed against the head tree. The per-run id looks right: resolved per execute and never stored on the step, stable across retries of one instance, and from_dict tolerates a missing key. I also checked that the bundle export/import code (ref_sites / _derived_outputs) handles a None OUTPUT ref. One suggestion inline.
CI: AWS CodeBuild us-west-2 is red here, but #310 and #311 fail the same check, so it looks like CodeBuild itself, not this PR.
kaihackney-scale
approved these changes
Oct 23, 2026
…e snapshotted A per-run snapshot_agent_state (artifact_id omitted) is meant to be added to every run that prompts an agent, and not every agent serves the snapshot extension. Failing the step on each of their runs only adds a failed step to runs that have nothing to capture, so in that mode the step logs a warning, records no agent_snapshots entry and succeeds. A step with an explicit artifact_id still raises: a caller that names the artifact expects it written. A missing a2a_context_id still raises: the agent can be snapshotted, but the prompt it would be taken from did not record its conversation. FD-4006 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the extension An agent recorded without a card points at how the run context was built, not at an agent that can't be snapshotted, so it no longer skips: the step goes on to the capture and fails (non-fatally where configured) as before. FD-4006 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o its own capture - The per-run skip no longer covers a step that also dumps per-service state (env_id and universe_artifact_id): the dump is written beside the workspace capture, so skipping it silently left the run without that state. Such a step still raises. The skip and the dump read one predicate, so they can't drift. - The derived artifact id is `<instance_id>__<step id>-agent-state`, so it can't share an id with the ids a prompt step's snapshot series derives from its own step id (`<step>-workspace`, `snapshot-<step>`). - `agent-env task run` shows a snapshot_agent_state step the row it wrote (matched on capture_step_id) and nothing when it wrote none, rather than whichever agent_snapshots row came last. - to_dict notes why it writes artifact_id even when None. FD-4006 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
approved these changes
Oct 23, 2026
…agent-state-per-run-id
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
snapshot_agent_statecan now derive its artifact id per run. Whenartifact_idis omitted, the step snapshots into<instance_id>__<step id>-agent-state. The suffix keeps it apart from the ids the prompt step's snapshot series derives (<instance_id>__<step>-workspace,snapshot-<step>).Part of FD-4006: capture an agent's end state in the workflow, before the run tears down its sandboxes.
The problem. Today
artifact_idis fixed in the task definition. A task that runs many times therefore writes a new version of one artifact on every run. Any reader that resolves the latest version can get another run's agent state.Optional
artifact_id. When it is omitted, the id comes from the same instance-scoped baseSnapshotSeriesuses (rollout_base+derive_id):adhoc-<hex>when there is no instance id.The id is resolved on each execute and never stored on the step, so concurrent rollouts sharing one step object don't interfere.
Explicit
artifact_id. Unchanged: every run uses it.Agents that can't be snapshotted. When
artifact_idis omitted and the deployed agent's card doesn't advertise the snapshot extension, the step logs a warning and finishes without recording an entry. Before, it failed (non-fatally) on every such run. It still raises as before with an explicitartifact_id, for an agent recorded without a card, or when the step also dumps per-service state (env_id+universe_artifact_id), since that dump is written beside the workspace capture.CLI.
agent-env task runshows theagent_snapshotsrow the step recorded (matched oncapture_step_id), and nothing when it recorded none.Records. Each
agent_snapshotsentry now carriescapture_step_id, like thesnapshot_envandprompt_agentcapture records.agent_snapshotshas several writers (prompt_agent's series rows and any number ofsnapshot_agent_statesteps), so a reader can find one step's snapshot without re-deriving the id.Compatibility.
from_dicttolerates a missingartifact_id, so a stored step without one loads.to_dictalways writes the key,nullwhen omitted. An older release therefore still loads the task, and only this step fails at run time, because it can't derive the id. Readers (workers, hub, modal-proxy) should be on this release before builders emit steps without anartifact_id.artifact_id=Noneexplicitly._rollout_baseinsnapshot_series.pyis renamed torollout_base, now that two modules share it. No behaviour change.How it was tested
make lint: clean.make unit-test: 9482 passed, 25 skipped, 0 failed.tst/unit/task_step/test_snapshot_agent_state.pycover:capture_step_id;adhoc-shape and@localderivation;to_dict/from_dictround trip with and without the id;🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge.Summary
snapshot_agent_statecan now derive a run-specific artifact ID whenartifact_idis omitted.No actionable issues were found. The previous-review merge bases differed, so the full PR diff was reviewed. No previous findings were supplied. Tests were inspected but not run.
Reviews (4) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." · Reviewed by Greptile