Claude Skill

finalize

Score the best candidate on the held-out TEST split exactly once and seal the run. Use as the last evaluation step, after optimization stops. The run dir enforces the seal — a second finalize raises an error — so the headline number is produced once on data the optimizer never sa

LLM Mart · 0 points · 11 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download skillberry-ai-cap-evolve-skills_phases_finalize-49fcedb.zip · 9 KB
Part of skillberry-ai/cap-evolve — 22 skills

Install

skills CLI npx skills add https://github.com/skillberry-ai/cap-evolve/tree/main/skills/phases/finalize
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install skillberry-ai-cap-evolve@llmmart
Git git clone https://github.com/skillberry-ai/cap-evolve.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole skillberry-ai/cap-evolve collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

finalize — the one honest number

Optimization hill-climbs on val: every accept decision consumed val as a tuning signal, so by the end of search val is optimistic — it has been selected against. The number you report must come from data nothing was tuned against. finalize scores the run's best candidate on the sealed test split, once, and writes final.json. That file is the run's result.

One finalize is two evals, not one

A bare test number cannot be defended — a reader cannot tell whether it beat the capability you started with. So one finalize scores test twice: the best candidate as tag FINAL, and the untouched seed candidate as FINAL_seed (harness.finalize). final.json therefore carries test, test_baseline, baseline_id, and test_delta — the held-out improvement, which is the figure report, the dashboard, and the event stream all headline. If the best candidate IS the seed (nothing was accepted), the second eval is skipped and test_delta is 0 by construction.

So budget --n-trials 3 as 3 trials × 2 candidates × |test| rollouts — twice what the flag looks like it buys on a paid benchmark.

Both evals sit inside one attempt and neither is a selection event: the delta is reported, never chosen on. That is why the seal counts attempts, not evals.

The seal (why "exactly once")

The instant test informs any choice — picking between finalists, "double- checking" a low number, re-running until it looks better — it stops being held out, because each peek is a selection event that pulls the number from an unbiased estimate toward an optimistic fit metric (references/concepts.md).

cap_evolve enforces this in three parts (rundir.py:358-407), and the split between them is the whole design:

  • reserve — every split="test" eval first checks the seal without burning it, so no phase other than finalize can reach test at all.
  • commit — the seal burns only after final.json is written, so a finalize that dies before scoring leaves it unused and is honestly retryable. A transient crash must not destroy a run's headline number.
  • attempt guard — seal-on-success alone cannot tell "crashed before scoring" from "crashed after". A real run hit the second case: a finalize killed by a timeout had already scored test, the retry scored it again, and the reported headline was that second look. begin_test_attempt refuses a retry once test rollouts exist on disk, before anything is spent.

The seal refuses that mistake by default; it is not unbypassable. CAPEVOLVE_ALLOW_TEST_RESCORE=1 is a deliberate opt-in override (rundir.py:166). Its own message promises the use "is recorded in the run" — nothing records it (issue #341), so a run that took a second look currently looks identical to an honest one. If you set it, disclose it in the write-up yourself.

Corollary: all selection happens before finalize. Choose the single best candidate on val, then finalize it. Finalists that genuinely need comparing get compared on val — never on test.

If finalize refuses

A TestSealError is three situations with three different right moves. Tell them apart from <run>/rollouts/test/ and test_used in splits.json:

State What happened Do this
no test rollouts, seal unused crashed before scoring Re-run finalize — the case seal-on-success exists for.
test rollouts exist, seal unused crashed after scoring, before commit Do not re-score. Read the rollouts under <run>/rollouts/test/ and report what that attempt already computed.
test_used: true the run is finalized Read final.json and regenerate the human artifact with report alone; cap-evolve run --resume skips finalize for you.

Never delete test rollouts or edit splits.json to get past the error — that manufactures a clean-looking number from a split that has already been seen.

Dual-mode

This phase runs two ways from the same SKILL.md: standalone as the slash command /cap-evolve:finalize (the argument-hint shows its run.py args), and orchestrator-callable — cap-evolve run / the orchestrate skill invokes the same scripts/run.py headlessly and threads the run dir between phases.

How to run

python scripts/run.py --run-dir .capevolve/run_XXXX --project .capevolve/project --n-trials 3

Multiple trials give the headline an honest stderr and a pass^k reliability figure instead of one noisy point. Under cap-evolve run the count comes from num_trials in capevolve.yaml and defaults to 1 — set it to ≥3, or the orchestrated headline ships with stderr 0: the exact single point this warns against. If the split was configured with no holdout (test == train/val) the number is a fit metric, not a held-out result; the dashboard flags it, so say so in the summary too.

Then read the result instead of just filing it: test ≈ val means the val gain generalized, test ≪ val means search overfit val — a real finding, not a reason to re-score.

References

  • references/concepts.md — why each peek biases the estimate, the train-fits / val-selects / test-estimates rationale, no-holdout runs, and how this maps to public benchmark protocol, with sources.
Files (cap-evolve)
  • references
    • concepts.md 4.4 KB
      # Concepts — finalize and held-out sealing
      
      > The whole pipeline exists to produce *one* number you can defend: how good is
      > the optimized capability on data nothing was tuned against. finalize produces
      > it, once, and the run dir enforces that "once". Implementation:
      > `harness.finalize` + the reserve/commit `test_used` seal in `cap_evolve/rundir.py`.
      
      ## Why val is not the answer
      
      During search, every accept/reject decision read the val split. Selecting the
      candidate that scores best on val means val has been *optimized against* — its
      score is biased upward by exactly the selection you performed (the more
      candidates you screened, the larger the bias). This is the standard reason ML
      keeps a third split: train fits, validation selects, **test estimates**. The test
      number is trustworthy only because nothing — no edit, no acceptance, no
      hyperparameter — was ever chosen using it.
      
      ## The seal: why "exactly once" is mechanical, not advisory
      
      A held-out set stays held out only while it is untouched. Each time you score
      test you create an opportunity to *act* on the result:
      
      - "The number looks low — let me try the second-best candidate on test too."
      - "Let me re-run with more trials until it stabilizes."
      - "Let me double-check after one more edit."
      
      Every one of these is a selection event. Picking the best of several test scores
      is the same best-of-noise inflation the acceptance gate guards against — now
      applied to the one split that was supposed to be clean. The result is no longer
      an unbiased estimate; it has quietly become a fit metric, and nothing in the
      output says so.
      
      cap-evolve refuses to let this happen by accident, in three parts
      (`rundir.py:358-407`). `reserve_test` *checks* the seal on every `split="test"`
      evaluation without burning it; `commit_test` burns it only once `final.json` is
      written, so a finalize that crashes *before* scoring is honestly retryable rather
      than a destroyed headline number; and `begin_test_attempt` refuses a retry once
      test rollouts exist on disk, since a crash after scoring means the held-out set
      was already observed. Any second attempt raises `TestSealError`. The honesty is
      enforced by the harness, not left to the operator's discipline — the one bypass,
      `CAPEVOLVE_ALLOW_TEST_RESCORE=1`, is deliberately an explicit env override.
      
      The seal counts *attempts*, not evaluations, because one honest finalize scores
      test twice by design: the best candidate (`FINAL`) and the unmodified seed
      (`FINAL_seed`), so the headline is a held-out improvement rather than a bare
      number. Neither is a selection event.
      
      ## Selection happens before finalize
      
      The corollary is a workflow rule: **all model selection must complete on val (or
      train) before finalize runs.** Choose the single best candidate, then finalize it.
      If two finalists genuinely need comparing, compare them on val or a freshly
      carved held-out slice — never on test. finalize takes `run_dir.best_id` (already
      chosen on val) precisely so the choice is made before the seal is touched.
      
      ## Report the uncertainty, and flag no-holdout runs
      
      A single-trial point estimate on test is honest about the split but dishonest
      about variance. Use ≥3 trials so `final.json` carries `stderr` and a pass^k
      reliability figure alongside the mean — a high mean with low pass^k is a fragile
      result the report should surface. And if the run was configured with no holdout
      (test == train/val, e.g. to fit a tiny task set), the number is a *fit* metric,
      not a held-out estimate; the report must label it so no reader mistakes it for
      generalization.
      
      ## Mapping to benchmark protocol
      
      Public benchmarks institutionalize this same seal: a hidden test set, a
      submission scored once, no resubmission tuning. τ-bench additionally reports
      pass^k so the headline reflects *reliability* across trials, not a single lucky
      run. finalize is the local, enforced version of that protocol for a single
      optimization run.
      
      ## Sources
      - Hastie, Tibshirani, Friedman, *The Elements of Statistical Learning* — train
        fits / validation selects / test estimates; why test stays sealed:
        https://hastie.su.domains/ElemStatLearn/
      - τ-bench (Yao et al., 2024) — scoring once and reporting pass^k reliability:
        https://arxiv.org/abs/2406.12045
      - Koehn, "Statistical Significance Tests for MT Evaluation" (EMNLP 2004) — why a
        reported difference needs uncertainty, not a bare point:
        https://aclanthology.org/W04-3250/
      - `cap_evolve/rundir.py` — the `test_used` flag and `TestSealError`.
      
  • scripts
    • abstract.py 165 B
      """The 'finalize' phase composes the project adapter + shared harness; it declares no
      abstract methods of its own. check.py verifies the wiring instead of stubs."""
      
    • check.py 1.6 KB
      """Contract: finalize scores the test split exactly once (seal-on-success) — a
      second finalize on the same run dir raises TestSealError.
      """
      
      from __future__ import annotations
      
      import sys
      import tempfile
      from pathlib import Path
      
      import _bootstrap  # noqa: F401
      
      from cap_evolve import Rollout, Score, Task, TestSealError, harness
      from cap_evolve.skillcheck import Checker, import_run, temp_run_dir
      
      
      class _Adapter:
          def tasks(self, split):
              return [Task(id=t, input={}) for t in ("a", "b", "c", "d")]
      
          def run_target(self, task, ctx, *, seed=0):
              return Rollout(task_id=task.id, output="ok")
      
          def score(self, task, rollout):
              return Score(task_id=task.id, reward=1.0)
      
          def materialize(self, candidate_dir):
              return {}
      
      
      def main() -> int:
          c = Checker("finalize")
          c.require_main(import_run())
      
          with tempfile.TemporaryDirectory() as d:
              rd, _ = temp_run_dir(Path(d), ids=("a", "b", "c", "d"), seed=0)
              adapter = _Adapter()
              best = Path(d) / "best"
              best.mkdir()
      
              payload = harness.finalize(adapter, run_dir=rd, best_dir=best)
              c.check("test" in payload and payload["test"]["reward"] == 1.0,
                      f"finalize did not produce a test result: {payload}",
                      note="test scored once on the sealed split")
      
              try:
                  harness.finalize(adapter, run_dir=rd, best_dir=best)
                  c.fail("second finalize succeeded — the test seal was not enforced")
              except TestSealError:
                  c.note("second finalize refused (test seal enforced)")
      
          return c.emit()
      
      
      if __name__ == "__main__":
          sys.exit(main())
      
    • run.py 1.9 KB
      """finalize — score the best candidate on the SEALED test split, exactly once."""
      
      from __future__ import annotations
      
      import argparse
      import json
      import sys
      from pathlib import Path
      
      import _bootstrap  # noqa: F401
      
      from cap_evolve import RunDir, harness
      from cap_evolve.check import load_adapter
      
      
      def main(argv=None) -> int:
          p = argparse.ArgumentParser(prog="finalize")
          p.add_argument("--run-dir", required=True)
          p.add_argument("--project", required=True)
          p.add_argument("--n-trials", type=int, default=1)
          args = p.parse_args(argv)
      
          run_dir = RunDir.open(Path(args.run_dir))
      
          try:
              from capevolve_telemetry import load_observers_from_state
              for obs in load_observers_from_state(run_dir.load_observer_state()):
                  run_dir.add_observer(obs)
          except Exception:  # noqa: BLE001
              pass
      
          adapter = load_adapter(Path(args.project))
          best_dir = run_dir.candidate_dir(run_dir.best_id)
          # Also score the unmodified seed (baseline) on the sealed test split, so the headline
          # is the honest optimized-vs-baseline improvement on held-out tasks. `baseline()` always
          # snapshots the seed, so a missing `candidates/seed` means a corrupted run dir — fail
          # fast rather than silently producing a misleading baseline==optimized comparison.
          seed_dir = run_dir.candidate_dir("seed")
          if not seed_dir.exists():
              raise FileNotFoundError(
                  f"baseline 'seed' candidate not found at {seed_dir} — the run dir looks "
                  "corrupted (baseline() should have snapshotted it). Refusing to finalize "
                  "without a baseline to compare on the sealed test split."
              )
          payload = harness.finalize(adapter, run_dir=run_dir, best_dir=best_dir,
                                     n_trials=args.n_trials, baseline_dir=seed_dir)
      
          run_dir.close_observers()
      
          print(json.dumps(payload, indent=2))
          return 0
      
      
      if __name__ == "__main__":
          sys.exit(main())
      
    • _bootstrap.py 3.6 KB
      """Thin shim: locate cap_evolve, then defer to cap_evolve._bootstrap.
      
      Skill scripts ``import _bootstrap`` first. The real path-resolution logic lives
      ONCE in ``cap_evolve._bootstrap`` (so it can't drift across skills); this shim
      only has to find that package, which means a minimal upward walk for ``core/`` —
      the single bit of bootstrapping that genuinely must run before cap_evolve is
      importable. Everything else delegates.
      """
      
      from __future__ import annotations
      
      import os
      import sys
      from pathlib import Path
      
      
      def _seed_path() -> None:
          """Minimal: put a dir containing the cap_evolve package on sys.path.
      
          ``CAPEVOLVE_CORE`` is honoured BEFORE any ambient import. An editable install of a
          *different* cap-evolve checkout registers a ``sys.meta_path`` finder, which outranks
          both ``sys.path`` and ``PYTHONPATH`` — so "cap_evolve imports fine" is not evidence that
          it imports the checkout you are standing in. Deferring to the ambient package here made
          an explicit override unreachable, and the symptom was a stale core silently answering
          for this one (``ModuleNotFoundError: cap_evolve.constraints`` from a checkout that
          predates that module). An explicit env var wins.
          """
          env = os.environ.get("CAPEVOLVE_CORE")
          want = Path(env).resolve() if env else None
          if want and (want / "cap_evolve" / "__init__.py").exists():
              loaded = sys.modules.get("cap_evolve")
              already = getattr(loaded, "__file__", None)
              if already and Path(already).resolve().parent.parent == want:
                  return                      # right checkout already imported: touch nothing
              p = str(want)
              if p in sys.path:
                  sys.path.remove(p)
              sys.path.insert(0, p)
              if loaded is not None:
                  # Evicting a module makes a re-import yield a DIFFERENT object, so anything
                  # already holding a reference fails an `is` check. Only ever do it when the
                  # loaded package really is the wrong checkout — otherwise this "fix" becomes
                  # the bug (it broke two identity assertions in core/tests exactly once).
                  for name in [m for m in sys.modules
                               if m == "cap_evolve" or m.startswith("cap_evolve.")]:
                      sys.modules.pop(name, None)
              for finder in list(sys.meta_path):
                  if "cap_evolve" in getattr(finder, "MAPPING", {}):
                      sys.meta_path.remove(finder)
              return
          # A checkout's own core outranks an ambient install. Without this, a skill script run
          # from checkout X silently executed against checkout Y's cap_evolve (an editable install
          # registers a sys.meta_path finder, which outranks sys.path), and the only symptom was
          # missing modules — or, worse, a green result measured against the wrong tree.
          here = Path(__file__).resolve()
          own = next((p / "core" for p in here.parents
                      if (p / "core" / "cap_evolve" / "__init__.py").exists()), None)
          if own is not None:
              os.environ.setdefault("CAPEVOLVE_CORE", str(own))
              return _seed_path()
          try:
              import cap_evolve  # noqa: F401
              return
          except Exception:
              pass
          cands = []
          for parent in here.parents:
              cands.append(parent / "core")
              cands.append(parent)
          for c in cands:
              if (c / "cap_evolve" / "__init__.py").exists():
                  p = str(c)
                  if p not in sys.path:
                      sys.path.insert(0, p)
                  return
      
      
      _seed_path()
      from cap_evolve._bootstrap import ensure_core  # noqa: E402
      
      # Anchor the upward walk at THIS skill script's location (not the core module's).
      ensure_core(Path(__file__).resolve())
      
  • meta.yaml 299 B
    component: phase
    name: finalize
    summary: Score the best candidate on the sealed test split, exactly once.
    entry: scripts/run.py
    abstract: scripts/abstract.py
    check: scripts/check.py
    needs: [candidate]
    provides: [report]
    compatible_with:
      capabilities: ["*"]
      optimizers: ["*"]
      algorithms: ["*"]
    
  • SKILL.md 5.7 KB
    ---
    name: finalize
    description: Score the best candidate on the held-out TEST split exactly once and seal the run. Use as the last evaluation step, after optimization stops. The run dir enforces the seal — a second finalize raises an error — so the headline number is produced once on data the optimizer never saw, the way an honest benchmark result must be.
    component: phase
    argument-hint: "--run-dir DIR --project DIR"
    allowed-tools: Read, Bash
    provides: [report]
    needs: [candidate]
    sources: [tau2bench]
    ---
    
    # finalize — the one honest number
    
    Optimization hill-climbs on val: every accept decision consumed val as a tuning
    signal, so by the end of search val is *optimistic* — it has been selected
    against. The number you **report** must come from data nothing was tuned against.
    finalize scores the run's best candidate on the sealed `test` split, once, and
    writes `final.json`. That file is the run's result.
    
    ## One finalize is two evals, not one
    
    A bare test number cannot be defended — a reader cannot tell whether it beat the
    capability you started with. So one finalize scores test **twice**: the best
    candidate as tag `FINAL`, and the untouched `seed` candidate as `FINAL_seed`
    (`harness.finalize`). `final.json` therefore carries `test`, `test_baseline`,
    `baseline_id`, and `test_delta` — the held-out *improvement*, which is the figure
    `report`, the dashboard, and the event stream all headline. If the best candidate
    IS the seed (nothing was accepted), the second eval is skipped and `test_delta` is
    0 by construction.
    
    So budget `--n-trials 3` as 3 trials × **2 candidates** × |test| rollouts — twice
    what the flag looks like it buys on a paid benchmark.
    
    Both evals sit inside **one** attempt and neither is a selection event: the delta
    is *reported*, never chosen on. That is why the seal counts attempts, not evals.
    
    ## The seal (why "exactly once")
    
    The instant test informs *any* choice — picking between finalists, "double-
    checking" a low number, re-running until it looks better — it stops being held
    out, because each peek is a selection event that pulls the number from an
    unbiased estimate toward an optimistic fit metric (`references/concepts.md`).
    
    `cap_evolve` enforces this in three parts (`rundir.py:358-407`), and the split
    between them is the whole design:
    
    - **reserve** — every `split="test"` eval first *checks* the seal without burning
      it, so no phase other than finalize can reach test at all.
    - **commit** — the seal burns only after `final.json` is written, so a finalize
      that dies *before* scoring leaves it unused and is honestly retryable. A
      transient crash must not destroy a run's headline number.
    - **attempt guard** — seal-on-success alone cannot tell "crashed before scoring"
      from "crashed after". A real run hit the second case: a finalize killed by a
      timeout had already scored test, the retry scored it again, and the reported
      headline was that second look. `begin_test_attempt` refuses a retry once test
      rollouts exist on disk, before anything is spent.
    
    The seal refuses that mistake by default; it is not unbypassable.
    `CAPEVOLVE_ALLOW_TEST_RESCORE=1` is a deliberate opt-in override
    (`rundir.py:166`). Its own message promises the use "is recorded in the run" —
    nothing records it (issue #341), so a run that took a second look currently looks
    identical to an honest one. If you set it, disclose it in the write-up yourself.
    
    Corollary: **all selection happens before finalize.** Choose the single best
    candidate on val, *then* finalize it. Finalists that genuinely need comparing get
    compared on val — never on test.
    
    ## If finalize refuses
    
    A `TestSealError` is three situations with three different right moves. Tell them
    apart from `<run>/rollouts/test/` and `test_used` in `splits.json`:
    
    | State | What happened | Do this |
    |---|---|---|
    | no test rollouts, seal unused | crashed before scoring | Re-run finalize — the case seal-on-success exists for. |
    | test rollouts exist, seal unused | crashed after scoring, before commit | Do **not** re-score. Read the rollouts under `<run>/rollouts/test/` and report what that attempt already computed. |
    | `test_used: true` | the run is finalized | Read `final.json` and regenerate the human artifact with `report` alone; `cap-evolve run --resume` skips finalize for you. |
    
    Never delete test rollouts or edit `splits.json` to get past the error — that
    manufactures a clean-looking number from a split that has already been seen.
    
    ## Dual-mode
    This phase runs two ways from the **same** SKILL.md: standalone as the slash command `/cap-evolve:finalize` (the `argument-hint` shows its run.py args), and orchestrator-callable — `cap-evolve run` / the `orchestrate` skill invokes the same `scripts/run.py` headlessly and threads the run dir between phases.
    
    ## How to run
    ```
    python scripts/run.py --run-dir .capevolve/run_XXXX --project .capevolve/project --n-trials 3
    ```
    Multiple trials give the headline an honest `stderr` and a pass^k reliability
    figure instead of one noisy point. Under `cap-evolve run` the count comes from
    `num_trials` in `capevolve.yaml` and **defaults to 1** — set it to ≥3, or the
    orchestrated headline ships with `stderr` 0: the exact single point this warns
    against. If the split was configured with no holdout (test == train/val) the
    number is a *fit* metric, not a held-out result; the dashboard flags it, so say so
    in the summary too.
    
    Then read the result instead of just filing it: test ≈ val means the val gain
    generalized, test ≪ val means search overfit val — a real finding, not a reason to
    re-score.
    
    ## References
    - `references/concepts.md` — why each peek biases the estimate, the
      train-fits / val-selects / test-estimates rationale, no-holdout runs, and how
      this maps to public benchmark protocol, with sources.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related