Skip to content

fix(editor): an unsaved project can't inherit phantom barline locks - #294

Merged
byrongamatos merged 1 commit into
mainfrom
fix/editor-beat-lock-guard
Jul 16, 2026
Merged

fix(editor): an unsaved project can't inherit phantom barline locks#294
byrongamatos merged 1 commit into
mainfrom
fix/editor-beat-lock-guard

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Straggler from #291 (bulk barline locks) — a small beat-lock fix from #285 missed in the re-cut, surfaced when auditing feature parity.

_beatLockStorageKeyPure returned the shared key editorBeatLocks: for a project with no filename yet, so barline locks set while charting one new import leaked into the next new import (both keyed by ''). It now returns null for an empty filename; _saveBeatLocks / _restoreBeatLocks treat null as "nowhere to persist" and scrub any legacy blank-key residue, so a fresh project always starts with a clean lock set.

Stacked on #293 (the tip of the re-cut chain) for delivery; conceptually belongs to #291.

Tests: beat_lock.test.mjs updated — the storage-key case now asserts null for empty/nullish filenames (was the leaky bare prefix). 183 JS suites, lint 0 errors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q

Summary by CodeRabbit

  • Bug Fixes
    • Unsaved projects no longer inherit barline/beat-lock settings from other new projects.
    • Unsaved projects now start without any persisted beat locks.
    • Any legacy “blank” lock entries are cleared to prevent stale locks from resurfacing later.
  • Documentation
    • Updated the unreleased changelog with details of the fix.
  • Tests
    • Expanded beat-lock persistence test coverage to ensure unsaved projects don’t pick up leftover lock state.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be2a4eb3-2bb4-4206-94d7-84f3daa919cd

📥 Commits

Reviewing files that changed from the base of the PR and between b29ba0a and 559d766.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • src/tempo.js
  • tests/beat_lock.test.mjs

📝 Walkthrough

Walkthrough

Beat-lock persistence no longer uses a shared localStorage key for unsaved projects. Save and restore paths remove legacy blank-key residue, clear active locks, and tests cover loaded-project restoration and unsaved-project isolation.

Changes

Beat-lock persistence

Layer / File(s) Summary
Unsaved project storage lifecycle
src/tempo.js
Unsaved projects now produce no beat-lock storage key; save and restore paths remove legacy blank-key data and clear active locks.
Persistence behavior validation
tests/beat_lock.test.mjs, CHANGELOG.md
Tests cover localStorage interactions, null keys, loaded-project restoration, and cleanup of blank-key residue; the changelog documents the fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main fix: preventing unsaved projects from inheriting barline locks.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/editor-beat-lock-guard

Comment @coderabbitai help to get the list of available commands.

@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-stem-engine branch from 0993e40 to 8724902 Compare July 16, 2026 03:54
@ChrisBeWithYou
ChrisBeWithYou force-pushed the fix/editor-beat-lock-guard branch from 9c0a04f to f7f6f85 Compare July 16, 2026 03:54
ChrisBeWithYou pushed a commit that referenced this pull request Jul 16, 2026
…s, +6 dB

Replaces the docked mixer side-panel with #285's bottom mixer DRAWER: a
horizontal row of vertical channel strips (one per stem + transcription
part), each a live level meter beside a rotated fader, followed by the
SOURCE/GUIDE/CLICK utility buses and a dedicated MASTER output strip.
Rises on open, falls on close (reduced-motion aware); opens closed each
session (a per-screen view toggle, not a saved pref).

Metering: an AnalyserNode taps each bus + per-stem gain (a zero-gain sink
keeps the browser processing them without a second audible copy);
audioMixerMeterLevels reads RMS→−60..0 dBFS levels + peak dB each frame;
the panel's rAF tick fills the bars with instant-attack/gravity-decay
ballistics and a peak-hold clip readout. A master gain sits post-limiter
(limiter → masterGain → destination); the reference joins there too, so
the recording is metered and master-trimmed but STILL never through the
limiter (a hot recording is never colored). Per-stem taps drop on song
switch; bus taps persist.

Headroom: the fader range is 0..106 — 0..100 linear to unity, 100..106 a
log map to +6 dB. partStripState/clapState resolve vol through
_mixerGainForFaderPure, so stems and synth parts alike can be pushed to
+6 dB; every fader reads dB. The selected Tracks-column row lights its
matching strip. Removed the vestigial edit-blip toggle.

Faithful port of #285's mixer design onto the current main.

Tests: mixer_meters.test.mjs (level/peak/ballistics/dB pures); mixer_panel
reconciled to the +6 dB clamp, dB labels, master bus, animated close, and
always-closed init; audio_mixer/loop_ab sliced envs inject the meter-tap
stub and assert the new post-limiter ref routing. 184 JS suites, 291
pytest, lint 0 errors.

Stacked on fix/editor-beat-lock-guard (#294) → the meters/+6dB correction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-stem-engine branch from 8724902 to 31c7381 Compare July 16, 2026 03:57
@ChrisBeWithYou
ChrisBeWithYou force-pushed the fix/editor-beat-lock-guard branch from f7f6f85 to 2ca32ac Compare July 16, 2026 03:57
ChrisBeWithYou pushed a commit that referenced this pull request Jul 16, 2026
…s, +6 dB

Replaces the docked mixer side-panel with #285's bottom mixer DRAWER: a
horizontal row of vertical channel strips (one per stem + transcription
part), each a live level meter beside a rotated fader, followed by the
SOURCE/GUIDE/CLICK utility buses and a dedicated MASTER output strip.
Rises on open, falls on close (reduced-motion aware); opens closed each
session (a per-screen view toggle, not a saved pref).

Metering: an AnalyserNode taps each bus + per-stem gain (a zero-gain sink
keeps the browser processing them without a second audible copy);
audioMixerMeterLevels reads RMS→−60..0 dBFS levels + peak dB each frame;
the panel's rAF tick fills the bars with instant-attack/gravity-decay
ballistics and a peak-hold clip readout. A master gain sits post-limiter
(limiter → masterGain → destination); the reference joins there too, so
the recording is metered and master-trimmed but STILL never through the
limiter (a hot recording is never colored). Per-stem taps drop on song
switch; bus taps persist.

Headroom: the fader range is 0..106 — 0..100 linear to unity, 100..106 a
log map to +6 dB. partStripState/clapState resolve vol through
_mixerGainForFaderPure, so stems and synth parts alike can be pushed to
+6 dB; every fader reads dB. The selected Tracks-column row lights its
matching strip. Removed the vestigial edit-blip toggle.

Faithful port of #285's mixer design onto the current main.

Tests: mixer_meters.test.mjs (level/peak/ballistics/dB pures); mixer_panel
reconciled to the +6 dB clamp, dB labels, master bus, animated close, and
always-closed init; audio_mixer/loop_ab sliced envs inject the meter-tap
stub and assert the new post-limiter ref routing. 184 JS suites, 291
pytest, lint 0 errors.

Stacked on fix/editor-beat-lock-guard (#294) → the meters/+6dB correction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-stem-engine branch from 31c7381 to 2c59788 Compare July 16, 2026 04:11
@ChrisBeWithYou
ChrisBeWithYou force-pushed the fix/editor-beat-lock-guard branch from 2ca32ac to dc58021 Compare July 16, 2026 04:11
ChrisBeWithYou pushed a commit that referenced this pull request Jul 16, 2026
…s, +6 dB

Replaces the docked mixer side-panel with #285's bottom mixer DRAWER: a
horizontal row of vertical channel strips (one per stem + transcription
part), each a live level meter beside a rotated fader, followed by the
SOURCE/GUIDE/CLICK utility buses and a dedicated MASTER output strip.
Rises on open, falls on close (reduced-motion aware); opens closed each
session (a per-screen view toggle, not a saved pref).

Metering: an AnalyserNode taps each bus + per-stem gain (a zero-gain sink
keeps the browser processing them without a second audible copy);
audioMixerMeterLevels reads RMS→−60..0 dBFS levels + peak dB each frame;
the panel's rAF tick fills the bars with instant-attack/gravity-decay
ballistics and a peak-hold clip readout. A master gain sits post-limiter
(limiter → masterGain → destination); the reference joins there too, so
the recording is metered and master-trimmed but STILL never through the
limiter (a hot recording is never colored). Per-stem taps drop on song
switch; bus taps persist.

Headroom: the fader range is 0..106 — 0..100 linear to unity, 100..106 a
log map to +6 dB. partStripState/clapState resolve vol through
_mixerGainForFaderPure, so stems and synth parts alike can be pushed to
+6 dB; every fader reads dB. The selected Tracks-column row lights its
matching strip. Removed the vestigial edit-blip toggle.

Faithful port of #285's mixer design onto the current main.

Tests: mixer_meters.test.mjs (level/peak/ballistics/dB pures); mixer_panel
reconciled to the +6 dB clamp, dB labels, master bus, animated close, and
always-closed init; audio_mixer/loop_ab sliced envs inject the meter-tap
stub and assert the new post-limiter ref routing. 184 JS suites, 291
pytest, lint 0 errors.

Stacked on fix/editor-beat-lock-guard (#294) → the meters/+6dB correction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/beat_lock.test.mjs (1)

224-247: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the changed save path.

These tests exercise _restoreBeatLocks, but do not visibly drive _saveBeatLocks() through its new unsaved-project branch at src/tempo.js Lines [3188-3190]. Add a test asserting that saving an unsaved project removes legacy editorBeatLocks: residue and never writes a blank key; also verify named projects still save under their filename-specific key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/beat_lock.test.mjs` around lines 224 - 247, Add coverage that invokes
_saveBeatLocks for an unsaved project and verifies any legacy editorBeatLocks:
residue is removed without writing a blank-key entry. Also invoke the save path
with a non-empty S.filename and assert locks are persisted under the
filename-specific editorBeatLocks key, preserving existing named-project
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/beat_lock.test.mjs`:
- Around line 224-247: Add coverage that invokes _saveBeatLocks for an unsaved
project and verifies any legacy editorBeatLocks: residue is removed without
writing a blank-key entry. Also invoke the save path with a non-empty S.filename
and assert locks are persisted under the filename-specific editorBeatLocks key,
preserving existing named-project behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4abf4e09-cba7-4dc7-af31-32862f178e32

📥 Commits

Reviewing files that changed from the base of the PR and between 2c59788 and b29ba0a.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • src/tempo.js
  • tests/beat_lock.test.mjs

@byrongamatos
byrongamatos changed the base branch from feat/editor-stem-engine to main July 16, 2026 18:28
@byrongamatos
byrongamatos force-pushed the fix/editor-beat-lock-guard branch from b29ba0a to 559d766 Compare July 16, 2026 18:36
@byrongamatos
byrongamatos merged commit c38caa1 into main Jul 16, 2026
3 checks passed
@byrongamatos
byrongamatos deleted the fix/editor-beat-lock-guard branch July 16, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants