Skip to content

Prepare an agent whose native release inspection was blocked - #361

Merged
ifdotpy merged 1 commit into
mainfrom
fix/native-release-blocked-hold
Oct 20, 2026
Merged

ifdotpy merged 1 commit into
mainfrom
fix/native-release-blocked-hold

Conversation

@ifdotpy

@ifdotpy ifdotpy commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Problem

A live lead agent (00399ad7) is failed with thread-preparation/NativeRelease. Its pending rule input from 2026-10-19 never runs. Its saved nativeRelease is {"phase":"blocked","error":"native background command is active", "resetPending":false, ...} from 2026-10-18, on a connection of an earlier backend.

blocked means the idle-release inspection found native work and never sent thread/unsubscribe. The thread stayed loaded.

Root cause

account_worker/preparation/snapshot.rs (thread_snapshot, admission) held every agent whose nativeRelease.phase was not resumed or released. The Rust backend has no native release driver, so nothing settles such a record. Every new input re-admits the agent, and the hold maps to PreparationRefused, so the agent fails again (prepare_ready in pipeline.rs).

Python prepares such an agent. Runtime.prepare_locked refuses only a truthy resetPending, and codex_native_release.reconcile_unknown acts only on unknown, checking or unsubscribing.

Change

The new native_release_held function holds only in the cases where Python does not prepare:

  • resetPending is truthy.
  • The phase is unknown, checking or unsubscribing, and the release is a captured reset (resetActorEpoch) that waits for exact native closure.
  • The phase is unknown, checking (without inspectionPhase) or unsubscribing on the current thread and connection. Python retries thread/unsubscribe here. This adapter has no release driver, so it holds.

All other releases prepare normally. This includes blocked, a superseded inspection, and an unsettled release on an earlier connection.

A failed hold that remains is still a failed start, as in Python (start_error after a raise from prepare_locked).

Tests

  • New account_worker::tests::preparation_review::native_release_phases_match_python. It runs the real Python Runtime.prepare_locked and reconcile_unknown over 13 saved releases, with only the runtime host replaced. Then it runs the production Rust turn preparation over the same rows. It fails on main at blocked-earlier-connection (the live record shape).
  • account_transfer_executor test: the fabricated {"phase":"pending"} blocker is now a real hold (resetPending: true).
  • cargo test -p studio-runtime-services --lib -- account_worker account_transfer thread_prepare: 292 passed. The 7 failures are the known failures on base (auth_hold x5, publication_off_path x2).
  • --test thread_prepare_corpus, --test account_transfer_corpus, --test account_transfer_driver: all pass.
  • cargo clippy -p studio-runtime-services --lib --tests: no findings in the changed files.
  • just test-fast (local): it stopped at "server safety witnesses". The failure was limit-reset-http-contract.py setUpClass: the backend did not start in my isolated environment. This PR changes no Python. The later stages did not run locally, so hosted CI is the fast-tier evidence.
  • Hosted CI on commit 53adaab: all 16 checks pass, including the fast tier (Rust workspace 1-4, server 1-4, remaining checks, browser smoke).

Live state (read-only check)

13 agents have nativeRelease.phase not in (resumed, released). 12 are blocked and 1 is checking. 11 of them are deleted. The two that are not deleted:

  • 00399ad7 (failed, 2 pending inputs).
  • d5dd7220 (completed, no pending input). It would fail on its next input without this fix.

The thread snapshot held every agent whose saved nativeRelease phase was
not resumed or released. A blocked inspection never unsubscribed, so the
thread stayed loaded, and Python prepares such an agent normally. The Rust
backend has no release driver to settle the record, so the hold never
cleared, and each new input failed with thread-preparation/NativeRelease.

Hold only where Python Runtime.prepare_locked and reconcile_unknown do not
prepare: a pending tool reset, or an unsettled unknown, checking or
unsubscribing release that waits for a captured reset or retries
thread/unsubscribe on the current thread and connection.

The new test runs the real Python preparation over each saved release
and compares it with the production Rust preparation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ifdotpy
ifdotpy merged commit 89502be into main Oct 20, 2026
16 checks passed
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