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
Duration accepts zero and negative lengths, so Note, Rest, ChordEvent and Progression.Parse accept "C4:-1/4:v64", "R:0/4", "C@-1/4" #353
Duration.Create (Semantics.Music/Duration.cs:42-55) rejects only a zero denominator.
Duration.TryParse explicitly allows a leading sign (NumberStyles.AllowLeadingSign, :104-105).
The following all accept whatever Duration holds:
Note.Create (Note.cs:27)
Rest.Create (Rest.cs:19)
ChordEvent.Create (ChordEvent.cs:23)
their TryParse counterparts
the Chord@n/d cell in Progression.Parse (Progression.Parse.cs:149)
So musical events with zero or negative length get through at every entry point.
Reproduction (net10.0)
Call
Result
Progression.Parse("4/4 C@-1/4 G / / /")
parses; TotalDuration = 3/4 and TotalBars = 0.75 for a chart holding a full bar of G
Progression.Parse("4/4 C@0/1 G / / /")
parses with a zero-length C
Note.Parse("C4:-1/4:v64")
succeeds
Rest.Parse("R:0/4")
succeeds (R:0/1)
ChordEvent.Parse("Am:-3/4")
succeeds
Note.Seconds(tempo) and Rest.Seconds(tempo) (Note.cs:39-42, Rest.cs:29-32) then return negative or zero time.
Why it matters
Negative events silently subtract from Progression.TotalDuration and TotalBars, and from anything built on them, such as the bar logic in Form and Section. Related: #305 fixes the same hole, but only for Tempo's beat unit.
Suggested fix / acceptance criteria
Pick one of these:
Make Duration strictly positive: reject numerator <= 0 in Create, and return false from TryParse.
Keep Duration as a general signed rational, and enforce > 0 in Note.Create, Rest.Create, ChordEvent.Create and Progression.Parse.
All five inputs above are rejected: TryParse returns false and Create throws ArgumentOutOfRangeException.
The zero Duration that seeds the TotalDuration aggregate still works, for example as an internal zero.
Next step: Decide whether Duration itself is strictly positive or whether each event type enforces > 0. Then reject all five inputs listed in the issue.
Activity
matt-edmondson commentedon Sep 30, 2026
Triage
TotalDurationandTotalBars.Semantics.Music(Duration,Note,Rest,ChordEvent,Progression.Parse)Tempo's beat unit.Durationitself is strictly positive or whether each event type enforces> 0. Then reject all five inputs listed in the issue.Generated by Claude Code