You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Take the click's tempo band, default and metre presets from the core #2225
The click's band (40 to 208, default 96, scaled by beat unit), its pattern and metre presets, and the reflection stepper's clamp all live in Swift, while the core accepts any tempo from 1 to 500. A tempo recorded outside the click's band is silently clamped when the reflection sheet opens.
Why it matters
Found by the Swift shell rules review on #2223. The Android app (#2220) would otherwise reimplement this rule, and the two copies would drift. Severity for the port: High.
What to do
Put the band, default, presets and the groupings per beat count in domain/metre.rs, exposed through LimitsView; the click and the stepper read them. Click seeding (default 4/4, the item's own tempo, a metre change resetting beats, the last beat staying on) moves with them.
Done looks like
The click and the reflection stepper show the same band as before, read from the view model.
No tempo constant remains in TempoScale.swift or ClickPattern.swift.
Done looks like: the click and the reflection stepper offer the same tempo range, metres, groupings and patterns as today, read from the view model; TempoScale.swift and ClickPattern.swift are gone.
Time budget: one session, two PRs.
Decisions taken: the band, step, default, metre presets, groupings and offered patterns live in domain/metre.rs and reach the shell through LimitsView, one band per beat unit derived with Metre::displayed_bpm (rejected: the shell scaling by unit, which Android would repeat). The click's starting tempo, whether it is the item's own, and a starting metre defaulting to 4/4 join ActiveSessionView. The last-beat-stays-on tap guard stays in Swift: the core can only rule per tap through an event, which would move click state into the core, and validation already refuses an empty mask at hand-off. Pattern titles stay in Swift, keyed by the core's preset.
Files: metre.rs, practice_defaults.rs (mask reuse), model.rs, view/session.rs, both generated bindings; then ClickController, ClickSheet, ClickControl, TempoStepper, ReflectionSheet, PracticeDefaultsSection, their tests; specs/practice-instruments.md gains a section.
Tests: Rust table tests from musician inputs (7/8 at 168, 2/2 at 20, nine beats grouped); deleting the unit scaling breaks the 6/8 ceiling of 416.
Rabbit holes: the generated type name colliding with the Swift ClickPattern while both exist; the core type takes a different name.
Out of scope: drag feel, the click engine, the crash-recovery blob, Android screens.
Routing: epic #2220; bridge shape, so sensitive, core PR first then screens, this session.
Activity
jonyardley commentedon Oct 2, 2026
Claimed: branch
click-limits-2225. Epic: #2220.jonyardley commentedon Oct 2, 2026
Done looks like: the click and the reflection stepper offer the same tempo range, metres, groupings and patterns as today, read from the view model;
TempoScale.swiftandClickPattern.swiftare gone.Time budget: one session, two PRs.
Decisions taken: the band, step, default, metre presets, groupings and offered patterns live in
domain/metre.rsand reach the shell throughLimitsView, one band per beat unit derived withMetre::displayed_bpm(rejected: the shell scaling by unit, which Android would repeat). The click's starting tempo, whether it is the item's own, and a starting metre defaulting to 4/4 joinActiveSessionView. The last-beat-stays-on tap guard stays in Swift: the core can only rule per tap through an event, which would move click state into the core, and validation already refuses an empty mask at hand-off. Pattern titles stay in Swift, keyed by the core's preset.Files:
metre.rs,practice_defaults.rs(mask reuse),model.rs,view/session.rs, both generated bindings; thenClickController,ClickSheet,ClickControl,TempoStepper,ReflectionSheet,PracticeDefaultsSection, their tests;specs/practice-instruments.mdgains a section.Tests: Rust table tests from musician inputs (7/8 at 168, 2/2 at 20, nine beats grouped); deleting the unit scaling breaks the 6/8 ceiling of 416.
Rabbit holes: the generated type name colliding with the Swift
ClickPatternwhile both exist; the core type takes a different name.Out of scope: drag feel, the click engine, the crash-recovery blob, Android screens.
Routing: epic #2220; bridge shape, so sensitive, core PR first then screens, this session.
Give the snapshot test's limits the click fields and format the Kotli…
Send the click's tempo range, bars and patterns from the core (#2225) (…
jonyardley commentedon Oct 3, 2026
Reopened: #2293 closed this by accident (its body said "the follow-up that closes #2225"). The iOS screens half is still to do and its PR closes this.
jonyardley commentedon Oct 3, 2026
Closing again: the core half shipped in #2293, and the iOS screens half moved to #2302 so it can be claimed and closed by its own PR.