Meta-Harness
A minimal outer loop that delegates selection AND mutation to a skill-steered proposer over an append-only candidate history, returning a scalar or task-configured Pareto frontier.
"""Meta-Harness Coding-Agent PromptBuilder — the reference ``render_task_prompt`` for the coding
agent that BROWSES the on-disk archive D.
One module per component (see scaffold.py). For ``proposer.mode: "coding_agent"`` the proposer is a
headless coding agent (claude_code / codex / cursor_agent) navigating the archive D, so — unlike
the llm-mode PromptBuilder, which serializes a slice of D into the user prompt — this builder
produces:
* **system** — a filesystem-native domain SKILL.md (the paper's primary hyperparameter: constraints,
exploitation axes, anti-overfitting, archive analysis and prototyping workflow), loaded once
and injected verbatim under the reference banner. The proposer's adapter passes ``prompt.system``
to the agent (a real ``--append-system-prompt`` for Claude Code, or prepended to the prompt for
agents without such a flag). The ``{candidates_per_proposal}`` / ``{exploitation_axes}`` tokens
are substituted exactly as in llm mode.
* **user** — the reference's ``render_task_prompt``: which proposal round this is, where archive D
lives (the agent's cwd), the files to browse, and the candidate contract (write
``candidates/<name>.py`` + a ``pending_eval.json`` manifest). The run state and candidate output
are files rather than chat-serialized approximations.
"""
from __future__ import annotations
from ...components.prompt import PromptBuilder
from ...models.base import Prompt
from ...records import LoopContext, Selection
from .prompt_builder import render_meta_skill
DEFAULT_CODING_AGENT_SKILL = "meta_harness/text_classification_filesystem"
def render_task_prompt(iteration: int, d_dir: str, candidates_per_proposal: int) -> str:
"""The reference ``render_task_prompt`` for galapagos: iteration header + where D is + the
filesystem override + the candidate/``pending_eval.json`` contract."""
return (
f"Run iteration {iteration} of the evolution loop. Produce {candidates_per_proposal} new "
"candidate program(s) this round.\n\n"
"## FILESYSTEM ARCHIVE\n"
"The full run state is NOT inlined in this prompt. It lives as FILES in the archive D at "
"your working directory:\n\n"
f" {d_dir}\n\n"
"Browse it with Read / Glob / Grep / Bash (grep, cat). The files:\n"
"- `evolution_summary.jsonl` — one JSON row per evaluated proposed candidate (Phase-0 seed "
"is intentionally excluded; name, iteration, "
"combined_score, delta, cost, outcome, axis, hypothesis) across the FULL history.\n"
"- `frontier_val.json` — the current frontier (`_pareto`: system, combined_score, and cost "
"only when the task configured a minimize objective).\n"
"- `candidates/<name>.py` — the full source of every prior program (the copy-then-edit pool).\n"
"- `current_best.py` — the current best program (the frontier top).\n"
"- `logs/<name>/result.json` — the complete baseline/candidate evaluation record (valid, metrics, artifacts, "
"per-instance results, text feedback).\n"
"- `logs/<name>/trace.txt` plus `metrics.json`, `artifacts.json`, and optional "
"`per_instance.json` — queryable per-candidate evidence (deep-read failures AND successes).\n"
"- `reports/<name>.md` — prior <=30-line candidate reports.\n"
"- `TASK.md` — the task description and objective.\n\n"
"## OUTPUT CONTRACT\n"
"For each candidate, write a COMPLETE program file at `candidates/<snake_case_name>.py` "
"(copy a frontier program from `candidates/` as your starting point; keep the EVOLVE-BLOCK "
"markers and the fixed interface outside them intact — evolve only between the markers). "
"Then declare ALL of this round's candidates in `pending_eval.json` at the archive root:\n\n"
" {\n"
' "iteration": ' + str(iteration) + ",\n"
' "candidates": [\n'
' {"name": "<snake_case_name>", "file": "candidates/<name>.py",\n'
' "hypothesis": "<falsifiable claim>", "axis": "<exploitation axis, or - >",\n'
' "changes": "<what changed>"}\n'
" ]\n"
" }\n\n"
"Do NOT evaluate the candidates yourself — the outer loop scores each one with the task's "
"evaluator. Do not write a report for an unevaluated new candidate; at the start of the next "
"round, write its <=30-line post-eval report after inspecting its result. Each "
"`candidates/<name>.py` is compile()-checked, then evaluated; invalid files never run."
)
class MetaHarnessCodingAgentPromptBuilder(PromptBuilder):
"""system = the shared SKILL.md steering (agent-injected); user = render_task_prompt."""
def __init__(self, candidates_per_proposal: int = 1,
skill: str = DEFAULT_CODING_AGENT_SKILL):
self.candidates_per_proposal = max(1, int(candidates_per_proposal))
# same shared-loader rendering as the llm builder (skill="none" -> empty steering) — fail
# fast at construction
(self.skill_path, self.skill_name, self.skill_description,
self._system_text) = render_meta_skill(skill, self.candidates_per_proposal)
def build(self, selection: Selection, memory=None, ctx: LoopContext | None = None) -> Prompt:
sig = (ctx.blackboard.get("meta_harness", {}) if ctx is not None else {}) or {}
iteration = sig.get("iteration", ctx.iteration if ctx else 0)
d_dir = str(sig.get("d_dir") or "the archive directory (your working directory)")
user = render_task_prompt(iteration, d_dir, self.candidates_per_proposal)
return Prompt(system=self._system_text, user=user)