Skip to content

refactor(harnesses): docker-* solver, sandbox and probe on the process runner - #104

Open
napetrov wants to merge 10 commits into
intel:mainfrom
napetrov:refactor/docker-solver-on-runner
Open

napetrov wants to merge 10 commits into
intel:mainfrom
napetrov:refactor/docker-solver-on-runner

Conversation

@napetrov

@napetrov napetrov commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Part of #4.
Follow-up to #29 and #79.

Summary

The docker-* harnesses (docker-oracle, docker-claude[-host][-skill:], docker-probe[-host]) were the last spawn sites outside dokimi/process.py. Now every docker step, both claude launches and both isolation probes go through the harness's runner. create_harness(runner=) and TaskSuiteRunner(runner=) reach them as they reach the other harnesses.

 DockerSolverHarness.run                          (+ runner= field)
   _docker(args)                                  # 43 call sites: 22 in docker_solver, 21 in ContainerSandbox
-    subprocess.run(timeout=None | budget)        # most steps unbounded
+    runner(Env.inherit, capture_limit=None, timeout=budget or 600 s)
   _build_image          docker build            build_timeout_sec       (unchanged)
   _solve_oracle         docker exec solve.sh
-                                                no limit
+                                                agent budget -> reward 0, rc 124
   _solve_claude (host)  run_solver
-    subprocess.run(env, timeout)                 # kills claude only
+    runner(Env.only((), env), capture_limit=None) # kills claude's whole tree
   _solve_claude_in_container
     run_solver(docker exec ... claude)           same path; agent env still in a 0600 --env-file
   _solve_probe
     ContainerSandbox.run_probe
-      subprocess.run(timeout=120)
+      self.runner(Env.only((), client_env), timeout=120)
     run_probe (host)                             already on the runner; now gets the harness's
   _verify               docker exec test.sh     verifier timeout        (unchanged)
   everything else       cp, inspect, exec mkdir/chmod/rm, run -d, rm -f, network *, sidecar create/start/logs, recompile
-                                                no limit
+                                                600 s

Output is kept whole (capture_limit=None), in memory, as subprocess.run kept it. The runner's default 1 MiB head and tail would cut solver.jsonl, the tool counts and the sidecar's decision log. Writing it to a spool file would put the stream on disk before it is redacted.

Tests drive the pipeline through DockerCLI (tests/docker_cli_fake.py), a FakeRunner that answers like a daemon with one healthy task container and stands in for claude. They no longer patch _docker, subprocess.run or the private steps.

Evidence

  • Tree kill, before/after. A stand-in claude (a real process) starts a tool process that sleeps, then hangs past a 1.5 s budget:

    run_solver(["claude", ...], timeout=1.5)        # same script against both trees
    main:   FAILED  AssertionError: the agent's tool process outlived the cell
    branch: 1 passed
    

    In the repo: test_a_timed_out_host_agent_leaves_none_of_its_tools_running runs it through docker-claude-host (cell rc 124, reward 0, tool gone).

  • Budgets. test_every_step_runs_with_a_budget[docker-oracle|docker-claude|docker-claude-host] checks every call's timeout:

    step main now
    docker build build_timeout_sec same (overrun: errored)
    solve.sh (oracle) none agent budget (overrun: reward 0, rc 124)
    claude host / in container agent budget same (host: whole tree killed)
    test.sh verifier timeout same
    container probe / host probe 120 s / 60 s same
    every other docker step none 600 s

    test_solve_or_verify_timeout_is_a_failed_cell[solve.sh] (reward 0, rc 124, partial output in run.log) and test_a_docker_step_without_a_budget_that_hangs_errors_the_cell (docker cp failed (timed out after 600s), env is the caller's).

  • Whole stream. test_a_stream_over_the_capture_limit_is_kept_whole: a 1.5 MiB stream with 3 tool_use events and a 1 MiB verifier log. With the runner's default capture: tool_calls == {}, so it fails. Now: {"Bash": 3}, solver.jsonl is byte-identical and the log's middle is in run.log.

  • Fake CLI instead of patches. Across test_docker_solver_harness, test_container_agent, test_agent_sandbox, test_docker_task_md, test_task_formats and test_skill_treatment: 92 monkeypatches of _docker / subprocess.run / private steps on main, 0 now. test_nothing_else_in_the_package_spawns_a_process fails on any new subprocess/os.system/asyncio spawn outside process.py. test_a_hung_container_probe_is_untested_not_sealed covers the probe's timeout.

  • No real CLI ran in tests. The suite ran under a spawn guard. It is a pytest plugin that raises on any spawn that resolves to an installed claude/codex/harbor/bench/docker, plus PATH shims for grandchildren. Two random seeds: 5187 passed, 2 skipped each, blocked=0, 0 shim hits. The timing-sensitive files (process runner, sandbox, container agent, docker solver, task.md; 313 tests) ran 3x in parallel: all passed.

  • Local CI. make ci-local --offline: 14 passed, 0 failed (ruff, mypy strict with no new exemption, tests, schema, docs build, changelog fragment). Coverage of changed lines: 100% (73 lines).

  • CI. All checks pass, including Tests on py3.11 to 3.14 and the external catalog smoke test, which runs a real uxlfoundation/skills task through docker-oracle and docker-probe against Docker.

Merge Danger

Door: two-way. No schema, artifact field or persisted format changes. Reverting restores the old spawn code.

Blast Radius: task-cells

  • docker-oracle: a solve.sh that overruns the agent budget (agent.timeout_sec, else 600 s) is now a failed cell, reward 0, rc 124, and the verifier does not run. Before, it waited with no limit. Oracle pass rates can drop for tasks whose oracle is slower than their own agent budget.
  • Steps that had no budget now get 600 s:
    • One that must succeed and runs out (copy-in, mkdir, chmod, docker run -d, the sidecar's create, which may pull its pinned image) errors the cell: docker <step> failed (timed out after 600s).
    • One whose failure was already tolerated treats running out as that failure: rm -f and network rm are ignored, the recompile lands in recompile_failed, and the reward-file copy falls back to the verifier's exit code.
  • A killed step's partial output now goes to run.log. Before, only the TIMEOUT line was written.
  • docker-claude-host: a timeout kills claude's whole process group, and so does its exit. A background process the agent started no longer outlives its cell. Ctrl-C, or SIGTERM to the dokimi CLI, kills a running docker step's or claude's group.
  • docker and claude output is decoded as UTF-8 with replacement. Before, it was decoded in the locale's encoding.
  • Library API: DockerSolverHarness has a runner field, and ContainerSandbox.open takes runner=. solver_stream.run_solver takes keyword-only timeout/env/cwd/runner and returns process.Completed, which record_solver and container_agent.Docker now use too.
  • Security properties unchanged:
    • The in-container agent stays on the --internal network with egress only through the sidecar to the LLM hosts.
    • Its env stays in a 0600 --env-file, never on argv. The docker client gets exactly its old env (Env.only((), client_env)).
    • The host agent gets exactly workspace.env(...), the allow-list. The docker CLI keeps the caller's env (Env.inherit).
    • Order of steps: the agent uid is reaped, then the plugin and the credential files are removed, then /tests is copied in. The verifier's proxy credentials are still stripped, and the proxy is still stopped before a public task joins bridge.
    • run.log and solver.jsonl are redacted before they are written. Output is never spooled raw to disk.
    • Isolation probes: a hung probe is still untested and reward 0, with the same 120 s and 60 s.
    • These are covered by the existing launch, egress and isolation tests, ported from patches to DockerCLI.

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)

🤖 Generated with Claude Code

@codecov-commenter

codecov-commenter commented Oct 19, 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 90.31%. Comparing base (964c87f) to head (aca2f9f).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #104      +/-   ##
==========================================
+ Coverage   90.26%   90.31%   +0.04%     
==========================================
  Files         206      206              
  Lines       24347    24371      +24     
  Branches     3921     3927       +6     
==========================================
+ Hits        21978    22010      +32     
+ Misses       1703     1697       -6     
+ Partials      666      664       -2     
Files with missing lines Coverage Δ
dokimi/dokimi/harnesses/container_agent.py 94.55% <100.00%> (-0.69%) ⬇️
dokimi/dokimi/harnesses/docker_solver.py 97.54% <100.00%> (+2.87%) ⬆️
dokimi/dokimi/harnesses/registry.py 91.17% <100.00%> (-0.26%) ⬇️
dokimi/dokimi/harnesses/solver_stream.py 100.00% <100.00%> (ø)
dokimi/dokimi/process.py 89.06% <ø> (ø)

... and 1 file with indirect coverage changes

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

Copilot AI 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.

🟡 Changes recommended

Cleanup and recompilation timeouts can allow verification to proceed while container-side processes or credentials remain present.

3 open issues
What changed in this PR

Completes the shared process-runner migration for Docker task harnesses, following #29 and #79.

Changes:

  • Routes Docker, solver and probe launches through the injected runner.
  • Adds bounded Docker steps and oracle execution while preserving solver output.
  • Replaces subprocess patches with fake-runner tests and documents timeout behavior.
File Description
dokimi/​tests/​test_task_formats.py Uses injected runners for format tests.
dokimi/​tests/​test_skill_treatment.py Updates stream and timeout tests.
dokimi/​tests/​test_process.py Guards against bypassing the process runner.
dokimi/​tests/​test_docker_task_md.py Migrates task.md harness tests.
dokimi/​tests/​test_docker_solver_harness.py Tests budgets and complete output capture.
dokimi/​tests/​test_container_agent.py Updates lifecycle and isolation tests.
dokimi/​tests/​test_agent_sandbox.py Adds host-agent process cleanup coverage.
dokimi/​tests/​docker_cli_fake.py Adds a reusable Docker CLI fake.
dokimi/​dokimi/​process.py Updates the module documentation.
dokimi/​dokimi/​harnesses/​solver_stream.py Runs solvers through the shared runner.
dokimi/​dokimi/​harnesses/​registry.py Enables runner injection for Docker harnesses.
dokimi/​dokimi/​harnesses/​docker_solver.py Migrates Docker calls and adds budgets.
dokimi/​dokimi/​harnesses/​container_agent.py Routes container probes through the runner.
dokimi/​docs/​coding-harnesses.md Documents timeout and scoring behavior.
dokimi/​docs/​architecture.md Documents centralized process spawning.
dokimi/​changelog.d/​20261018_210000_napetrov_docker-solver-on-runner.md Records behavior and API changes.

Review effort: Balanced

Comment thread dokimi/dokimi/harnesses/docker_solver.py Outdated
Comment thread dokimi/dokimi/harnesses/docker_solver.py
Comment thread dokimi/dokimi/harnesses/docker_solver.py
Copilot AI balanced review requested due to automatic review settings October 19, 2026 08:02

Copilot AI 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.

🟡 Changes recommended

Test isolation, incompatible cleanup doubles, unused imports, and lost telemetry after cleanup timeouts remain unresolved.

6 open issues
1 resolved since last review

Review effort: Balanced

Comment thread dokimi/tests/test_cleanup_timeouts.py Outdated
Comment thread dokimi/tests/test_cleanup_timeouts.py
docker(["rm", "-f", self.proxy_name], log, check=False)
# Require successful removal before marking as stopped.
proc = docker(["rm", "-f", self.proxy_name], log, check=False)
if proc.timed_out:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c653714: both callbacks (the close test and the Ctrl-C test) now return reply(), and reap/stop_proxy read timed_out from the runner's Completed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No change in this push: c653714 already made both callbacks return reply() and reap and stop_proxy read timed_out from the runner Completed.

Comment thread dokimi/dokimi/harnesses/docker_solver.py Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 09:12

Copilot AI 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.

Comment thread dokimi/dokimi/mcp/__init__.py Outdated
Comment thread dokimi/changelog.d/20261018_210000_napetrov_docker-solver-on-runner.md Outdated
…s runner

DockerSolverHarness takes runner= like the other harnesses, and
create_harness passes it on (the pass-through for harnesses without one
is gone). Every docker step, the host and the in-container claude, and
both isolation probes go through it. Nothing in the package spawns
outside dokimi/process.py now, and a test keeps it that way.

- _docker(): a step with its own budget (build, solve, verify) still
  raises TimeoutExpired, so the caller decides the cell. Any other step
  gets 600 s (before: none). Running out errors the cell where the step
  must succeed. A killed step's partial output is written to run.log.
- docker-oracle: solve.sh gets the agent budget (before: unbounded). An
  overrun is a failed cell, reward 0, like an agent timeout.
- docker-claude-host: a timeout kills claude's whole process tree, so
  the tool processes it started no longer outlive the cell.
- run_solver() and _docker() keep their output whole (capture_limit=None).
  The runner's 1 MiB head-and-tail capture would cut solver.jsonl, the
  tool counts and the sidecar's decision log. The output stays in memory,
  as with subprocess.run, so nothing reaches disk before it is redacted.
- run_solver(): exactly the env it is given (Env.only), stdin closed.
  ContainerSandbox carries the runner for the in-container probe.

Tests drive the harness through DockerCLI (tests/docker_cli_fake.py), a
FakeRunner standing for the docker CLI and claude. They no longer patch
_docker, subprocess.run or the private pipeline steps (_build_image,
_start_container, _solve_*, _verify, _recompile, ContainerSandbox.open).

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Three critical timeout handling fixes ensure failed cleanup operations or
timeouts don't allow task scoring to proceed with stale state:

1. stop_proxy timeout (line 235): Raise if sidecar removal times out, preventing
   verification from running with credentials still exposed. close() retries
   removal as best-effort but doesn't re-raise.

2. reap timeout (line 450): Raise if process reap times out, preventing verification
   from running while backgrounded agent processes may still access test files.

3. recompile timeout (line 577): Raise if compilation times out, preventing stale
   or partially-built binaries from being verified, and stopping resource consumption.

All three changes ensure the container is removed without running verification
when these critical pre-verification operations timeout.

Test coverage added for all three scenarios.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 10:35
@napetrov
napetrov force-pushed the refactor/docker-solver-on-runner branch from e2e88ec to ae23b35 Compare October 19, 2026 10:35
…d; drop unused imports

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI 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.

Comment thread dokimi/changelog.d/20261018_210000_napetrov_docker-solver-on-runner.md Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 12:04
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI 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.

🔵 Needs a closer look

The cleanup regression tests depend on developer configuration and can fail before exercising cleanup.

7 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Isolate provider environment and settings in lifecycle tests

dokimi/​tests/​test_cleanup_timeouts.py:27

These tests still read the developer's environment and Claude settings through operator_source_env(). Importing _fixture_task and _pipeline does not activate their module's _hermetic fixture. For example, ANTHROPIC_BASE_URL=http://localhost:4000 makes all four tests fail in preflight before reaching cleanup. Isolate the provider/proxy environment and settings here, as the other Docker lifecycle tests do.

2 resolved since last review

Review effort: Balanced

@antonwolfy antonwolfy 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 at aca2f9f. The runner migration itself is sound: budgets, env handling and the credentials-off-argv path hold up. The problems are the cleanup steps the PR left on check=False. Two of them can now score a cell as a pass when a step times out. I'd fix those before merge.

What I ran, on a Linux review host at aca2f9f:

  • make ci-local with docker: 23 passed, 0 failed, 3 skipped. Changed-line coverage gate passed.
  • Reproductions for the code findings through the DockerCLI fake, no real docker, claude or LLM.
  • Targeted mutants of the new code (timeout dropped from the oracle, build, verify and recompile calls, the timed_out check in _recompile, redaction, credentials=True for the verifier, the FILES_DIR removal). Of those I have results for, two survive: the oracle's 600 s default changed to 6000 s, and the [killed after ...] line dropped from run.log.

Documentation

  1. Documentation does not cover the reap and sidecar-removal timeouts. I agree with @copilot-pull-request-reviewer on the recompile line in the changelog. Two more cases: reap() (container_agent.py:223) and stop_proxy() (container_agent.py:337) also raise on a timeout, and run() turns that into reward 0, rc 124. The changelog lists rm -f under steps whose failure is tolerated, and coding-harnesses.md:881-890 lists only the agent, verifier and oracle overruns as reward 0. A slow daemon will read as a model failure in a pass-rate table.

Checked, no issues

  • Env: the docker client gets Env.inherit only for the CLI; claude and the probe get Env.only((), env) with the same dict as before. Agent env stays in the 0600 --env-file.
  • Callers of run_solver / record_solver: both call sites in docker_solver.py pass timeout, env and runner; record_solver only uses fields Completed has.
  • Sidecar: a stop_proxy timeout leaves _stopped false, close() retries, and test.sh never runs after it.
  • Registry: create_harness(runner=) reaches all docker-* adapters.
  • Repo rules: changelog fragment present, SPDX headers on new files, no generated artifacts, no nav change needed.

Comment thread dokimi/dokimi/harnesses/docker_solver.py Outdated
Comment thread dokimi/dokimi/harnesses/docker_solver.py Outdated
Comment thread dokimi/dokimi/harnesses/docker_solver.py
Comment thread dokimi/dokimi/harnesses/container_agent.py Outdated
Comment thread dokimi/tests/test_cleanup_timeouts.py Outdated
Comment thread dokimi/tests/test_cleanup_timeouts.py Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 14:04
…xt copy timeouts

Add tests that verify credential-file removal timeout aborts the cell before
verification, preventing access to leftover credential files. Also add test
for reward.txt copy timeout to ensure the task fails rather than proceeding
with incorrect assumptions.

Update docstrings to use behavior descriptions instead of thread numbers.
Fix assertion to check for exactly 3 removal attempts (open + stop_proxy + close).

Update changelog to clarify that critical cleanup steps abort the cell on
timeout to prevent security issues and stale binary scoring.

Addresses PR intel#104 review threads.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ting the verifier run

The pre-verifier reap, the plugin and credential-file removals, the reward.txt
copy and the recompile now raise on a timeout (reward 0, rc 124). The rest of the
cleanup still runs after a reap timeout, and the solve is recorded before it.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI 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.

if runner is process.run or "runner" not in {f.name for f in fields(adapter)}:
return adapter
return replace(adapter, runner=runner)
return adapter if runner is process.run else replace(adapter, runner=runner)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No change: every adapter create_harness can return (CommandHarness, DockerSolverHarness, BenchflowHarness) declares a runner field, so the replace cannot hit an adapter without one.

Comment thread dokimi/dokimi/cli.py Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 14:22

Copilot AI 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.

🟡 Changes recommended

The unrelated rollbacks reintroduce invalid judge defaults, missed contradictions, and misleadingly cleared novelty checks.

4 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Prevent zero verdicts when conflict coverage is incomplete

dokimi/​dokimi/​gate/​novelty_gate.py:182

Nonempty categories do not establish that conflict detection completed. With zero harmful atoms and conflict_coverage={"pairs_compared": 0, "pairs_total": 45}, this now reports measured=True, passed=True, and value 0. Partial scans, malformed coverage, and capped samples can likewise appear cleared. Restore the coverage and cap guards together with persisted coverage metadata: incomplete scans should withhold a zero verdict, while conflicts actually found should still be reported.

Medium severity Restore whole capped-set batching for cross-chunk conflict detection

dokimi/​dokimi/​novelty/​analyze.py:273

Removing the batch override restores chunks of 40. At the default 120-atom cap, only 2,340 of 7,140 pairs share a call, so contradictions between chunks cannot be detected even when every call succeeds. Restore the whole capped-set batch to preserve cross-chunk conflict detection.

6 resolved since last review

Review effort: Balanced

Comment thread dokimi/dokimi/cli.py Outdated

@antonwolfy antonwolfy 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 at c653714. All six of my earlier threads are fixed, and the cleanup logic in docker_solver.py and container_agent.py now does what the changelog says. The problem is a different one: commit 36d2e48 (test(cleanup-timeouts): add tests ...) also reverts three merged changes that have nothing to do with this PR. I'd fix that before merge.

What I ran, on a Linux review host at c653714:

  • make ci-local with docker: 23 passed, 0 failed, 3 skipped. The changed-line coverage gate passed. schema_compat prints a warning for novelty.v1.json (see item 1).
  • Reproductions for the code findings through the DockerCLI fake, no real docker, claude or LLM.
  • The base's tests for the reverted features run against the PR head.
  • 17 mutants of the cleanup code against test_cleanup_timeouts.py, test_container_agent.py and test_docker_solver_harness.py: 14 killed, 4 survive (see the inline comments and the last item).

Findings that cannot be anchored inline

  1. The PR reverts #72 (conflict_coverage) and #84 (provider-aware judge default), and 36d2e48 hides it. The commit message names cleanup-timeout tests, but the commit changes 30 files and removes 556 lines. Both changes are on the PR's base (964c87f, d9f8c5a). Looks like a stale tree or a bad conflict resolution. Effects at this head:

    • evaluate --product x --judge-provider openai now pairs openai with claude-sonnet-5-5 (base: gpt-6.1-sol). --judge-provider google no longer exits with "has no default judge model; pass --judge-model". parse_args is gone from dokimi.cli; base's tests/test_judge_provider_default.py fails at collection with ImportError: cannot import name 'parse_args' from 'dokimi.cli'. The same applies to the 12 commands the #84 fragment lists. @copilot-pull-request-reviewer raised the CLI part; New evidence: the openai and google results above and the collection failure.
    • harmful_atoms reports a cleared check on an unfinished conflict scan. With zero harmful atoms and conflict_coverage={"pairs_compared": 0, "pairs_total": 45} the gate gives measured=True, passed=True, value=0 (base: measured=False, "compared 0 of 45 atom pairs"). Base's tests/test_novelty_gate.py run against the PR head: 14 failed, 35 passed. analyze is back to batches of 40 (about a third of the pairs at the default cap of 120) and no longer records conflict_coverage.
    • schemas/novelty.v1.json loses the conflict_coverage property with no version bump. Malformed values ({"pairs_compared": -1}, string counts) now validate: base's test_a_negative_or_string_pair_count_fails_the_schema fails with DID NOT RAISE ArtifactValidationError. ci-local prints schema_compat: property 'conflict_coverage' removed from an open object as a warning, not a failure.
    • The two changelog fragments (judge-provider-default, partial-conflict-not-cleared) are deleted, and docs/skill-novelty.md and docs/benchmarking-and-comparison.md go back to the old text ("chunks of 40", 33% of pairs).
    • The PR's own suites pass only because the tests for the reverted behaviour were deleted along with it.
      Suggestion: rebase on the current base and drop every hunk outside the docker-solver change.
  2. The #90 dependency bump is reverted too. .agent-eval/Dockerfile:13 goes from uv==0.13.0 to uv==0.11.9, and dokimi/requirements.lock.txt:17 from anthropic==1.13.0 to anthropic==1.12.0. Both pins were last set by 6e695ce. requirements.txt has anthropic~=1.12, so nothing fails loudly. Same fix as item 1.

I agree with @copilot-pull-request-reviewer on the unhermetic test_cleanup_timeouts.py. New evidence: with ANTHROPIC_BASE_URL=http://localhost:4000 set by an autouse fixture, all 8 tests fail with ValueError: http://localhost:4000 is on the host's loopback from ContainerSandbox.open. Importing _fixture_task and _pipeline does not bring in the _hermetic fixture.

Checked, no issues

  • Earlier threads: the credential and plugin removals, the reward.txt copy and the reap each end the cell with reward 0 / rc 124 and test.sh does not run. The work tree, solver.jsonl and the cost survive a reap timeout (record_solver runs before the cleanup). Mutants that drop the note() calls, the raise stuck, the pulled.timed_out raise or the work copy are all killed.
  • Exception order: when the solve itself times out, the finally runs _stop_container_agent and the original TimeoutExpired wins.
  • Registry: create_harness(id, runner=...) works for every registered id (codex, claude-code, terminal-bench, docker-, benchflow, command). Every adapter has a runner field, so the unconditional dataclasses.replace is safe at this head.
  • Host path: a timed-out copy-back in docker-claude-host raises through check=True and test.sh does not run. The rm -f count of 3 in test_stop_proxy_timeout_retried_in_close is meaningful: dropping the stop_proxy call in close() fails it.
  • Docs: the changelog and coding-harnesses.md match the code for the timeouts that end a cell with reward 0, rc 124 (no metrics.error). Repo rules: SPDX headers, no generated artifacts, not stacked, no nav change needed.

A fourth surviving mutant: removing and stuck is None in note() (first timeout wins) passes all 137 tests. Only the argv in the raised exception changes, so I did not make it a finding.

Comment thread dokimi/tests/test_cleanup_timeouts.py Outdated
Comment thread dokimi/tests/test_cleanup_timeouts.py
Comment thread dokimi/dokimi/harnesses/docker_solver.py
if self._stopped:
return
self._stopped = True
logs = docker(["logs", self.proxy_name], log, check=False)

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.

A timed-out docker logs of the sidecar leaves an empty egress record and the cell keeps its pass.

logs has check=False and timed_out is not read. self.log becomes parse_log(""), then the rm -f succeeds and _stopped is set, so close() never reads the log again. Through DockerCLI with docker-claude, answering the second logs call with reply(timed_out=True): returncode 0, reward 1.0, metrics["egress"] == {'allowed': [], 'denied': [], 'errors': [], 'denied_entries': []}, no error recorded. That reads as "the agent made no requests", which is what the sidecar's decision log exists to rule out. This predates the PR as check=False, but the 600 s budget is what makes a hang end instead of block.

Suggestion: treat a timed-out logs like the timed-out rm -f (raise TimeoutExpired, not stopped), or mark the egress summary as unknown.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changed in this push, needs a decision: close() swallows a sidecar teardown timeout by design, so raising in stop_proxy alone would still pass the cell with an empty egress record. Options: fail the cell on a timed-out logs call, or mark the egress summary as unknown. Leaving this open.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

# Conflicts:
#	dokimi/requirements.lock.txt
The refactor branch dropped parse_args and JUDGE_MODEL_FOR_PROVIDER, so
--judge-provider openai picked the Claude default judge and failed at the
first call. Take main's cli, defaults and command registrations back;
none of them is part of the docker-* change.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
An unknown image WORKDIR fell back to /app on a timeout, so the agent
ran in a tree the image never declared and the cell was billed. Raise
the timeout instead, and tighten the cleanup tests: the reap matcher
matches only kill -9 (rm -rf /opt/abskill contains kill), and close()
is asserted to save egress.jsonl and remove the network after a timed
out sidecar removal.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
They start every child through dokimi.process now, so the spawn
baseline shrinks to nothing; a stale entry would fail the ratchet.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

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.

4 participants