feat(research): persist what the gauntlet computed instead of printing it (#726) - #746
Merged
Merged
Conversation
…g it (#726) keel computed evidence and kept the prose. `trials pbo` printed ten figures and wrote none. `trials deflate` printed a DSR whose inputs the ledger could not supply, so the number could never be recomputed or checked. `trials monte-carlo` stored a distribution's ends and not its shape. All three surfaced as "the UI cannot show this" and none of them was a UI problem. THE CONSTRAINT THAT SHAPED THE DESIGN, FOUND BEFORE WRITING ANYTHING `_decode_summary` maps a summary value to `None`, an `int`, or `Decimal(value)`. A list raises `ValueError`; a dict raises `TypeError`. And it raises ON READ, inside `read_trials` -- which every later `verify_chain`, `trials list`, `trials pbo` and web page goes through. ONE nested value would make an APPEND-ONLY, git-tracked file unreadable forever, with no way to take it back. That is not hypothetical: `_decode_summary`'s own comment records a null value doing exactly this once already. So the guard moved to the WRITE, where it is a refusal rather than a catastrophe -- and every artifact below is stored as FLAT scalar keys. `final_p05`, not a nested quantile ladder. The shape a reader can survive is the shape a writer may use. WHAT IS NOW RECORDED CSCV: every field of `PBOResult` -- pbo, both degradation coefficients, prob_loss, both dominance flags, the column/block/combination counts, rows used and dropped, and how many columns were refused. Ten figures were computed and one reached the ledger. DSR: the INPUTS as well as the outputs, and the inputs are the point. `--sharpe` is a required operator input because the ledger stores no per-trial Sharpe, so DSR was not merely expensive to recompute -- it was impossible without synthesising a number the operator had supplied, which is the one thing this codebase refuses. Recorded at the moment they were stated, the figure can be checked rather than trusted. A run with no `--trial-sharpe-variance` still refuses to compute a DSR and now also records nothing: a stored figure nobody ran is worse than an honest gap. MONTE CARLO: a seven-point quantile ladder for finals and drawdowns. `distribution_min/median/max` say how far the resampling reached; the ladder says what its shape was, which is what a histogram needs. Quantiles rather than the raw array -- thousands of Decimals per row in a git-tracked file -- and NEAREST-RANK rather than interpolated, because every value in this distribution is an equity the model actually produced and an interpolated quantile is a number no path reached. TWO NEW DIAGNOSTIC KINDS, AND WHY NEITHER FEEDS BACK `cscv` and `deflated_sharpe` are measurements ABOUT a set of trials rather than trials themselves, so both are always `series_missing` -- and `matrix.build_matrix` refuses `series_missing` rows, so a recorded PBO can never become a column in the next PBO over the same file. A diagnostic that changed the thing it measured would be worse than one nobody kept. The test for that passed for the wrong reason first: it built the matrix over session `s1` while the recorded row landed in session `all`, so the SESSION FILTER excluded it and `series_missing` did no work at all. It now records into the same session and fails against a mutant that gives the row a real series. NO BACKFILL. The six existing gauntlet rows keep exactly the fields they have and the UI keeps reading "not recorded" for the rest -- the posture #721 settled for the audit chain. The 93 tracked rows still verify. ⛔ THE STRATHERN RAIL. Every figure here is a diagnostic. Storing them makes them easier to rank by, which is why `PBOResult` carries no configuration field and why nothing written here names a winning parameter set. Nine mutants killed: the summary guard removed, the guard admitting a list, `trials pbo` back to printing and discarding, the cscv row dropping the fields beyond pbo, the cscv row carrying a series and becoming a column, deflate recording a row with no variance, the dsr row dropping its inputs, the monte carlo ladder dropped, and the quantile interpolating instead of nearest-rank. Unblocks #708's Evidence Matrix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #726. Unblocks #708's Evidence Matrix.
keel computed evidence and kept the prose.
trials pboprinted ten figures and wrote none.trials deflateprinted a DSR whose inputs the ledger couldn't supply, so the number could never be recomputed or checked.trials monte-carlostored a distribution's ends and not its shape.The constraint that shaped the design
Found before writing anything:
_decode_summarymaps a summary value toNone, anint, orDecimal(value). A list raisesValueError; a dict raisesTypeError. And it raises on read, insideread_trials— which every laterverify_chain,trials list,trials pboand web page goes through. One nested value would make an append-only, git-tracked file unreadable forever, with no way to take it back.Not hypothetical:
_decode_summary's own comment records a null value doing exactly this once already.So the guard moved to the write, where it's a refusal rather than a catastrophe — and every artifact is stored as flat scalar keys (
final_p05, not a nested ladder). The shape a reader can survive is the shape a writer may use.What is now recorded
CSCV — every field of
PBOResult: pbo, both degradation coefficients,prob_loss, both dominance flags, the column/block/combination counts, rows used and dropped, columns refused. Ten figures computed, one reached the ledger.DSR — the inputs as well as the outputs, and the inputs are the point.
--sharpeis a required operator input because the ledger stores no per-trial Sharpe, so DSR wasn't merely expensive to recompute — it was impossible without synthesising a number the operator had supplied. A run with no--trial-sharpe-variancestill refuses to compute a DSR and now also records nothing: a stored figure nobody ran is worse than an honest gap.Monte Carlo — a seven-point quantile ladder for finals and drawdowns. Quantiles rather than the raw array (thousands of
Decimals per row in a git-tracked file), and nearest-rank rather than interpolated: every value here is an equity the model actually produced, and an interpolated quantile is a number no path reached.Two new diagnostic kinds, and why neither feeds back
cscvanddeflated_sharpeare measurements about a set of trials rather than trials themselves, so both are alwaysseries_missing— andmatrix.build_matrixrefusesseries_missingrows, so a recorded PBO can never become a column in the next PBO over the same file.The test for that passed for the wrong reason first: it built the matrix over session
s1while the recorded row landed in sessionall, so the session filter excluded it andseries_missingdid no work at all. It now records into the same session and fails against a mutant that gives the row a real series.Refusals
No backfill. The six existing gauntlet rows keep exactly the fields they have and the UI keeps reading "not recorded" for the rest — the posture #721 settled for the audit chain. The 93 tracked rows still verify (
verify_chain→[]).⛔ The Strathern rail. Every figure here is a diagnostic. Storing them makes them easier to rank by, which is why
PBOResultcarries no configuration field and nothing written here names a winning parameter set.Gates
E501is pre-existing indocs/experiments/)trials pboback to printing and discarding · the cscv row dropping fields beyondpbo· the cscv row carrying a series and becoming a column · deflate recording a row with no variance · the dsr row dropping its inputs · the Monte Carlo ladder dropped · the quantile interpolating instead of nearest-rank🤖 Generated with Claude Code
https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6