Skip to content

chore(deps): agent-runtime 0.175.0, add agent-eval, and prove the join compiles - #37

Merged
drewstone merged 6 commits into
mainfrom
chore/bump-runtime-and-eval
Aug 27, 2026
Merged

chore(deps): agent-runtime 0.175.0, add agent-eval, and prove the join compiles#37
drewstone merged 6 commits into
mainfrom
chore/bump-runtime-and-eval

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

The runtime was pinned at 0.142.3 against a current 0.175.0, and @tangle-network/agent-eval was not installed at all, so the optimizer this study wants to compose with could not be typechecked against.

0.175.0 carries what a game needs.VerifyResult gained keepGoing and score. That is the disposition a score requires and a test suite does not: a green suite has nothing left to win, while a game always has a higher number above it. hillclimb.ts already returns that exact shape.

examples/agent-eval-fit.mts is the proof, and it exists to be compiled. A playproof cell types as an agent-eval Scenario — where seedGroup is what makes two profiles on one game a paired comparison rather than two unrelated samples. Playing a candidate types as a Verifier. A change on either side that breaks the join now fails the typecheck instead of failing a study three hours in.

Also fixes a flaky test the bump exposed, which was flaky before it. It spawned a child that exits, slept 120 ms, then asserted the driver had already been told the child was gone — which asserts how busy the machine is. It now waits for the condition, and passed 10 of 10 consecutive runs.

Local gate: 20 of 20 test files pass, boundary and attestation-blindness pass, 0 repo typecheck errors.

…n compiles
The runtime was pinned at 0.142.3 against a current 0.175.0, and agent-eval
was not installed at all, so the optimizer this study wants to use could not
be typechecked against.
0.175.0 carries what a game needs. `VerifyResult` gained `keepGoing` and
`score`, which is the disposition a score requires: a test suite is green and
there is nothing left to win, while a game always has a higher number above
it. `hillclimb.ts` already returns that exact shape.
`examples/agent-eval-fit.mts` is the proof, and it exists to be compiled. A
playproof cell types as an agent-eval `Scenario`, where `seedGroup` is what
makes two profiles on one game a paired comparison instead of two unrelated
samples. Playing a candidate types as a `Verifier`. A change on either side
that breaks the join now fails the typecheck instead of failing a study three
hours in.
Also fixes a flaky test the bump exposed. It spawned a child that exits, slept
120 ms, then asserted the driver had already been told the child was gone.
That asserts how busy the machine is. It now waits for the condition, and
passed 10 of 10 consecutive runs.
…he statistics
`compareOptimizationMethods` owns everything hard about comparing agent
profiles: disjoint train, selection and final-test scenarios, bootstrap
resamples, Bonferroni-adjusted simultaneous confidence across every contrast, a
shared cost ceiling, and a policy that refuses to score a final test when the
search history is incomplete.
That machinery is the reason to route a game through it instead of tabulating
means. On this repo's own three-profile study the ranking read cleanly as
opus > sonnet > haiku; a paired bootstrap over the same replicates put two of
the three contrasts across zero, and only opus over haiku survived. A table of
means said one thing and an interval said another.
Playproof supplies two things. A cell becomes a scenario, where `seedGroup`
hands two surfaces the same seed for a replicate and makes the comparison
paired rather than two unrelated samples of a noisy game. A played cell becomes
a judge score, deterministic, because the game already produced the number.
A blocked cell throws instead of scoring zero. A cell that could not be built
did not play badly, and folding it into a zero would let an unbuildable clock
read as a bad candidate.
The surface reaches the game only through the profile, so none of this needs to
know which of the ten searchable surfaces is being moved.
A cost of null could mean two different things and printed the same either
way: the agent reported nothing, or the agent bills a plan and has no
per-request figure to report. A study that reads those the same ranks the arm
nobody could meter first on cost per point.
Reuse the vocabulary the rest of the stack already has rather than inventing a
second one. `authMode: 'api-key' | 'oauth'` is the union in
`@tangle-network/agent-interface`, and the CLI agent registry passes the same
value to a spawned backend as `CLAUDE_CODE_AUTH_MODE`. Both names are used
here, so a run in this harness and a run in a sandbox describe themselves
identically.
The launcher honours an explicit `CLAUDE_CODE_AUTH_MODE`, and otherwise reads
`api-key` when `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set and
`oauth` when neither is. Verified on all three paths.
The value travels with the meter to the row, and the runner prints an absent
cost as its reason: `unbilled(oauth)` rather than a bare blank, and never
`$0.0000`.
…were not
An earlier count said 41 em-dashes across docs/. Most were markdown table
cells using a dash as a value separator, and one is verbatim CI output inside
a fence. Rewriting those would have damaged a table and falsified a quote.
20 were prose, and each is rewritten by hand rather than substituted. A
parenthetical becomes brackets, an appositive becomes a colon, and a clause
that was doing a full sentence's work becomes one.
Also fixes a sentence in the RetroArch gate section whose subject drifted: the
blind search watched PyBoy's work RAM to find the reference's channel
addresses, and that fact was nested inside a clause about replaying through
gambatte.
`gap()` sends one run-loop iteration between frame advances. That is a
barrier, not a courtesy: `FRAMEADVANCE` returns as soon as RetroArch accepts
it, so without an intervening iteration the next advance can be issued while
the previous frame is still settling.
`command` returns None when it gives up, and `gap` discarded that return. A
barrier that did not happen looked exactly like one that did.
This is a diagnostic as much as a fix. The same-process replay gate has failed
four times today, and its divergence has a specific shape: two replays of one
boot state and one input log agree byte for byte to emuFrame 811, then differ
on channel values at IDENTICAL frame numbers. Identical frame numbers rule out
a miscounted advance and point at a run loop that had not settled.
It is NOT proven that a missed barrier causes it. `COMMAND_TIMEOUT` is 5
seconds with a resend loop, so a timeout there means RetroArch is wedged
rather than busy, which should be rare. The fix decides the question: a gate
that now fails with "the run loop was not synchronised" was this, and one that
still diverges silently is something else.
`save_state` and `load_state` also discard a command return, and are left
alone deliberately. Both verify the outcome downstream, by waiting for the
state file and by reading the log, which is a stronger check than the return.
The `authMode` column read empty on every authored cell. The row's `authMode`
describes whoever played, and an authored cell is played by a program with no
credentials at all, so that field was correctly null while the build behind it
was very much billed to something.
The authoring phase already read the value and then dropped it. It now travels
in the `build` column, beside the tokens and minutes that were spent alongside
it, and the runner prints the build cost on the cell line: minutes, tokens,
and either a dollar figure or `unbilled(oauth)`.
Measured on the six-cell study this fixes: sonnet spent 5.14M tokens building
a player that lost the game after 317 decisions, while opus spent 1.19M on one
that survived all 2000. Build cost and play score answer different questions
and a single number cannot carry both.
@drewstone
drewstone merged commit 40625bf into mainAug 27, 2026
6 checks passed
@drewstone
drewstone deleted the chore/bump-runtime-and-eval branch August 27, 2026 06:58
Sign up for freeto 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

@drewstone