feat(rhythm): surfacing-only tempo-octave evidence at extremes (PR-G3) - #212
Merged
Conversation
slittycode
force-pushed
the
accuracy/genre-corpus-expansion
branch
from
July 13, 2026 05:52
fc0fc92 to
deaa72a
Compare
slittycode
force-pushed
the
accuracy/genre-tempo-octave-evidence
branch
from
July 13, 2026 05:52
31f71d6 to
2d33463
Compare
…acy PR-G3) Adds bpmOctaveEvidence (full-mode only): simple-ratio candidates (1:2, 2:3, 1:1, 3:2, 2:1 of the shipped bpm, clamped 40-220) scored against two inter-onset streams — low-band kick articulation (integer multiples, fit divided by the median multiple) plus the full-band hat/snare grid (admits the 8th-note 0.5 multiple), which pins the notated tempo when the kick only marks bars. Never overrides bpm (invariant #1); abstains (null) on sparse/beatless material. Measured on the corpus: recovers all three broken octave cases (174 -> 173.8, 190 -> 189.8, halftime-174 -> 175.5 via 3:2) with zero false alarms on the 10 correctly-shipped rhythm clips; the beatless ambient clip abstains. Promoted as an active tempoOctave:preferredBpm gate on 14 rhythm clips (13 pass; grid_7_8_140 stays informational — its bpm is meter-smeared, not octave-wrong: PR-G4 territory). Both sides landed: JSON_SCHEMA.md + full-only docstring, measurement.ts types, backendPhase1Client reconstructor, phase1FullPayload fixture; golden deliberately re-baselined (bpmOctaveEvidence key + one rounding-step lufsShortTermMax nudge within designed tolerance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
slittycode
force-pushed
the
accuracy/genre-tempo-octave-evidence
branch
from
July 13, 2026 05:59
2d33463 to
944a0d1
Compare
…o-fixture key set Two omissions from PR-G3 that CI caught on the rebased branch: - server_phase1._build_phase1 did not forward bpmOctaveEvidence to the frontend (test_phase1_golden.BuildPhase1KeySupersetTests caught it) - EXPECTED_TOP_LEVEL_KEYS in test_audio_fixture.py did not include it (AudioFixtureSmokeTest.test_output_contains_all_expected_keys caught it) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NCtxwnhn8nzHBjzeehPtf8
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
PR-G3 of the genre-generalization program (stacked on #211, which stacks on #210): a new full-mode-only Phase 1 field,
bpmOctaveEvidence, that surfaces how well simple-ratio alternatives (1:2, 2:3, 1:1, 3:2, 2:1, clamped to 40–220 BPM) explain the track's pulse. Surfacing-only: it never overridesbpm(PURPOSE.md invariant #1). Promotion of any correction into the shipped value stays behind the pre-registered GiantSteps tempo gate named inplans/genre-generalization-program.md.Design
Candidates are scored against two inter-onset-interval streams (IOIs are local, so near-miss candidates can't hide behind grid drift):
test_correct_halftime_shipping_is_not_contradictedlocks the case in.Abstains (
null) on sparse/beatless material — the ambient corpus clip confirms.Measured on the synthetic corpus
Promoted as an active
tempoOctave:preferredBpmcheck (tolerance 2.0) on all 14 rhythm-kind clips: 13 gate green;grid_7_8_140is registered informational — its 142.6 reading is a 7/8 meter smear, not an octave error, so no simple ratio lands on 140 (PR-G4's problem).Contract chain (tripwires #3/#4)
analyze.pyfull-only key →JSON_SCHEMA.mdroot inventory + full-only list + field row →test_analyze.pyfull-only docstring →src/types/measurement.ts(BpmOctaveEvidence) →backendPhase1Client.tsreconstructor →phase1FullPayload.tsfixture → golden re-baselined deliberately (UPDATE_PHASE1_GOLDEN=1; picks up the new key plus a one-rounding-steplufsShortTermMaxnudge that the golden's tolerance design explicitly absorbs).Validation
unittest discovergreen (includes 9 new scorer tests intest_bpm_octave_evidence.py).npm run verify: 51 smoke passed (parity + citation-contract tests re-armed by the new golden).Synthetic-corpus truth only; the real-audio octave-error rate lands when the GiantSteps tempo eval (staged locally) is re-run against this field — that run is the promotion gate's evidence, not this PR's.
Note: CI only arms on PRs based on
main— full local gates documented above; rebase after the stack below it merges.🤖 Generated with Claude Code