Repository navigation
feat(tasks): run terminal-bench v1 tasks natively, export to Harbor lazily - #212
Conversation
A probe registry (dokimi.task_formats) names the format of a file, a task directory or a tree, and reports kind, item count, present/missing parts, lint problems and which dokimi backends consume it today. Probes: harbor and task-md (wrapping harnesses.tasks), terminal-bench v1, SWE-bench, Inspect AI samples and @task files (text scan, never imported), OpenAI evals, skill-creator evals.json and trigger sets, skill-reach QuerySet, promptfoo and dokimi questions. Unknown paths get a best-effort structure report; malformed files are lint problems. Detection only: nothing new runs. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
`tasks inspect --json` now prints a stamped task_inspect.v1 artifact and `--out FILE` writes it through save_artifact, as `repo budget` does. Probes added or widened from runs over upstream checkouts: - a dataset file a config names is read as that config says: Inspect FieldSpec remaps, OpenAI evals registry specs (openai-evals-spec, with model-graded samples needing no ideal), promptfoo `tests: file://` (promptfoo-tests); - git-LFS pointer stubs are git-lfs-pointer, with the expected format when a config names them; - CSV datasets, promptfooconfig.<variant>.yaml and redteam configs, ExploreBench task YAML (dokimi-explore-task, localization); - a task.md with no BenchFlow front matter and no task part directories is no longer a task-md task. YAML aliases are reported as refused, not as invalid YAML. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
tests/fixtures/task_formats/ holds small upstream files of every format the probes know (intel/skills, skillsbench, terminal-bench, Inspect AI, openai/evals, promptfoo; licenses in LICENSES/) and generated ones where no public file exists (SWE-bench row, skill-creator evals.json, skill-reach QuerySet, and the output of dokimi's trigger-set writers). MANIFEST.yaml records each file's source, commit and license and what inspect must report; test_task_format_corpus.py inspects the corpus as one mixed tree, validates every --json report as task_inspect.v1, and reads back what to_skill_creator, to_reach and to_questions write. The fixture tree is kept out of pytest collection, ruff and the license header check: its test_*.py are the verifiers of copied tasks. Fixes found on the way: a task.md scan leaked the loader's UserWarning from the fingerprint, and a path that is neither file nor directory (/dev/null) crashed the tree scan. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #212 +/- ##
==========================================
+ Coverage 92.19% 92.24% +0.04%
==========================================
Files 235 239 +4
Lines 29387 29934 +547
Branches 4836 4917 +81
==========================================
+ Hits 27094 27613 +519
- Misses 1615 1633 +18
- Partials 678 688 +10
🚀 New features to boost your workflow:
|
|
The CLI surface now matches the base branch: no user-facing CLI change. |
|
docker-oracle on 25 sampled upstream tasks: 20/25 pass. Failures: 2 OOM under the 2g default memory (lm-eval-harness, predicate-pushdown-bench), 2 upstream rot (build-pmars pinned apt version gone, cron-broken-network example.com content), 1 task-side (build-tcc-qemu). docker-claude pilot with haiku: 3/3 pass, $0.006. |
…efault build, solve and verify each get none, allowlist (egress sidecar) or public. Defaults: build public, solve and verify none. Sources, later wins: the defaults, task.toml ([environment]/[agent]/[verifier] network_mode and allowed_hosts, Harbor's allow_internet, environment.egress), then dokimi tasks run --network PHASE=MODE[:hosts]. The plan and each phase's source land in metrics.network; a skill's SKILL.md metadata.network is recorded, not granted. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ed cells too Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…azily A task.yaml task loads as a TaskSpec and runs under docker-oracle and docker-claude as terminal-bench's harness runs it (solution.sh at /oracle, run-tests.sh + tests/ at /tests, image user, TEST_DIR and compose env, pytest-summary verdict). Tasks one docker run cannot reproduce are refused with the reason. Harbor-backed harnesses and benchflow run a content-keyed Harbor export whose test.sh applies the same pytest-summary rule. Stacked on intel#210. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…om compose terminal-bench sets no memory limit; dokimi's 2 GB default OOM-killed add-benchmark-lm-eval-harness and predicate-pushdown-bench. A v1 task now reads compose deploy.resources.limits.memory when set, else 8192 MB (DOKIMI_TBENCH_V1_MEMORY_MB overrides), and the Harbor export writes it to task.toml. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
1260499 to
183a6f3
Compare
… solve default Rebased onto intel#218. The corpus manifest's terminal-bench hello-world now names its consumers (docker-*, harbor, benchflow), not none-yet. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
docker-* parsed stdout followed by all of stderr, so a stderr line such as pip's "ERROR: pip's dependency resolver ..." landed after the pytest summary and failed a passing task. Run run-tests.sh with 2>&1, as terminal-bench's pane and the Harbor export's test.sh read it. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
The cache key hashed only the task's files, so a cached export kept the memory_mb of its first run after DOKIMI_TBENCH_V1_MEMORY_MB changed, while docker-* used the new value. Hash the resolved task.toml too. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
The cache sat at a predictable $TMPDIR path, created group-writable, and any dir there with a task.toml was run and scored as the export. Default to ~/.cache/dokimi/harbor-export, created 0700. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
export() refuses solution.yaml on every run, yet tasks inspect listed harbor and benchflow, and each refusal left a .staging-* dir in the cache. Report the refusal as a problem, clean staging in a finally, and say tasks list/run read three formats now. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Every Harbor environment builds environment/Dockerfile, so a task whose compose names build.dockerfile: Dockerfile.x exported with nothing to build. Rename it, refuse a clash, and pin the subdir-context, kept COPY tests/solution.sh and working_dir branches with tests. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Pushed:
Left for the author: whether agent runs of |
antonwolfy
left a comment
There was a problem hiding this comment.
Review at cd1d348. The native tb-v1 path is sound: the pytest-summary verdict, the refusals, the memory default and the per-user cache all behave as documented. The problems are at the edges, mostly in the Harbor export. The PR shape and one test gap are below, the rest are inline.
What I ran, on a Linux review host at cd1d348:
make ci-local(with Docker): 32 passed, 1 failed, 6 skipped. The failure ispip-auditon the lock files, which this PR does not touch. Changed-line coverage gate passes.- Reproductions for the code findings, with no model or network involved.
- A mutation run on the new tests: 138 mutants, 47 of which pass the full tb-v1, task-md and network-policy tests. The gaps are in the inline comments below.
Findings that cannot be anchored inline:
- The PR is stacked on #218 and #210.
AGENTS.md(Commits and pull requests) says: "When a change depends on an open PR, wait for it to merge or put both in one PR; PRs are not stacked on each other's branches." The diff againstmainis 96 files and 13 commits, and carriestask_formats/*,network_policy.py,task_inspect.v1.json, the corpus fixtures and three changelog fragments. This PR cannot be reviewed or merged on its own. Rebase ontomainafter #218 lands, or fold the three into one PR.
Also untested (outside the inline anchors): the refusal of a host-side agent on a tb-v1 task at docker_solver.py:760. Narrowing that condition to (FORMAT_TASK_MD,) passes the full suite. Add _refuse_unrunnable(tb1_task, "claude", "host") expecting ValueError.
@xaleryb's question on whether agent runs of solution.yaml tasks should export without solution/ waits for the author.
Checked, no issues
- Schema versioning:
task_inspect.v1is a new kind registered inSCHEMA_VERSIONSwith a fixture; no existing schema changed. Nowith_docs/without_docskeys involved. - Docs against code: the 8192 default, compose memory limit,
~/.cache/dokimi/harbor-exportmode 0700,tasks inspectconsumers and--networkparsing match the code. - Refactor: the Harbor and task-md
docker build/cp/execargv is unchanged apart from the existence check. - Export concurrency: the atomic replace handles a lost race, and staging is removed in
finally.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
antonwolfy
left a comment
There was a problem hiding this comment.
I pushed a commit per nit, up to 33cff32, which fix the nit from my review:
33cff32:dokimi/docs/coding-harnesses.md:129: the verdict rule now reads "every listed test passed, skipped or xfailed", matching_PASSINGintbench_v1.py.
make ci-local at 33cff32: 32 passed, 1 failed (pip-audit, as at the reviewed head), 6 skipped.
GitHub CI has not run on these commits: the branch conflicts with main, and Actions runs no workflow on a conflicting PR. CI will check them once main is merged in.
Not approving yet. Still open: the bug and the seven risks from my review.
…s TOML reads it json.dumps escaped a character above U+FFFF as a surrogate pair, which tomllib rejects, so every Harbor and BenchFlow run of such a task failed on parse. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
…test.sh reads them Keyed by the name after the file, a later PASSED b.py::test_x hid an earlier FAILED a.py::test_x: docker-* scored 1.0 where the Harbor export's test.sh scored 0. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Only its awk was run before: dropping the exit code, the tee, the 2>&1, TEST_DIR or the reward file name passed the suite. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
…tasks A non-task directory with a task.yaml and a Dockerfile made tasks list and --all fail for the whole root; it is skipped with a warning now, as a task.md without frontmatter is, and load_task on it still raises. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
…ost environment on docker-* One docker run of the default stage, idling, with literal values did none of these, so such a task ran and its failure read as the task's, not a refusal. The idle sleep infinity and TEST_DIR stay accepted; the top-level-key and extra-volume refusals are now pinned too. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
…s Harbor export Dropping harbor_view from BenchflowHarness.run passed the suite; bench would then get a task.yaml directory with no task.toml. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Narrowing the refusal to task.md tasks passed the suite. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
|
Merged main after #210 landed (5df4fce): conflicts merged three-way against e4b4e80, the commit this branch was built on. The PR's own diff against main is line for line the same as before (22 files); the two consumer-table rows for harbor and task-md take main's "(with This branch still carries #218's first two commits (ec91e10, bab9fa9), so #218 should land first. Still for the author: whether an agent run of a |
Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com> intel#212 landed: the format table, the docs paragraph and tasks.py's solution/tests maps take both the terminal-bench-v1 and the swe-bench entries.
Stacked on #218 (
feat/task-network-policy, itself on #210); review/merge after both. (AGENTS.md says PRs are not stacked; stacked here on request.)Summary
TaskSpec stays canonical; Harbor is only an export target, written lazily.
Evidence
tasks run --harnesses docker-oracleon a terminal-benchoriginal-tasks/dir: no task.toml/task.md, not loadable;tasks inspectconsumersnone-yet.After:
tests/test_tbench_v1.py(37 unit + 1 docker test gated byDOKIMI_DOCKER_TESTS=1): load/defaults, refusals, pytest-summary verdict incl. exit-code mismatch both ways, awk == Python verdict parity, export layout, cache keying, harbor harness runs the export. Full suite 7877 passed;make ci-local --offline0 failed.predicate-pushdown-benchpasses;add-benchmark-lm-eval-harnessruns its solution to the end, then fails at test collection (transformershas noAutoModelForVision2Seq; unpinned dependency, upstream rot).Scoring: pytest summary vs exit code
docker-oracle with the real
solution.shand with a no-op one (true), 60 sampled tasks (the earlier 25 + 35 random, seed 212), 8 CPUs / 8 GB per container: 118 runs verified (qemu-startup: image build fails, apt). The summary verdict andexit == 0agree on all 118. A static scan of all 241run-tests.shfinds the scripts where the exit code is not pytest's:pytest ...thenif [ $? -eq 0 ]; then echo <summary>; fi: anifwith a false test and noelseexits 0run_tests_in_same_shell, refused by docker-*)pytest ... | tee, thenecho "Pytest end."$?andexit $exit_codeharness.py_is_resolved(L536-542, all parsed tests PASSED; no summary means unresolved), andPytestParsermaps SKIPPED/XFAIL to passed and XPASS/ERROR to failed; the exit code is never read (d28711d).mappers/terminal_bench.pyREWARD_LOGGING_SUFFIX(L37-44,_EXIT_CODE=$?-> reward 1/0), added torun-tests.shbycopy_test_script_with_reward_logging(L357-361, L480) (harbor 6099b8c). It does not checkrun_tests_in_same_shellorparser_name.Merge Danger
Door: two-way
New format path; harbor/task-md code paths only touched by refactors (build context, step objects) covered by existing tests.
Blast Radius: docker-harnesses
Regressions would show as changed
docker build/cpargv for existing Harbor/task-md tasks.🤖 Generated with Claude Code