Skip to content

fix(harnesses): a verifier sidecar removal that times out keeps the verdict and is retried - #234

Merged
antonwolfy merged 2 commits into
intel:mainfrom
antonwolfy:fix/verify-sidecar-stop-timeout
Oct 26, 2026
Merged

antonwolfy merged 2 commits into
intel:mainfrom
antonwolfy:fix/verify-sidecar-stop-timeout

Conversation

@antonwolfy

@antonwolfy antonwolfy commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Follow-up to the review of #218 (thread). The thread had two parts:

  • The sidecar that never gets ready was fixed in 243ec2f.
  • The second part was left as "a choice for the author" and merged unchanged. _verify_phase stops the -verify sidecar in a finally with stop_proxy, which raises TimeoutExpired when its docker rm times out. That replaced the reward the verifier had just computed with a TIMEOUT cell. close() could not retry the removal either, because the sandbox did not know the verifier sidecar, which stayed on the network close() removes.

On main, take a fake docker whose rm -f <name>-verify times out. docker-oracle with --network verify=allowlist:pypi.org, on a task whose test.sh passes, gives returncode 124 and reward 0.0. After the verifier, the only removal of the sidecar is the one that timed out.

Changes

dokimi/harnesses/container_agent.py:

  • verifier_sidecar() records the sidecar on its sandbox.
  • The new stop_proxy_quietly() is stop_proxy for teardown. A removal that times out is logged to run.log ([sidecar removal] <name> timed out after ...), not raised.
  • close() stops the verifier sidecar, then the solve sidecar, with it. A removal that timed out is therefore retried before network rm.
  • verifier_sidecar() records the sidecar before its start, so close() also retries one whose start failed and whose removal then timed out; a removal there that works marks it stopped.
  • open() also clears a -verify sidecar a crashed run with the same id left, before it removes and creates the network.

dokimi/harnesses/docker_solver.py:

  • _verify_phase stops the verifier sidecar with stop_proxy_quietly, so the verdict and metrics.network.verify_egress stand. The file keeps its 900 lines: one call changes.
  • The solve sidecar's stop before the verifier is unchanged and still raises. The verifier must not start while that sidecar may still run (test_stop_proxy_timeout_aborts_before_verification).

Changelog fragment (Fixed).

Risk

Only the teardown after the verifier changes. The log line close() writes for a timed-out removal of the solve sidecar now names the sidecar. A run without an allowlisted verifier has no verifier sidecar, so its teardown makes the same docker calls as before.

Testing

On a Linux review host, at 84c9f2c (on main 54ce188):

  • make ci-local CI_LOCAL_ARGS='--with-docker': 33 passed, 0 failed, 6 skipped (test-latest, the cli-surface packaging step, conda-forge-deps without micromamba, docs deploy, the pip-audit report, semgrep). 8,203 tests passed, 2 skipped; changed-line coverage 100%.
  • From the review of 1d2dbd8: a verify sidecar whose failed start cannot be removed gets its retry in close(), one whose removal worked is not touched again, and open() clears a leftover -verify sidecar. The first two fail at 1d2dbd8; 4 mutants each fail a test (no leftover removal, the sidecar recorded after the start, a failed start never or always marked stopped).
  • test_a_verify_sidecar_removal_that_times_out_keeps_the_verdict_and_is_retried: on main it gives (124, 0.0) for (0, 1.0). Here the verdict and verify_egress stand, and the sidecar gets two removals after the verifier, both before the last network rm.
  • 5 mutants each fail a test: _verify_phase back on stop_proxy, the verifier sidecar not recorded, close() skipping it or stopping only the solve sidecar, and stop_proxy_quietly raising.
  • The existing cleanup-timeout tests pass, including the one that stops the cell when the solve sidecar's removal times out before the verifier; the one that counts the solve sidecar's removals now leaves the verifier sidecar's leftover removal out.
  • With the four follow-up branches merged together on main 54ce188 (0a5717c): the full suite (8,232 passed, 2 skipped), the growth check, mypy and ruff pass.

Checklist

  • Tests added/updated and pytest passes
  • ruff, mypy, and schema checks pass
  • Docs updated (or N/A) — see the docs-drift checklist in CONTRIBUTING.md
  • No ad-hoc run artifacts committed
  • New files carry the SPDX license header (or are exempt in .github/.licenserc.yaml)

…erdict and is retried

In the intel#218 review the verifier sidecar's teardown had a second gap,
left to the author and not changed before the merge: `_verify_phase`
stops the `-verify` sidecar in a `finally` with `stop_proxy`, which
raises `TimeoutExpired` when `docker rm` times out. That replaced the
reward the verifier had just computed with a TIMEOUT cell (exit 124,
reward 0), and `ContainerSandbox.close()` could not retry, because it
did not know the verifier sidecar, which then stayed on the network
that `close()` removes.

`verifier_sidecar()` now records the sidecar on its sandbox. The verify
phase stops it with the new `stop_proxy_quietly`, which logs a timed-out
removal instead of raising, and `close()` uses it for both sidecars, so a
removal that timed out is retried before `network rm`. The solve
sidecar's stop before the verifier still raises: the verifier must not
start while it may still run.

`docker_solver.py` keeps its 900 lines. Each of 5 mutants of the fix
fails the new test.

Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
@codecov-commenter

codecov-commenter commented Oct 26, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.94%. Comparing base (54ce188) to head (84c9f2c).
⚠️ Report is 19 commits behind head on main.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #234      +/-   ##
==========================================
+ Coverage   92.25%   92.94%   +0.69%     
==========================================
  Files         239      241       +2     
  Lines       29937    30653     +716     
  Branches     4918     5060     +142     
==========================================
+ Hits        27618    28491     +873     
+ Misses       1632     1460     -172     
- Partials      687      702      +15     
Files with missing lines Coverage Δ
dokimi/dokimi/harnesses/container_agent.py 97.87% <100.00%> (+0.05%) ⬆️
dokimi/dokimi/harnesses/docker_solver.py 97.73% <100.00%> (ø)

... and 45 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vchamarthi

Copy link
Copy Markdown
Contributor

@exai-bot /review

@exai-bot exai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note

AI-generated review of 1d2dbd8b5f48, requested by @vchamarthi, using the default review skill. It can be wrong: verify a finding before acting on it. React with a thumbs up or down on a finding to tell the bot whether it helped.
Model us.anthropic.claude-sonnet-5-5, effort default, 3 of 80 turns, 0.2 of 40 minutes, 59k in / 1k out tokens.

The change records the verifier sidecar on its sandbox and adds stop_proxy_quietly, which logs a timed-out docker rm instead of raising. _verify_phase uses it, so a removal timeout no longer replaces the computed reward. close() now stops the verifier sidecar and then the solve sidecar, so a failed removal is retried before network rm. I found no defects. The one removal path that still raises is the solve sidecar's stop before the verifier, and that is intended. The new test covers the verdict, verify_egress, and the retry happening before network rm.

No issues found.

Not verified

I did not run the tests. I read stop_proxy's retry behaviour. A retry after the sidecar is already gone keeps the first successful log read (_log_read), and _stopped makes a second call a no-op. That reading supports the claim that verify_egress stays valid.

@napetrov napetrov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review of 1d2dbd8. No fixes pushed; I found nothing that needs a change.

Summary. _verify_phase now stops the -verify sidecar with stop_proxy_quietly, so a timed-out docker rm after test.sh no longer replaces the verdict. close() retries the verifier sidecar's removal, then the solve sidecar's, before network rm. The solve sidecar's stop before the verifier still raises, as intended. This adds nothing to the earlier bot review; I checked its points by running the tests.

Open items. None blocking.

  • Nit (introduced, optional): _verifier holds only the most recent verifier sidecar. That is fine today because _verify_phase makes one per run. If a second verifier sidecar is ever started on the same sandbox, the first would no longer be retried.

Test evidence (Linux review host, head 1d2dbd8)

  • uv run pytest tests/test_network_policy.py tests/test_container_agent*.py tests/test_docker_solver*.py: 172 passed, 1 skipped.
  • Mutant: with _verify_phase back on stop_proxy, the new test fails. The new test therefore covers the fix.
  • ruff check dokimi tests: clean.

@vchamarthi vchamarthi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved.

Sidecar-removal timeout no longer replaces the verdict, and close() retries it before network rm. Read stop_proxy and close at 1d2dbd8: a retry keeps the first log read (_log_read) and is a no-op once _stopped, so verify_egress stays valid. No defects found.

CI at send time: 0 failing, 0 pending.

@napetrov napetrov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving at 1d2dbd8: reviewed, CI green, no blocking items open.

…o start, or a crashed run left

From vlad-perevezentsev's review of intel#234:

- `verifier_sidecar()` recorded the sidecar on its sandbox only after a
  successful start. If the start failed and the removal in its `except`
  timed out, `close()` did not know the sidecar, which stayed on the
  run's network. It is recorded before the start now; a removal there
  that works marks it stopped, so `close()` does not touch it again.
- `open()` cleared a crashed run's container and solve sidecar, but not
  its `-verify` sidecar, so a leftover one still held the network and
  `network create` failed the next cell with the same id. It is cleared
  with the others.

The test that counts the solve sidecar's removals on a timeout now
leaves the verifier sidecar's leftover removal out of its count.

Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
@antonwolfy

Copy link
Copy Markdown
Contributor Author

@napetrov on the optional nit: I kept a single _verifier. _verify_phase starts at most one verifier sidecar per run, so a list would always hold one item. If a second phase ever starts one, that change is the place to make it a list. @vlad-perevezentsev's two points are fixed in 84c9f2c; see the threads.

@antonwolfy
antonwolfy merged commit bd76adc into intel:main Oct 26, 2026
17 checks passed
@antonwolfy
antonwolfy deleted the fix/verify-sidecar-stop-timeout branch October 26, 2026 23:12
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.

6 participants