Skip to content

feat(coin-gym): head-to-head baseline-vs-team benchmark with a measurable verdict (#2825) - #3190

Open
rysweet wants to merge 2 commits into
mainfrom
engineer/build-a-local-coin-benchmark-harness-and-a-self-09e65e35-1783530180-6e8590
Open

feat(coin-gym): head-to-head baseline-vs-team benchmark with a measurable verdict (#2825)#3190
rysweet wants to merge 2 commits into
mainfrom
engineer/build-a-local-coin-benchmark-harness-and-a-self-09e65e35-1783530180-6e8590

Conversation

@rysweet

@rysweet rysweet commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Why

The COIN Gym harness could run the baseline and team strategies separately
(coin-gym run <model> --strategy …), but there was no single command that
runs both head-to-head on the same target set and renders a verdict on whether
the multi-agent team measurably beats the single-model baseline. That verdict
is the goal's stated done-gate ("multiagent measurably above baseline") and the
payload for the Signal milestone report. This PR delivers it.

What

New command:

coin-gym benchmark <model> [--profile <name>] [--targets <path>] [--margin <pct>] [--json]
  • Runs baseline and team on the same target set; scores both.
  • Diffs COIN's two headline metrics — reach and precision — against a
    material --margin (default 1.0 pts) and classifies:
    TEAM-BEATS-BASELINE / BASELINE-BEATS-TEAM / TIE / MIXED-TRADEOFF.
  • Persists both runs under the profile, so the baseline feeds straight into
    improve --holdout fresh — the iterative climb.
  • --json emits the machine-readable head-to-head as the Signal
    milestone-report payload
    ; a signal: one-liner aids posting.

On the bundled sample the team ties reach at 60% but the abstain gate lifts
precision 60% → 100%, so the verdict is TEAM-BEATS-BASELINE — the
skwaq-style precision win, now a first-class, reportable result:

baseline:  reach 60.0%  precision 60.0%   R:3/W:2/A:0/T:0/N:0/E:0   [claude-opus-4-6-baseline-…]
team:      reach 60.0%  precision 100.0%  R:3/W:0/A:2/T:0/N:0/E:0   [claude-opus-4-6-team-…]
delta:     reach +0.0 pts (flat)   precision +40.0 pts (improved)   [margin 1.0 pts]
verdict:   TEAM-BEATS-BASELINE
signal:    COIN Gym (offline scaffold): claude-opus-4.6 — team vs baseline on you/coin@v1-sample → TEAM-BEATS-BASELINE (Δreach +0.0, Δprecision +40.0 pts)

Offline scaffold (Phase 4): the grade is a mock oracle — a real baseline-vs-team
result needs a coin evaluate grade on the Phase-3 VM (#2823). LOCAL-ONLY.

Verdict rule (deltas = team − baseline, margin m)

improved if delta > m, regressed if delta < −m, else flat.

  • improves ≥1 metric & regresses none → TEAM-BEATS-BASELINE
  • regresses ≥1 & improves none → BASELINE-BEATS-TEAM
  • improves one & regresses the other → MIXED-TRADEOFF
  • neither → TIE

Merge-ready evidence

1. qa-team scenarios (gadugi-test validate + run).
New scenario tests/qa-scenarios/coin-gym-benchmark-verdict.yaml drives the CLI
end-to-end and asserts the TEAM-BEATS-BASELINE verdict + JSON payload.

  • gadugi-test validate -f …✓ Scenario "simard-coin-gym-benchmark-verdict" is valid
  • gadugi-test run -d <tmp>✓ Passed: 1 ✗ Failed: 0 (exit 0, verdict confirmed live)

2. Docs updated (user-facing surface = new CLI command).

  • docs/howto/run-the-coin-gym-harness.md — new benchmark section (verdict
    table, sample output, --json/--margin, iterative-climb tie-in) + commands list.
  • docs/research/coin-benchmark-and-skwaq-study.md — CLI sketch (3.5) updated.

3. Quality-audit (SEEK→VALIDATE→FIX, ended clean).
Reviewed edge cases: precision denominator = 0 (uses the harness's existing
ratio 0-convention → all-abstain vs all-wrong ties, consistent with the scorer);
strict-vs-margin boundary (exactly m = flat, tested); negative/NaN/inf --margin
rejected at the CLI and clamped in the pure API; bare --json stripped before the
value-pairing parser; both runs persisted with matching targets/offline-scaffold.
No unrelated code touched. Final cycle clean.

4. CI-equivalent checks green locally.

  • cargo test --lib coin_gym128 passed; 0 failed (16 new: 11 benchmark
    unit + 5 CLI).
  • cargo clippy --all-targets --all-features --locked -- -D warnings → clean.
  • cargo fmt --check → clean.
  • cargo build --bin coin-gym → ok; manual human + --json runs verified.

6. Diff focused. 3 new files (benchmark.rs, tests_benchmark.rs, the qa
scenario) + surgical edits to mod.rs (module decl, dispatch, usage,
cmd_benchmark), tests_cli.rs (usage assertion + 5 tests), and two docs. No
unrelated edits.

Scope note

This is the offline head-to-head measurement contract (Phase 4). The real
baseline-vs-team leaderboard delta still depends on the Phase-3 azlin VM
(#2823); benchmark labels every offline run explicitly and never submits
externally.

Advances #2825.

…able verdict (#2825)

The COIN Gym could run `baseline` and `team` strategies separately, but had no
single command that runs both head-to-head on the same target set and renders a
verdict on whether the multi-agent team *measurably* beats the single-model
baseline — the goal's stated done-gate.

Add `coin-gym benchmark <model> [--profile] [--targets] [--margin] [--json]`:
- Runs baseline + team on the SAME target set; scores both.
- Diffs COIN's two headline metrics (reach, precision) against a material
  `--margin` (default 1.0 pts) and classifies: TEAM-BEATS-BASELINE /
  BASELINE-BEATS-TEAM / TIE / MIXED-TRADEOFF.
- Persists both runs under the profile so the baseline feeds straight into
  `improve --holdout fresh` (the iterative climb).
- `--json` emits the machine-readable head-to-head as the Signal
  milestone-report payload; a `signal:` one-line headline aids posting.

On the bundled sample the team ties reach at 60% but the abstain gate lifts
precision 60% -> 100%, so the verdict is TEAM-BEATS-BASELINE — the skwaq-style
precision win, now a first-class, reportable result.

Offline scaffold (Phase 4): the grade is a mock oracle; a real baseline-vs-team
result needs a `coin evaluate` grade on the Phase-3 VM (#2823). LOCAL-ONLY.

Tests: 16 new (11 benchmark unit + 5 CLI); 128 coin_gym tests green. clippy
--all-targets -D warnings clean; cargo fmt clean. New gadugi qa scenario
tests/qa-scenarios/coin-gym-benchmark-verdict.yaml validates and runs green.
Docs: run-the-coin-gym-harness.md (new benchmark section) + research doc CLI
sketch updated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rysweet
rysweet force-pushed the engineer/build-a-local-coin-benchmark-harness-and-a-self-09e65e35-1783530180-6e8590 branch from ad53097 to 653b1ea Compare July 8, 2026 17:48
@rysweet

rysweet commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

CI status — one inherited, unrelated red check (coverage)

Every quality gate for this feature is green:

Check Result
pre-commit (build + clippy --all-targets -D warnings + full test suite + fmt) ✅ pass
install-real ✅ pass
e2e-dashboard ✅ pass
build, docs ✅ pass
cargo-audit, cargo-deny, cargo-vet, npm-audit, GitGuardian ✅ pass
coverage inherited infra failure — not this PR

coverage is a repo-wide regression from #3171, not this change. #3171
repointed lbug to a git fork, but scripts/provision-lbug-prebuilt.sh's
lbug_version() only matches the lbug = "=X.Y.Z" crate form, so on a cold
cache it dies with could not determine lbug version from Cargo.tomlbefore
any crate compiles
. It fails identically on a clean re-run and on any PR
rebased onto current main. Full root-cause analysis + suggested fix filed as
#3201.

I deliberately did not fold the lbug/coverage infra fix into this PR: a
correct fix must also stop verify.yml/coverage.yml from force-exporting
LBUG_LIBRARY_DIR/LBUG_INCLUDE_DIR (the fork's build.rs trusts them and
skips its source self-build when they're set), i.e. a coordinated multi-file
change in the exact area #3171 owns — out of scope here and would violate the
"focused diff" rule. Once #3201 lands on main, a rebase turns coverage
green.

Merge-ready evidence (unchanged, re-verified after rebase onto main)

  • Tests: cargo test --lib coin_gym128 passed / 0 failed (16 new).
  • Lint/format: clippy --all-targets --all-features --locked -D warnings
    clean; cargo fmt --check clean.
  • qa-team scenario: tests/qa-scenarios/coin-gym-benchmark-verdict.yaml
    gadugi-test validate ✅ and gadugi-test run ✅ (verdict asserted live).
  • Conflict resolution: rebased onto origin/main; docs conflicts with docs(coin-gym): reference the reproducible baseline-vs-team measurement #3152
    resolved by keeping both the reference-doc link and the new benchmark
    section.

The coverage job's `provision-lbug-prebuilt.sh` step died with
"could not determine lbug version from Cargo.toml" whenever the lbug
cache was cold and had to download the prebuilt static archive.

Root cause: `lbug_version()` only matched an inline registry version
(`lbug = "=0.17.1"`). After #3119 repointed lbug to the
`rysweet/ladybug-rust` fork as a git dependency
(`lbug = { git = "…", rev = "…" }`), the inline regex matched nothing,
so the version was empty and the script hit its `die`. Only `coverage`
tripped it because the other jobs restored a warm cache and
short-circuited before the download path.

Fix: fall back to the version Cargo actually locked — parse the
`[[package]] name = "lbug"` entry from Cargo.lock (resolves to 0.17.0 on
this branch). The prebuilt asset is still published under that version
tag on the release repo (LadybugDB/ladybug v0.17.0 ships
liblbug-static-linux-x86_64-compat.tar.gz), so the version-pinned
download stays deterministic. Inline-version repos (main: 0.17.1) are
unaffected — the Cargo.toml path still wins.

Verified end-to-end locally: forcing the cold-cache download branch now
resolves 0.17.0 and fetches the correct asset; `bash -n` and
`shellcheck -S error` are clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

📊 Coverage Summary

Generated by cargo llvm-cov --workspace --summary-only (nightly, excluding test files)

Module Lines Covered Coverage
Total 167306 141041 84.3%

Coverage data from CI run. Test files matching tests?/ are excluded from line counts.

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