Skip to content

feat(task-step): derive snapshot_agent_state's artifact id per run when omitted - #302

Merged
canersoz-scaleai merged 6 commits into
scaleapi:mainfrom
canersoz-scaleai:canersoz/fd-4006-snapshot-agent-state-per-run-id
Oct 23, 2026
Merged

canersoz-scaleai merged 6 commits into
scaleapi:mainfrom
canersoz-scaleai:canersoz/fd-4006-snapshot-agent-state-per-run-id

Conversation

@canersoz-scaleai

@canersoz-scaleai canersoz-scaleai commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

snapshot_agent_state can now derive its artifact id per run. When artifact_id is 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_id is 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 base SnapshotSeries uses (rollout_base + derive_id):

    • distinct across runs;
    • stable across retries of one instance;
    • 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_id is 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 explicit artifact_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 run shows the agent_snapshots row the step recorded (matched on capture_step_id), and nothing when it recorded none.

  • Records. Each agent_snapshots entry now carries capture_step_id, like the snapshot_env and prompt_agent capture records. agent_snapshots has several writers (prompt_agent's series rows and any number of snapshot_agent_state steps), so a reader can find one step's snapshot without re-deriving the id.

  • Compatibility.

    • from_dict tolerates a missing artifact_id, so a stored step without one loads.
    • to_dict always writes the key, null when 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 an artifact_id.
    • The constructor keyword is still required, so pass artifact_id=None explicitly.
  • _rollout_base in snapshot_series.py is renamed to rollout_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.
  • New tests in tst/unit/task_step/test_snapshot_agent_state.py cover:
    • the derived id differs across instance ids and is stable across retries of one instance;
    • the recorded entry's id and capture_step_id;
    • an explicit id is used for every run;
    • the no-instance-id adhoc- shape and @local derivation;
    • a to_dict/from_dict round trip with and without the id;
    • a stored dict with no key loads;
    • per-run mode skips an agent whose card lacks the snapshot extension, and still raises for an explicit id or a missing card.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge.

Summary

snapshot_agent_state can now derive a run-specific artifact ID when artifact_id is omitted.

  • Snapshot steps can derive a separate artifact name for each run.
  • Task run output shows the capture from the matching snapshot step.

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

canersoz-scaleai and others added 2 commits October 21, 2026 22:53
…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>
@canersoz-scaleai
canersoz-scaleai requested a review from a team as a code owner October 22, 2026 06:11

@kaihackney-scale kaihackney-scale left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/agent_env/task_step/task_steps/snapshot_agent_state.py
canersoz-scaleai and others added 2 commits October 22, 2026 18:02
…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>
Comment thread src/agent_env/task_step/task_steps/snapshot_agent_state.py
Comment thread src/agent_env/task_step/task_steps/snapshot_agent_state.py
Comment thread src/agent_env/task_step/task_steps/snapshot_agent_state.py
Comment thread src/agent_env/task_step/task_steps/snapshot_agent_state.py Outdated
…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>
@canersoz-scaleai
canersoz-scaleai merged commit 79d1414 into scaleapi:main Oct 23, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants