Skip to content

feat(task_step): set_env_clock_rate starts or stops an armed env clock in place - #631

Open
andrewpark-scaleai wants to merge 3 commits into
mainfrom
andrew-park/env-clock-rate
Open

andrewpark-scaleai wants to merge 3 commits into
mainfrom
andrew-park/env-clock-rate

Conversation

@andrewpark-scaleai

@andrewpark-scaleai andrewpark-scaleai commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Why

A graded run should give the agent the virtual time its task was written for, and its verifiers should read a world that stopped when the agent stopped. Today a task can do neither:

  • sync_env_clock arms the clock, which re-anchors it. A task that needs the clock armed during setup, for trigger registration or warmup, has to arm it early. The agent then starts late on the virtual timeline, and nothing resets it without re-anchoring every relative time trigger.
  • Nothing stops the clock after the agent's last prompt_agent. Verifiers that read the env afterwards see time triggers keep firing while they read.

The gateway already supports both cases. clock/v1 set_rate changes an armed clock's rate from its current time, keeping t0, the generation and every time trigger's mark. Rate 0 stops time triggers and virtual waits. No task step called it.

What

  • New step set_env_clock_rate (env_id, virtual_seconds_per_real_second, timeout_seconds). It calls set_rate and appends {step_id, env_id, virtual_seconds_per_real_second, virtual_time} to context.metadata["clock_configurations"]. It fails when the env isn't deployed or its clock isn't armed, and refuses a negative rate at load.
  • sync_env_clock's docstring names the setup pattern.
  • The step is registered, classified in episode/rules.py and colored in the explorer's pipeline view.

Two patterns:

sync_env_clock(rate=0) -> setup steps -> set_env_clock_rate(1.0) -> prompt_agent   # agent starts at the armed time
prompt_agent -> set_env_clock_rate(0) -> verifiers                                 # verifiers read a stopped clock

Stopping the clock does not stop work already in flight: a trigger action mid-firing finishes, and action triggers still fire on calls by the roles they watch.

Tests

  • tst/unit/task_step/set_env_clock_rate_test.py drives the step against a real gateway Clock. A clock held at rate 0 through setup starts at its armed time. A stopped clock holds its time and records it. An unarmed clock and a missing env fail the step.
  • tst/unit/env/gateway/triggers_time_test.py: a relative time trigger doesn't fire while the clock is stopped, and fires on restart at its original mark.
  • make lint, make unit-test (14625 passed, 8 skipped).

Docs: the site's task-step reference should list set_env_clock_rate beside sync_env_clock, with the two patterns above.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge, with no outstanding findings.

Summary

Adds set_env_clock_rate so tasks can start or stop an armed clock without resetting its time or trigger marks.

  • Tasks can change an armed environment clock without resetting its timeline.

Reviews (4) · Last reviewed commit: "test(gateway): a clock stopped at rate 0..." · Reviewed by Greptile

…k in place

A task could arm an env's clock only with sync_env_clock, which re-anchors it.
So a task that needs the clock armed during setup started its agent late on the
virtual timeline. Nothing could stop the clock after the agent's last turn, so
verifiers read a world whose time triggers kept firing.

set_env_clock_rate calls clock/v1 set_rate. It keeps the clock's time and its time
triggers' marks. Rate 0 stops time triggers and virtual waits.
@andrewpark-scaleai
andrewpark-scaleai requested a review from a team as a code owner November 12, 2026 03:04
Ubuntu and others added 2 commits November 12, 2026 16:36
…s error

When an env is deployed more than once, the step changed the first
deployment's clock, which may not be the one the agent uses. It now selects
the deployment through bound_deployment: env_step_id names the deploy_env
step, and an ambiguous or unmatched selection fails the step before any
clock write.

A refused rate change reports the HTTP status and the gateway's error
field, cut to 300 characters, instead of the whole response body.
@andrewpark-scaleai

Copy link
Copy Markdown
Contributor Author

@greptileai review

The one open finding (time triggers firing after the clock stops) was withdrawn in its thread after test_stopping_the_clock_fires_only_the_marks_it_already_passed landed in 103479e, so the summary's 4/5 reflects a finding that no longer stands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant