Skip to content

feat(harnesses): per-phase network policy for docker-* runs, off by default - #218

Draft
napetrov wants to merge 5 commits into
intel:mainfrom
napetrov:feat/task-network-policy
Draft

napetrov wants to merge 5 commits into
intel:mainfrom
napetrov:feat/task-network-policy

Conversation

@napetrov

Copy link
Copy Markdown
Contributor

Stacked on #210 (feat/task-format-detect); review/merge after it. #213 (SWE-bench) is rebased on this branch.

Summary

Every docker-* cell gets a network per phase, recorded with where it came from:

phase    default   modes: none | allowlist:host[:port],... (egress sidecar) | public (bridge + host proxy)
build    public    docker build --network host|none
solve    none      oracle / agent (an in-container agent still reaches its LLM via the sidecar)
verify   none      tests/test.sh

sources, later wins
  DEFAULTS
  task.toml  [environment] network_mode/allowed_hosts | allow_internet  -> verify, oracle solve
             [agent] / [verifier] network_mode                         -> that phase
             environment.egress                                         -> agent solve
  CLI        dokimi tasks run --network PHASE=MODE[:hosts]  (repeatable, all=every phase)
 DockerSolverHarness.run
+  plan = network_policy.resolve(task, self.network, agent=...)
   _build_image(plan.build)
   _start_container(...)            # --network none | bridge | internal+sidecar
+    enter_phase(plan.solve)        # leave/join bridge before solve.sh runs
   _solve_oracle(plan.solve)
+  _verify_phase(plan.verify)       # stop solve sidecar, join/leave bridge, own -verify sidecar for an allowlist
+  metrics.network = {build, solve, verify: {mode, hosts, source}, verify_egress, skill}
+  <cell>/network.json              # errored cells too

Skills that need the network declare it in SKILL.md as metadata.network: "allowlist:pypi.org,..." (reason in compatibility, the Agent Skills spec field). It is recorded as metrics.network.skill.missing, not granted. Widening only the skill arm would put its baseline on another network.

Evidence

Unit tests: tests/test_network_policy.py (22) cover resolution order, Harbor fields, bad --network specs, and docker argv order (disconnect < solve.sh < connect < test.sh). They also check that proxy env reaches only the public step, that the verifier gets its own sidecar, and that an errored cell keeps network.json. Full suite: 7954 passed. make ci-local: all green except pip-audit (filelock / setuptools CVEs in the lock, also on main).

Real runs with docker-oracle on this host (full tables in scratch/netpolicy/RESULTS.md):

probe from inside the container default (none) verify=public verify=allowlist:pypi.org
interfaces lo eth0, lo eth0 (internal), lo
DNS example.com / docker gateway 172.17.0.1:22 fail / fail ok / open fail / fail
https pypi.org / example.com via proxy env fail / fail 200 / 200 200 / 403
corpus none -> open, verdict changes
Harbor examples (12) 4 flip 0 -> 1 (hello-world, hello-alpine, hello-skills, reward-kit-example): test.sh installs uv/pytest at verify time
terminal-bench v1 (12, with #212) 12 flip: run-tests.sh does apt + uv + pip install pytest
SWE-bench pilot (7, with #213) 0 flip: gold passes offline. requests-863/2317 fail in both (live httpbin)

Allowlist on real tasks: reward-kit-example and hello-alpine pass with verify=allowlist:<7-8 HTTPS hosts> and fail with pypi.org alone. Each time, verify_egress.denied named the missing hosts.

Merge Danger

Door: two-way

Behaviour change: a task with [environment] network_mode = "public" now also gives the oracle the network, and a verifier that nothing grants a network runs fully offline (--network none, where before it was an internal network). Undeclared Harbor tasks that install their test deps at verify time score 0 until run with --network verify=public. That was already true before; Harbor's own default is public.

Blast Radius: docker-*

Only docker-* harnesses; --network with any other harness is refused. Plain HTTP (apt over :80) through an allowlist still fails behind an upstream proxy (an existing egress_proxy rule).

🤖 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>
…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>
@github-actions github-actions Bot added the cli-interface-change Changes dokimi's user-facing CLI surface label Oct 24, 2026
@github-actions

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.

@codecov-commenter

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 94.34470% with 63 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.09%. Comparing base (09be35b) to head (bab9fa9).

Files with missing lines Patch % Lines
dokimi/dokimi/task_formats/source.py 84.11% 12 Missing and 5 partials ⚠️
dokimi/dokimi/task_formats/detect.py 87.96% 8 Missing and 5 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/task_formats/dirs.py 91.08% 4 Missing and 5 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     #218      +/-   ##
==========================================
+ Coverage   92.01%   92.09%   +0.07%     
==========================================
  Files         223      233      +10     
  Lines       27768    28837    +1069     
  Branches     4563     4714     +151     
==========================================
+ Hits        25552    26557    +1005     
- Misses       1570     1606      +36     
- Partials      646      674      +28     
Files with missing lines Coverage Δ
dokimi/dokimi/__init__.py 100.00% <ø> (ø)
dokimi/dokimi/artifacts.py 95.12% <ø> (ø)
dokimi/dokimi/harnesses/container_agent.py 96.00% <100.00%> (+0.10%) ⬆️
dokimi/dokimi/harnesses/docker_solver.py 97.75% <100.00%> (+0.04%) ⬆️
dokimi/dokimi/harnesses/runner.py 99.17% <100.00%> (+0.04%) ⬆️
dokimi/dokimi/task_formats/__init__.py 100.00% <100.00%> (ø)
dokimi/dokimi/task_formats/links.py 100.00% <100.00%> (ø)
dokimi/dokimi/task_formats/model.py 100.00% <100.00%> (ø)
dokimi/dokimi/task_formats/pointers.py 100.00% <100.00%> (ø)
dokimi/dokimi/commands/tasks.py 92.81% <97.50%> (+1.47%) ⬆️
... and 6 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.

napetrov added a commit to napetrov/dokimi that referenced this pull request Oct 24, 2026
… 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