feat(format): training-loop-v1 6-gate PARTIAL discharge#1389
Closed
noahgift wants to merge 2 commits into
Closed
Conversation
Binds FALSIFY-LOOP-001..006 from training-loop-v1 at PARTIAL_ALGORITHM_LEVEL via 6 verdict functions plus a reference EMA helper. - LOOP-001: EMA(losses[0..5]) > EMA(losses[len-5..]) (model is learning) - LOOP-002: every epoch's val_loss finite + accuracy ∈ [0, 1] - LOOP-003: |restored_loss - checkpoint_loss| < 0.01 (full state restored) - LOOP-004: LR climbs in warmup, decays post-warmup (cosine schedule) - LOOP-005: train/val sets disjoint AND |train| + |val| == total - LOOP-006: every adjacent epoch order differs (shuffle applied) ## Five Whys 1. Why does training-loop-v1 list 6 falsification IDs without algorithm-level discharge? PMAT lints flagged FALSIFY-LOOP-001..006 as unbound at PARTIAL_ALGORITHM_LEVEL. 2. Why does that block ship? Coverage % cannot move while peripheral training-loop invariants have no algorithm-level verdict module. 3. Why bind both verdicts AND a reference `ema()` helper? The loss-decreasing gate (LOOP-001) needs deterministic EMA — the contract YAML uses `ema(&[..5])` and `ema(&[15..])`. Pinning the alpha-from-len formula in-module prevents future "we tried a different smoothing" drift from silently passing the gate. 4. Why a 1e-2 tolerance for LOOP-003 (not bit-exact)? Per the contract YAML: `(restored_loss - loss_at_5).abs() < 0.01`. Numerical evaluation passes through the same float ops; small rounding differences (e.g., reordered batch summation when restoring optimizer state) are tolerated, but a missing optimizer state (drift > 0.01) is correctly caught. 5. Why HashSet equality for LOOP-005 vs sorted-vec? IDs are unordered; sorted-vec equality would impose an arbitrary canonical-order constraint the contract doesn't require. HashSet captures the set semantics exactly: "no element appears in both sets." Adds 30 unit tests including 9-bucket loss sweep + 3-state shuffle table. Realistic-healthy walks the canonical 20-epoch train run; pre-fix walks 6 simultaneous regressions (flat losses, NaN val_loss, checkpoint drift, constant LR, train/val overlap, no shuffle). No runtime % shift; algorithm-level coverage advances by 6 gates.
0218b77 to
28c570e
Compare
Contributor
Author
auto-merge was automatically disabled
May 12, 2026 09:21
Pull request was closed
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.
Summary
training-loop-v1atPARTIAL_ALGORITHM_LEVELvia 6 verdict functions + referenceema()helper.Gates bound
EMA(losses[0..5]) > EMA(losses[len-5..])(learning)|restored - checkpoint|.abs() < 0.01|train| + |val| == totalReference helper
ema(values) -> f32withalpha = 2/(n+1)— pinned EMA formula matches the YAML test code.Five Whys
See commit message — captures why bind reference EMA in-module, why 1e-2 tolerance for restorable, and why HashSet for split disjointness.
Test plan
cargo test -p aprender-core --lib loop_001_006— 30 passed🤖 Generated with Claude Code