Skip to content

fix(runner): let an environment own the configuration it installed - #5746

Open
mmabrouk wants to merge 1 commit into
agent-config-editing-planfrom
agent-config-editing-s5
Open

fix(runner): let an environment own the configuration it installed#5746
mmabrouk wants to merge 1 commit into
agent-config-editing-planfrom
agent-config-editing-s5

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Aug 5, 2026

Copy link
Copy Markdown
Member

Context

Part of the agent-config-editing stack. Targets agent-config-editing-plan. Read the stack bottom up.

A warm session could serve a turn while running a different configuration than the one the request asked for. The session pool stored a configFingerprint that its caller passed in, and the caller passed the incoming request's fingerprint. On the ordinary path that is harmless, because the dispatch has already proved the incoming and parked configurations equal. On the approval-resume path it is not. That branch never compares configurations, so re-parking stamped the environment with a configuration it had never applied. The next turn read that stamp, found a match, and continued warm on an environment running something else.

Two smaller costs sat next to it. Committing a revision mid-conversation threw away a warm sandbox, because the workflow revision id was part of environment identity. And every configuration mismatch deleted the sandbox, even when the sandbox itself was fine.

Changes

The environment now owns its applied state and the pool reads it. ParkInput has no configFingerprint field for a caller to fill in, and LiveSession.configFingerprint is a read-only view of environment.appliedState. The bug is not patched, it is unrepresentable: there is no longer a place to write a configuration the environment never installed. commitApplied is the only writer, and it runs after a successful acquire.

Before: pool.park({ key, environment, configFingerprint: configFingerprint(request), ... })
After: pool.park({ key, environment, ... }), and the pool reads environment.appliedState.configFingerprint.

configFingerprint no longer hashes the workflow revision id, the revision version, or the draft flag. Those are turn metadata, not environment identity. Nothing in the sandbox, the daemon, the workspace, or the harness session changes when a revision id changes, so committing a revision now keeps the warm session instead of rebuilding it. They stay in runContext for tool binding and observability.

Teardown reasons now name the layer that failed instead of collapsing into one compatibility-mismatch. A wrong harness session or an invalid conversation parks the sandbox, because the daemon and its installed credentials are sound. Stale daemon material or a wrong sandbox still deletes. The parkable set is an allowlist, so a new reason deletes until somebody proves its sandbox is safe to reuse. compatibility-mismatch stays as a deprecated alias that deletes.

The runner also remembers the sandbox ids it deleted, so a stale pointer to one of them skips the failed reconnect and goes straight to a fresh create. This is per-process and in-memory. Another replica still reads the stale id and still falls through correctly. It saves a round trip; it is not a durable guarantee.

Tests / notes

  • 20 new tests, including a 729-line characterization suite that pins today's session lifecycle behavior before the later lanes move any of it. The rest of the stack is reviewable because this suite exists.
  • Worth poking at: the teardown allowlist. If you think a reason belongs on the parkable side, say so here rather than later, because a wrong entry leaves stale credentials inside a resumed daemon.

What to QA

  • Run a turn in the playground, commit a revision, then send another message. The session stays warm. Before this change the commit rebuilt the sandbox.
  • Approve a gated tool call, let the turn resume, then send a follow-up message that changes nothing. The follow-up still reuses the warm session and does not silently run an older configuration.

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview Aug 6, 2026 5:14pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b60e2fdc-bdff-483f-a0bc-c3439a5400e0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved sandbox session reuse by tracking the configuration actually applied to each environment.
    • Added clearer handling for lifecycle mismatches, allowing safe sessions to be parked while deleting incompatible environments.
    • Automatically avoids reconnecting to sandboxes known to have been destroyed.
    • Revision metadata changes no longer unnecessarily invalidate warm sessions.
  • Bug Fixes

    • Sandbox destruction is now recorded even when deletion encounters an error.
    • Re-parked sessions retain their applied configuration rather than incoming request metadata.

Walkthrough

The runner now tracks the configuration applied to each environment. Session reuse and reparking use that state. Lifecycle mismatches map to park or delete actions. Destroyed sandbox IDs block stale reconnects.

Changes

Session lifecycle migration

Layer / File(s) Summary
Applied state contracts and environment wiring
services/runner/src/engines/sandbox_agent/applied-state.ts, environment-setup.ts, runtime-contracts.ts
Adds generation-tracked applied state with defensive snapshots and commit-only updates. Environments expose this state through SessionEnvironment.
Session identity and applied-state pooling
services/runner/src/engines/sandbox_agent/session-identity.ts, session-pool.ts
Excludes workflow revision metadata from configuration fingerprints. Session and parked-session fingerprints derive from the environment’s applied state.
Mismatch disposition and sandbox reconnect handling
services/runner/src/engines/sandbox_agent/teardown.ts, server.ts, environment.ts, sandbox-reconnect.ts
Adds layer-specific teardown reasons, parks session and continuity mismatches, deletes runtime and sandbox mismatches, and skips reconnects for recently destroyed sandbox IDs.
Lifecycle characterization and regression coverage
services/runner/tests/unit/session-lifecycle-characterization.test.ts, session-pool.test.ts, session-keepalive-approval.test.ts, session-keepalive-dispatch.test.ts, teardown.test.ts
Adds coverage for applied-state ownership, fingerprint identity, approval resume, parking, deletion, teardown dispositions, reconnect handling, and session-pool migrations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • Agenta-AI/agenta#5156: Extends the same session keep-alive implementation across session pooling, server lifecycle handling, and tests.
  • Agenta-AI/agenta#5158: Overlaps with session parking, reparking, and teardown lifecycle changes.
  • Agenta-AI/agenta#5225: Overlaps with sandbox-agent lifecycle, reconnect, teardown, fingerprint, and pointer persistence changes.

Sequence Diagram(s)

sequenceDiagram
  participant RunnerServer
  participant SessionPool
  participant SessionEnvironment
  participant Teardown
  RunnerServer->>SessionPool: evaluate reusable session
  SessionPool->>SessionEnvironment: read applied configuration
  SessionEnvironment-->>SessionPool: return applied fingerprint
  SessionPool-->>RunnerServer: reuse or report mismatch
  RunnerServer->>Teardown: classify mismatch
  Teardown-->>RunnerServer: park or delete environment
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: environments own the configuration they installed.
Description check ✅ Passed The description directly explains the applied-state change, lifecycle fixes, teardown behavior, and related tests.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent-config-editing-s5

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread services/runner/src/engines/sandbox_agent/session-pool.ts
Comment thread services/runner/src/engines/sandbox_agent/teardown.ts
Comment thread services/runner/src/engines/sandbox_agent/session-identity.ts
* That path was always correct and stays correct. This is a latency fix with a correctness-shaped
* name, and treating it as a durable guarantee would be wrong.
*/
const destroyedSandboxIds = new Set<string>();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This set is a latency fix with a correctness shaped name, and the distinction matters.

The stored sandbox pointer is the latest turn's sandbox_id, and the turns table is append only. There is no pointer row to clear when a destroy deletes the sandbox, so the dead id stays readable until the next turn appends its own row. Reconnecting to it fails and falls through to a fresh create.

That fallback was always correct and stays correct. This set only skips the round trip, inside one runner process.

It is in memory and per process. Another replica, or this replica after a restart, still reads the stale id and still falls through. Treating this as a durable guarantee would be wrong.

@mmabrouk

mmabrouk commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mmabrouk

mmabrouk commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

lgtm

@mmabrouk mmabrouk added the lgtm This PR has been approved by a maintainer label Aug 6, 2026
@mmabrouk
mmabrouk marked this pull request as ready for review August 6, 2026 10:56
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. Backend labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-5746.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-5746-9d7767c
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-08-06T15:02:44.018Z

…s 1-2)

The environment now owns its applied state; park and repark no longer
accept request-derived fingerprints, so the approval-stale-config bug is
unrepresentable. Four named teardown reasons with a parkable allowlist:
a config change stops the sandbox instead of deleting it, and true
incompatibility still destroys with the reconnect pointer cleared.
Revision id, version, and draft flag leave the fingerprint, so a commit
with identical content keeps the warm session.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant