Skip to content

feat(executor): Phase 4b — GridRunner dispatch-by-stack (StackExecutorCaller) - #42

Merged
explosivebit merged 8 commits into
mainfrom
feat/grid-dispatch-rfc006
Jun 2, 2026
Merged

feat(executor): Phase 4b — GridRunner dispatch-by-stack (StackExecutorCaller)#42
explosivebit merged 8 commits into
mainfrom
feat/grid-dispatch-rfc006

Conversation

@explosivebit

Copy link
Copy Markdown
Contributor

Summary

Stacked on #40. The keystone integration: makes a CLI stack usable through the EvalCaller Protocol so GridRunner dispatches it identically to raw-llm. Turns the standalone executor into part of the grid pipeline.

  • src/orchestrator/stack_caller.py: StackExecutorCaller (EvalCaller adapter) — resolve the stack adapter → seed a candidate snapshot → run StackExecutor → bridge the patch to an EvalResult; a non-OK run → a graceful FAILED row (FR-009, never dropped). Plus factory providers (make_task_prompt_provider, make_be01_snapshot_provider) and default_model_alias.
  • grid_runner.py: optional caller_for_stack resolver — raw-llmInspectEvalCaller, CLI stacks → StackExecutorCaller. Backward-compatible (falls back to the single caller when unset). GridRunner's judge hook scores the produced submission with no downstream special-casing.
  • 6 offline tests (FakeHarnessLauncher + FakeEvalCaller).

Gates

ruff + mypy --strict clean · full suite 706 passed.

Pipeline status

With this, the real-data pipeline is built end-to-end: Half A (executor, #39) + bridge (#40) + judge fix (#41) + grid dispatch (this). Remaining: a real grid run → Board emission → point apps/site (PR #36) at real data (Phase 4c).

Refs: rfc-006

🤖 Generated with Claude Code

explosivebit and others added 8 commits June 2, 2026 18:20
…cher seam (RFC-006)

Half A candidate path (run agent-CLI harness -> patch), no Docker / no spend.

- StackAdapter: operational parse of stacks/<slug>/stack.yaml (distinct from
  the manifest StackPin run-pin — separate concern, separate contract)
- build_proxy_invocation: per-CLI proxy recipe. aider proven (the RFC-006 first
  slice); other known harnesses surface HarnessRecipePending (recipe lands at
  its Phase-5 per-stack smoke); unknown CLI -> UnsupportedHarnessError
- HarnessLauncher Protocol seam + deterministic FakeHarnessLauncher;
  DockerHarnessLauncher is a typed Phase-2 stub (launch() raises)
- build_docker_run_kwargs: writable /workspace bind + locked-down flags
  (cap_drop ALL, no-new-privileges). PROXY_ONLY egress bridge raises
  NetworkPolicyNotConfigured — the security-sensitive bridge is an open
  RFC-006 decision wired in Phase 2
- StackExecutor.execute: adapter + recipe -> HarnessRunPlan -> launch ->
  patch/trace/cost(metered)/status; never drops a result (FR-009 discipline)

Gates: ruff + mypy --strict clean; 33 new unit tests; full suite 690 passed.

Refs: rfc-006

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion A)

RFC-006 Phase 2 (no spend): the image + network now exist; the real run is gated.

- infra/docker/harness-aider/Dockerfile -> pollmevals-harness-aider:0.1.0
  (python:3.12-slim + aider-chat 0.86.2 + git 2.47.3, non-root, writable
  /workspace). Built + smoke-verified (aider --version, git --version).
- Network decision A wired declaratively: pollmevals-sandbox `internal: true`
  net in the litellm compose; the proxy joins it as the bastion (only reachable
  host, no external route -> harness reaches ONLY the metered proxy, and
  cap_drop ALL holds because no NET_ADMIN/iptables is needed). Portable
  Linux/macOS/CI.
- stack_executor: PROXY_ONLY now joins the internal bastion net (no longer
  raises); in-sandbox the harness addresses the proxy by container name
  (SANDBOX_PROXY_BASE_URL), not localhost (the internal net has no host route);
  DockerHarnessLauncher.launch moves to Phase 3 (built + validated against the
  first real run, not blind).
- Makefile: harness-image-aider (build) + sandbox-net-up (attach proxy to the
  bastion net on an already-running stack, idempotent).

Gates: ruff + mypy --strict clean; 36 stack-executor tests; full suite 692
passed; compose validates with internal:true preserved.

Refs: rfc-006

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ider×qwen×be_01)

RFC-006 Phase 3: the candidate pipeline (Half A) runs end-to-end on real money.

- DockerHarnessLauncher.launch: docker-py run on the pollmevals-sandbox bastion
  net (decision A) against a writable /workspace; host-side git base + then
  `git add -A && git diff --cached <base>` robustly captures the patch
  (committed + working-tree + new files) without shell-quoting the prompt.
  Auto-discovers the Docker Desktop socket (~/.docker/run) since docker-py's
  from_env() defaults to /var/run and misses it.
- scripts/stack_exec_live_smoke.py: --plumbing ($0 no-model docker/patch check)
  + --confirm-spend (real run, gated like smoke_run.py).
- Tests: git base/capture + token-parse plumbing (host git, no Docker); the
  container run itself is validated by the live smoke.

FIRST REAL NUMBER — aider × qwen-3-14b × be_01 via the metered bastion proxy:
  status=ok, 208-line patch (a real Express JWT auth middleware), cost=$0.000626,
  in=1400/out=2200 tokens, ~55s. Bastion proven end-to-end: a sandbox container
  reaches the proxy (200) but NOT the internet (DNS fails) — no un-metered egress.

Gates: ruff + mypy --strict clean; 39 executor tests; full suite 694 passed.

Refs: rfc-006

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RFC-006 Phase 4. Bridges StackExecResult (Half A patch) → EvalResult so the
judge panel (and evaluators) score a (model × harness × task) run.

- src/orchestrator/stack_scoring.py: changed_files (parse diff, filter harness
  noise .aider/.gitignore), extract_submission (final content of changed source
  files — code, not diff), exec_result_to_eval_result (write submission as the
  raw_output artifact + map to EvalRow; rejects non-OK runs). GridRunner
  dispatch-by-stack will reuse this same path.
- scripts/stack_score_live_smoke.py: executor → bridge → judge panel → scored
  number. Anchors cwd at repo root (the panel resolves rubric.yaml via cwd).
- 6 bridge unit tests (no Docker/judges).

FIRST SCORED NUMBER — aider × qwen-3-14b × be_01, judged (inversion-free, since
the be_01 deterministic evaluators invert per EVID-027):
  claude-sonnet 5.27 · gemini-3-flash 7.33 · gpt-5-mini 0.00* · cost $0.070
  (*DEFECT: gpt-5-mini truncated its rubric JSON at the 2048 cap → parse-fail →
  0.0 fallback, dragging the median + alpha. Trustworthy 2-judge signal ≈ 6.3.
  Fix is reasoning_effort per EVID-023, NOT raising the cap — separate judge work.)

Gates: ruff + mypy --strict clean; 6 bridge tests; full suite 700 passed.

Refs: rfc-006
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
How to run a real harness×model×task eval: the bastion network model (decision
A), one-time setup (stack-up / sandbox-net-up / harness-image-aider), the $0
plumbing check + the two live smokes (Half A only, full chain to a scored
number), how host-side git patch capture works, the be_01 judge-vs-evaluator
caveat (EVID-027 inversion), known defects (gpt-5-mini truncation, proxy cost
reconciliation, GridRunner dispatch), and the first real numbers.

Refs: rfc-006
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rms RFC-006

First-real-run evidence pack: bastion proven ($0), Half A first patch
(aider×qwen×be_01, $0.000626), first judged score (median ~6.3 on 2 trustworthy
judges), and the gpt-5-mini truncation defect. verdict=PASS, CL3,
evidence_type=live_integration_run. Activated; R_eff recomputed.

Refs: evid-rfc-006-stackexecutor-live-first-real-harness-model-patch-judged-score
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rCaller)

RFC-006 Phase 4b. Makes a CLI stack usable through the EvalCaller Protocol so a
real grid run dispatches it identically to raw-llm — the keystone that turns the
standalone executor into part of the grid pipeline.

- src/orchestrator/stack_caller.py: StackExecutorCaller (EvalCaller adapter) —
  resolve the stack adapter → seed a candidate snapshot → run StackExecutor →
  bridge the patch to an EvalResult (stack_scoring); a non-OK run → a graceful
  FAILED row (FR-009, never dropped). + factory providers (task prompt from
  task.yaml, be_01 snapshot) and default_model_alias (route → proxy alias).
- grid_runner.py: optional caller_for_stack resolver — raw-llm →
  InspectEvalCaller, CLI stacks → StackExecutorCaller. Backward-compatible
  (falls back to the single caller when unset). GridRunner's judge hook scores
  the produced submission with no downstream special-casing.
- 6 offline tests: caller OK→SCORED+submission / empty-patch→FAILED, cost/alias
  carry-over, dispatch routing, single-caller fallback.

Gates: ruff + mypy --strict clean; full suite 706 passed.

Refs: rfc-006
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@explosivebit
explosivebit changed the base branch from feat/stack-scoring-rfc006 to main June 2, 2026 20:54
@explosivebit
explosivebit merged commit 409779d into main Jun 2, 2026
3 checks passed
@explosivebit
explosivebit deleted the feat/grid-dispatch-rfc006 branch June 2, 2026 20:55
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.

1 participant