fix: Synth event recorder logs settings changes twice (near-duplicate events) - #6
Merged
Merged
Conversation
One logical parameter change flowed through two logging code paths when it touched a settings parameter: Synth.update() recorded the full update, then _rebuild_graph() recorded the settings subset again a few milliseconds later, producing near-duplicate events in the recording (issue #5). Dial-only changes were unaffected, which is why only waveform/attack changes showed duplicates. Collapse to a single logging chokepoint: update() records; _rebuild_graph() (whose only caller is update()) no longer records. The recorded-event schema (relative_time, updates_dict) is unchanged. Add headless regression tests (hum/tests/test_event_recording.py) that exercise the update -> rebuild path with a fake synth output and fake dials — no pyo server, and a minimal fake pyo module when pyo is not installed — and add hum/tests to pytest testpaths so these (and the existing import-safety tests) actually run in CI, which invokes pytest with no path arguments. Closes #5 Claude-Session: https://claude.ai/code/session_01GUjT9e7sAyckG1SR9czApr
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 #5
Root cause
One logical parameter change flowed through two code paths that each log:
Synth.update()records the full update dict (the intended chokepoint).update()calls_rebuild_graph(rebuild_updates), which recorded the settings subset again after rebuilding — a few milliseconds later (the rebuild time).That exactly matches the issue's log:
freq-only (dial) changes recorded once, while every change touchingwaveform/attack(settings) appeared twice, ~0.5–3 ms apart, the second event holding only the settings subset (identical when the whole update was settings)._rebuild_graphhas exactly one caller (update()), so its record call was pure double-logging — not two legitimate writes, hence no dedup/coalesce window.Fix
Remove the
_record_updatecall from_rebuild_graph;update()is the single logging chokepoint and its event already contains the rebuild updates. Recorded-event schema(relative_time, updates_dict)is unchanged — replay/render consumers are unaffected.Evidence (issue repro, run headless with
audio='dummy')Before: 9 events, 3 near-duplicate pairs (matching the issue's listing).
After: 6 events, 0 near-duplicate pairs — one event per logical change:
Tests
New
hum/tests/test_event_recording.py— 4 headless regression tests (fake synth output + fake dials, no pyo server; a minimal fakepyomodule is injected when pyo isn't installed, cleaned up so the import-safety tests still see the real environment):test_dial_only_change_records_exactly_one_event(guards against overcorrection)test_settings_only_change_records_exactly_one_eventtest_dial_plus_setting_change_records_exactly_one_event(the issue's signature case)test_sequence_of_changes_records_one_event_each(the issue's full sequence + schema/ordering checks)Mutation-tested: re-introducing the removed record call turns 3 of the 4 tests red in both a with-pyo and a no-pyo environment (the dial-only test correctly stays green — dial changes were never duplicated); restoring the fix turns them green.
Also added
hum/teststo pytesttestpaths— CI invokes pytest with no path arguments, sohum/tests/(including the existing import-safety tests) was not being collected in CI at all. Verified by simulating the exact CI pytest invocation (--doctest-modules+ ignore list) in a no-pyo environment: 9 passed, 2 skipped.Full local suite (
pytest tests/ hum/tests/ -v, pyo + portaudio available): 10 passed, including the end-to-endtest_synth_frequency_sequenceaudio test.https://claude.ai/code/session_01GUjT9e7sAyckG1SR9czApr