Skip to content

feat(tasks): run terminal-bench v1 tasks natively, export to Harbor lazily - #212

Draft
napetrov wants to merge 8 commits into
intel:mainfrom
napetrov:feat/run-tbench-v1
Draft

napetrov wants to merge 8 commits into
intel:mainfrom
napetrov:feat/run-tbench-v1

Conversation

@napetrov

@napetrov napetrov commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

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.

 load_task(dir)
   task_format: harbor | task-md
+               | terminal-bench-v1 (task.yaml)  -> tbench_v1.read
 docker-oracle / docker-claude
-  build ./environment, copy solution/ tests/, reward.txt
+  build task.build_context (-f task.dockerfile), -e container_env
+  oracle_step / verifier_step:
+    tb-v1: solution.sh -> /oracle, run-tests.sh + tests/ -> /tests, image user
+    verdict = pytest "short test summary info" (not exit code), as terminal-bench
+  refuse with reason: extra services/volumes/tmpfs/ports, solution.yaml,
+    run_tests_in_same_shell, non-pytest parser
 harbor* / benchflow*
+  harbor_view(task): content-keyed export in $TMPDIR/dokimi-harbor-export/<sha16>/
+    (follows harbor.mappers.terminal_bench; test.sh scores the same pytest summary)
 tasks inspect: tb-v1 consumers none-yet -> docker-*, harbor, benchflow
+memory: tb-v1 environment.memory_mb = compose deploy.resources.limits.memory
+        else 8192 ($DOKIMI_TBENCH_V1_MEMORY_MB), not the 2048 default; exported to task.toml
+network (#218): environment.network_mode public -> oracle + verify public, agent solve default
dokimi/harnesses/
├── tbench_v1.py      # task.yaml/compose reader, refusals, PytestParser port
├── harbor_export.py  # lazy Harbor layout + awk verdict
└── task_env.py       # env helpers moved out of docker_solver.py (growth cap)

Evidence

  • Before: tasks run --harnesses docker-oracle on a terminal-bench original-tasks/ dir: no task.toml/task.md, not loadable; tasks inspect consumers none-yet.
    After: tests/test_tbench_v1.py (37 unit + 1 docker test gated by DOKIMI_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 --offline 0 failed.
  • Real docker-oracle over upstream terminal-bench (d28711d) sample: hello-world 1/1; first 5 of 25 sampled: 2 pass, 3 fail from task rot/limits (pinned apt version gone, OOM at the 2g default memory, QEMU guest test) – table in follow-up comment.
  • Memory: at 8 GB the two 2 GB OOMs clear: predicate-pushdown-bench passes; add-benchmark-lm-eval-harness runs its solution to the end, then fails at test collection (transformers has no AutoModelForVision2Seq; unpinned dependency, upstream rot).
  • Corpus survey (241 tasks): 215 runnable natively, 26 refused with reason (8 solution.yaml, 2 same-shell, rest multi-service/compose keys) – those go through the Harbor export.

Scoring: pytest summary vs exit code

docker-oracle with the real solution.sh and 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 and exit == 0 agree on all 118. A static scan of all 241 run-tests.sh finds the scripts where the exit code is not pytest's:

task run summary exit code why
play-lord no-op (ran) 0 (4 FAILED) 0 -> pass pytest ... then if [ $? -eq 0 ]; then echo <summary>; fi: an if with a false test and no else exits 0
multistep-definite-integral static (run_tests_in_same_shell, refused by docker-*) follows the tests always 0 pytest ... | tee, then echo "Pytest end."
stable-parallel-kmeans both agree agree saves $? and exit $exit_code
  • terminal-bench v1 scores the summary only: harness.py _is_resolved (L536-542, all parsed tests PASSED; no summary means unresolved), and PytestParser maps SKIPPED/XFAIL to passed and XPASS/ERROR to failed; the exit code is never read (d28711d).
  • Harbor's mapper scores the exit code: mappers/terminal_bench.py REWARD_LOGGING_SUFFIX (L37-44, _EXIT_CODE=$? -> reward 1/0), added to run-tests.sh by copy_test_script_with_reward_logging (L357-361, L480) (harbor 6099b8c). It does not check run_tests_in_same_shell or parser_name.
  • Recommendation: keep the summary. It is what terminal-bench and its published scores use. The exit code matched on every sampled run, but on play-lord Harbor's rule passes a no-op (and on multistep-definite-integral it passes anything). Harbor parity would give those tasks a free pass.

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/cp argv for existing Harbor/task-md tasks.

🤖 Generated with Claude Code

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-commenter

codecov-commenter commented Oct 24, 2026 •

Copy link
Copy Markdown

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

Codecov Report

❌ Patch coverage is 93.85666% with 90 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.10%. Comparing base (09be35b) to head (183a6f3).

Files with missing lines Patch % Lines
dokimi/dokimi/task_formats/source.py 84.11% 12 Missing and 5 partials ⚠️
dokimi/dokimi/harnesses/harbor_export.py 87.61% 6 Missing and 7 partials ⚠️
dokimi/dokimi/task_formats/detect.py 87.96% 8 Missing and 5 partials ⚠️
dokimi/dokimi/task_formats/dirs.py 89.90% 5 Missing and 6 partials ⚠️
dokimi/dokimi/task_formats/datasets.py 95.95% 5 Missing and 5 partials ⚠️
dokimi/dokimi/harnesses/network_policy.py 94.07% 5 Missing and 4 partials ⚠️
dokimi/dokimi/harnesses/tasks.py 90.16% 4 Missing and 2 partials ⚠️
dokimi/dokimi/harnesses/tbench_v1.py 95.16% 4 Missing and 2 partials ⚠️
dokimi/dokimi/task_formats/configs.py 97.71% 1 Missing and 3 partials ⚠️
dokimi/dokimi/commands/tasks.py 97.50% 0 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #212      +/-   ##
==========================================
+ Coverage   92.01%   92.10%   +0.08%     
==========================================
  Files         223      236      +13     
  Lines       27768    29147    +1379     
  Branches     4563     4759     +196     
==========================================
+ Hits        25552    26845    +1293     
- Misses       1570     1618      +48     
- Partials      646      684      +38     
Files with missing lines Coverage Δ
dokimi/dokimi/__init__.py 100.00% <ø> (ø)
dokimi/dokimi/artifacts.py 95.12% <ø> (ø)
dokimi/dokimi/harnesses/benchflow.py 95.43% <100.00%> (+0.02%) ⬆️
dokimi/dokimi/harnesses/command.py 97.29% <100.00%> (+0.02%) ⬆️
dokimi/dokimi/harnesses/container_agent.py 96.00% <100.00%> (+0.10%) ⬆️
dokimi/dokimi/harnesses/docker_solver.py 97.73% <100.00%> (+0.01%) ⬆️
dokimi/dokimi/harnesses/runner.py 99.17% <100.00%> (+0.04%) ⬆️
dokimi/dokimi/harnesses/task_env.py 100.00% <100.00%> (ø)
dokimi/dokimi/task_formats/__init__.py 100.00% <100.00%> (ø)
dokimi/dokimi/task_formats/links.py 100.00% <100.00%> (ø)
... and 12 more

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

@github-actions github-actions Bot added the cli-interface-change Changes dokimi's user-facing CLI surface label Oct 24, 2026
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

CLI interface change detected

This PR changes dokimi's user-facing CLI surface (commands and/or options):

+ added command `dokimi tasks inspect`
    + argument `path`
    + option `--help/-h`
    + option `--json`
    + option `--out`
~ `dokimi tasks run` parameters changed:
    + added option `--network`

Updated on every push; the cli-interface-change label is on while the diff is non-empty.

@napetrov

Copy link
Copy Markdown
Contributor Author

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>
… 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>

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

cli-interface-change Changes dokimi's user-facing CLI surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants