fix(sleep): honor val_fraction and test_fraction in the nightly cycle - #235
Open
Bogdan (Dan) Baciu (bogdanbaciu21) wants to merge 1 commit into
Open
Conversation
config.py documents val_fraction and test_fraction and assign_splits() implements both, but the nightly path only ever forwarded the legacy holdout_fraction alias: mine() could not carry the new knobs, so test_fraction was dead config -- no untouched test split could exist and no held-out test score was ever recorded. - mine() now mirrors assign_splits(): val_fraction/test_fraction are the real controls, holdout_fraction stays a legacy alias with unchanged override semantics for existing callers. - run_sleep_cycle() resolves the alias (documented value-based precedence, since the merged config has no key provenance) and passes both fractions through; both are now recorded in the evidence config row. - Nights that produce test-split tasks score the night's FINAL documents on the untouched test split (same replay_batch + aggregate_scores path the experiment harness uses) and write a write-only test/held_out_score row to evidence.jsonl. The gate never reads it. - Defaults are bit-for-bit unchanged: test_fraction=0.0 yields the legacy two-way split, no test tasks, no extra calls. Tests: tests/test_split_wiring.py pins the wiring end to end (mine forwarding, alias precedence, evidence row present/absent).
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.
What Problem This Solves
Fixes an issue where operators who set
test_fraction(orval_fraction) in their Sleep config silently got neither: the nightly cycle only ever forwarded the legacyholdout_fractionalias, sotest_fractionwas dead config. No untouched test split could exist, which means every nightly lift number was a validation-split number, and validation is the same signal candidate skills are selected against.Why This Change Was Made
config.pydocumentsval_fractionandtest_fraction, andassign_splits()already implements both (withholdout_fractionas its alias), butmine()could not carry them andcycle.pypassed only the alias. This PR wires what the config promises:mine()now mirrors theassign_splits()knobs;holdout_fractionstays a legacy alias with unchanged override semantics for existing callers.val_fractionstill at its shipped default) and records both fractions in the evidence config row.replay_batchplusaggregate_scorespath the experiment harness uses, and write a write-onlytest/held_out_scorerow toevidence.jsonl. The gate never reads it.Non-goals: no gate-semantics change, and no new split semantics (content-hash near-duplicate hardening is deliberately a separate follow-up).
Project Fit
consolidate._split()already documents that test is held out entirely and scored by the caller; in the nightly, this PR is that caller.User Impact
Operators setting
test_fractionnow get what the config promises: a real untouched test split and a per-night held-out score row inevidence.jsonl. Operators who leave defaults alone see zero change: withtest_fraction: 0.0the split is bit-for-bit the legacy two-way split, no test tasks exist, and the scoring block never runs (no extra backend calls, no extra tokens).Proof
Before (stock
main): the new wiring tests cannot pass.mine(..., val_fraction=..., test_fraction=...)is aTypeErrorbecause the signature cannot carry the knobs, a seeded night with a test-split task writes no held-out row, and no cycle-level alias resolution exists.After (this branch):
Baseline at the same commit before this change: 1096 passed, 11 skipped. The delta is exactly the 10 new tests, with zero regressions.
The behavior-preservation claims are pinned by tests rather than asserted:
test_default_call_is_two_way_like_before(defaults reproduce the legacy split),test_legacy_holdout_alias_still_wins_when_passed(alias override identical to explicitval_fraction), andtest_no_score_row_without_test_tasks(legacy nights write nothing new).Academic Support
The measurement-validity motivation, briefly:
Testing
New:
tests/test_split_wiring.py, 10 tests pinning (a)mine()forwards both fractions intoassign_splits, (b) cycle-level alias precedence (legacyholdout_fractionconfigs keep their behavior; a user-setval_fractionwins), and (c) thetest/held_out_scoreevidence row appears exactly when test-split tasks exist. Full suite:python -m pytest -q.Every failure above is present on stock
mainat the same base commit and is unrelated to this diff: one Copilot backend parse case (Linux and macOS), two macOS/varsymlink path checks in the Superpowers adapter tests, and 37 POSIX-only adapter tests on Windows (baseline there is 1054 passed with the same 37 failures). Relative to that baseline, this branch adds exactly the 10 new tests as passes on every platform, with zero new failures anywhere.Limitations & Negative Results
val_fractionto exactly its default while also settingholdout_fractiongets the alias. This is documented in the_resolve_split_fractionsdocstring.test_fractionreduces train and val mass;0.0stays the default for that reason.Reproduce It Yourself