Skip to content

feat(adaptive): measure whether the learning is real - #68

Merged
sanil-23 merged 1 commit into
tinyhumansai:mainfrom
sanil-23:feat/adaptive-eval-slope
Aug 19, 2026
Merged

feat(adaptive): measure whether the learning is real#68
sanil-23 merged 1 commit into
tinyhumansai:mainfrom
sanil-23:feat/adaptive-eval-slope

Conversation

@sanil-23

Copy link
Copy Markdown
Contributor

feat(adaptive): measure whether the learning is real

This crate accumulates knowledge in five ways — a ledger, lessons with
help rates, workflow scores, repaired variants, a promotion gate — and
had no way to answer the only question that matters about any of it: does
an episode go better because earlier episodes happened?

Derived from the eval design in a sibling PoC, which had already worked
out the hard part: a success rate cannot answer it. On ten unrelated
tasks solved once each, a learning loop and a plain retry loop produce
identical numbers. Neither can a single arm — "it solved six of six" is
compatible with the ledger being decorative. What answers it is a family
of related tasks run twice in the same order, differing only in whether
anything survives between episodes, and the quantity to read is the
slope: attempts-to-success falling as the family progresses.

Two pieces, because that experiment needed two things the crate lacked.

evals::slopeEpisode, two arms, the fit, and a comparison that
refuses to call a bend a win unless the arm also converges (twenty
attempts falling to ten loses to a flat two). Episode::of reads an
episode off the ledger rows the loop already writes, rather than off a
parallel bookkeeping nobody uses.

evals::arms — the control. A loop with learning off is not a loop
with its ledger removed: this ledger is load-bearing within an episode,
holding the exclusion list that stops attempt four repeating attempt two.
Forgetful blanks exactly the cross-episode reads — lessons, evidence,
workflow scores, repair lineage — and leaves the rest. Writes still
happen and are never read, so the control arm still pays for
consolidation; an arm that skipped the writes would look cheaper for a
reason unrelated to learning.

Three departures from the design as derived, each one a defect the
running example found:

An arm that solved nothing must not tie with an arm that solved four.
The example produced exactly that and reported no evidence, because both
slopes read 0.0 — one meaning "flat", the other meaning "two points are
needed for a line and there was one". Going from never to once is the
largest fall in attempts-to-success there is. trend() and
mean_attempts() are now Option, the report emits null rather than a
flattering zero, and compare returns a Verdict naming which test it
won on: SolvedMore, Converged, or NoEvidence. SolvedMore is
guarded by "no worse per solve" so it cannot decay into the success-rate
metric this design exists to avoid.

A wrong answer the judge accepted is not a success. Episode carries
an outside check separately from the verdict, and Outcome names all
four states — solved, refused (right, judge would not accept), wrong
(judge accepted, world refuted), failed. None for "not checked" is
distinct from Some(true), so a report can say which runs were taken at
the judge's word.

A failed episode is a gap, not a shift. Wins are fitted at their
original positions, so a wasted episode makes the measured convergence
shallower — the family took an extra episode to cover the same ground.
The test states the direction explicitly because the intuition runs the
other way.

No task set ships here. A family has to be chosen for shared technique
and checkable answers, and belongs to whoever runs the eval; a list baked
into a host-agnostic crate would be an opinion about somebody else's
domain. The example carries a simulated one, and says so.

This crate accumulates knowledge in five ways — a ledger, lessons with
help rates, workflow scores, repaired variants, a promotion gate — and
had no way to answer the only question that matters about any of it: does
an episode go better because earlier episodes happened?

Derived from the eval design in a sibling PoC, which had already worked
out the hard part: **a success rate cannot answer it.** On ten unrelated
tasks solved once each, a learning loop and a plain retry loop produce
identical numbers. Neither can a single arm — "it solved six of six" is
compatible with the ledger being decorative. What answers it is a family
of related tasks run twice in the same order, differing only in whether
anything survives between episodes, and the quantity to read is the
slope: attempts-to-success falling as the family progresses.

Two pieces, because that experiment needed two things the crate lacked.

**`evals::slope`** — `Episode`, two arms, the fit, and a comparison that
refuses to call a bend a win unless the arm also converges (twenty
attempts falling to ten loses to a flat two). `Episode::of` reads an
episode off the ledger rows the loop already writes, rather than off a
parallel bookkeeping nobody uses.

**`evals::arms`** — the control. A loop with learning off is *not* a loop
with its ledger removed: this ledger is load-bearing within an episode,
holding the exclusion list that stops attempt four repeating attempt two.
`Forgetful` blanks exactly the cross-episode reads — lessons, evidence,
workflow scores, repair lineage — and leaves the rest. Writes still
happen and are never read, so the control arm still pays for
consolidation; an arm that skipped the writes would look cheaper for a
reason unrelated to learning.

Three departures from the design as derived, each one a defect the
running example found:

**An arm that solved nothing must not tie with an arm that solved four.**
The example produced exactly that and reported no evidence, because both
slopes read `0.0` — one meaning "flat", the other meaning "two points are
needed for a line and there was one". Going from never to once is the
largest fall in attempts-to-success there is. `trend()` and
`mean_attempts()` are now `Option`, the report emits `null` rather than a
flattering zero, and `compare` returns a `Verdict` naming which test it
won on: `SolvedMore`, `Converged`, or `NoEvidence`. `SolvedMore` is
guarded by "no worse per solve" so it cannot decay into the success-rate
metric this design exists to avoid.

**A wrong answer the judge accepted is not a success.** `Episode` carries
an outside check separately from the verdict, and `Outcome` names all
four states — solved, refused (right, judge would not accept), wrong
(judge accepted, world refuted), failed. `None` for "not checked" is
distinct from `Some(true)`, so a report can say which runs were taken at
the judge's word.

**A failed episode is a gap, not a shift.** Wins are fitted at their
original positions, so a wasted episode makes the measured convergence
shallower — the family took an extra episode to cover the same ground.
The test states the direction explicitly because the intuition runs the
other way.

No task set ships here. A family has to be chosen for shared technique
and checkable answers, and belongs to whoever runs the eval; a list baked
into a host-agnostic crate would be an opinion about somebody else's
domain. The example carries a simulated one, and says so.
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Important

Review skipped

This review includes 6 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 48f8834f-9c44-4715-a299-8cbfeb406d6f

📥 Commits

Reviewing files that changed from the base of the PR and between dbab492 and b86731a.

📒 Files selected for processing (6)
  • crates/adaptive/examples/eval.rs
  • crates/adaptive/src/evals/arms.rs
  • crates/adaptive/src/evals/mod.rs
  • crates/adaptive/src/evals/slope.rs
  • crates/adaptive/src/evals/tests.rs
  • crates/adaptive/src/lib.rs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

$0.0000 · 0 in / 0 out · 673 embedded · openrouter/openai/text-embedding-3-small

@tinysweeper

tinysweeper Bot commented Aug 19, 2026

Copy link
Copy Markdown

How this change flows

0 changed behaviours across 6 relationships. 5 surrounding behaviours are shown (60 graph nodes walked). 30 further behaviours left out to keep the diagram readable.

flowchart LR
  n0["ep"]:::impacted
  n1["series"]:::impacted
  n2["experiment"]:::impacted
  n3["...pted_and_the_world_refuted_is_not_a_solve"]:::impacted
  n4["...ut_refused_is_reported_apart_from_failing"]:::impacted
  n1 -->|calls| n0
  n2 -->|calls| n0
  n3 -->|calls| n0
  n3 -->|tests| n0
  n4 -->|calls| n0
  n4 -->|tests| n0
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading

Green: changed behaviour. Grey: surrounding behaviour. Arrows name the call, use, implementation, or test relationship. Orange: has findings. Red: has a finding that blocks the merge.

tinysweeper 0.1.0

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Aug 19, 2026
@sanil-23
sanil-23 merged commit fa06377 into tinyhumansai:main Aug 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant