Filed from the clean side per PROVENANCE.md ("spec gaps go upstream"). This does not block any conformance-testable behavior — all peers share the gossiped timeline tuple, so cross-peer alignment holds either way — but it determines whether an implementation's application-facing beat is continuous across tempo changes.
The ambiguity
Chapter 02 §9 defines the beat value reported to the application as
b_app(t) = alignNear(B(t), B(t) − beatOrigin, q)
with beatOrigin taken from the §6 session timeline. Algebraically this means b_app = (B − beatOrigin) + alignNear(beatOrigin, 0, q), so the app beat's phase is phase(B − beatOrigin, q).
Chapter 02 §6 rule 3 says a peer modifying the timeline emits beatOrigin' = max(beats-at-now-on-old-timeline, old + 1 µbeat) — and sync-tempo-change.pcap confirms beatOrigins like 1000178 µbeats, i.e. not multiples of any plausible quantum.
Combining the two: when any peer changes the tempo, beatOrigin moves by a non-multiple of q, so phase(B − beatOrigin, q) — and therefore every session member's app-visible beat and phase — jumps by up to ±q/2 at the moment of adoption.
Worked example (quantum 4): timeline (500000 µs/beat, beatOrigin 0, timeOrigin 0), at ghost time 9.3 s → B = 18.6e6 µb, b_app = 18.6 beats. A tempo change to 100 bpm per rule 3 gives beatOrigin' = 18.6e6, timeOrigin' = 9.3e6; now b_app = alignNear(18.6e6, 0, 4e6) = 20.0e6 — the app beat jumps +1.4 beats and the phase snaps to 0, i.e. the change moment becomes a bar line for everyone.
This is hard to reconcile with §6 rule 4 ("Beat 0 is the phase reference … Timeline changes preserve this anchoring"), with chapter 03 §6.1's "different peers in one session have different timeline origins" (which suggests a client-side timeline distinct from the gossiped one), and with the publicly documented Link behavior that tempo changes do not move the bar grid.
Possible resolutions the spec could state
- §9's
beatOrigin refers to a client-side timeline whose origin is not replaced by gossip adoptions (only its tempo/time mapping is), so app phase is continuous; the §6 session timeline's beatOrigin is purely a priority stamp. The spec currently has no concept of a client timeline distinct from the session timeline.
- The app beat really does jump at timeline modifications, and §6 rule 4 / ch. 03 §6.1 need rewording.
- Something else (e.g. the modification rule constrains beatOrigin differently than §6 rule 3 states).
What observation would settle it
Run a reference peer that continuously prints its app beat/phase at quantum 4 (LinkHut's session display, or the candidate contract's status line), nudge the tempo by 1 bpm mid-bar, and watch whether the printed phase is continuous across the change or snaps by the predicted ±q/2. A second observation on a non-modifying member of the session distinguishes resolution 1 (continuous on both) from 2 (jump on both) and from a hybrid (continuous on the modifier only).
A capture is not needed — this is about endpoint-visible behavior, not wire bytes — but if the answer is resolution 1, chapter 02 needs a new section specifying how the client timeline evolves on adoption, join, and modification, since that's what chapter 03 §6's Δ is actually computed from.
Until resolved, link-wire-rs implements §9 literally against the session timeline (cross-peer alignment is unaffected; local beat continuity across tempo changes is not guaranteed).
Filed from the clean side per PROVENANCE.md ("spec gaps go upstream"). This does not block any conformance-testable behavior — all peers share the gossiped timeline tuple, so cross-peer alignment holds either way — but it determines whether an implementation's application-facing beat is continuous across tempo changes.
The ambiguity
Chapter 02 §9 defines the beat value reported to the application as
with
beatOrigintaken from the §6 session timeline. Algebraically this meansb_app = (B − beatOrigin) + alignNear(beatOrigin, 0, q), so the app beat's phase isphase(B − beatOrigin, q).Chapter 02 §6 rule 3 says a peer modifying the timeline emits
beatOrigin' = max(beats-at-now-on-old-timeline, old + 1 µbeat)— andsync-tempo-change.pcapconfirms beatOrigins like1000178µbeats, i.e. not multiples of any plausible quantum.Combining the two: when any peer changes the tempo,
beatOriginmoves by a non-multiple ofq, sophase(B − beatOrigin, q)— and therefore every session member's app-visible beat and phase — jumps by up to ±q/2 at the moment of adoption.Worked example (quantum 4): timeline
(500000 µs/beat, beatOrigin 0, timeOrigin 0), at ghost time 9.3 s →B = 18.6e6 µb,b_app = 18.6beats. A tempo change to 100 bpm per rule 3 givesbeatOrigin' = 18.6e6,timeOrigin' = 9.3e6; nowb_app = alignNear(18.6e6, 0, 4e6) = 20.0e6— the app beat jumps +1.4 beats and the phase snaps to 0, i.e. the change moment becomes a bar line for everyone.This is hard to reconcile with §6 rule 4 ("Beat 0 is the phase reference … Timeline changes preserve this anchoring"), with chapter 03 §6.1's "different peers in one session have different timeline origins" (which suggests a client-side timeline distinct from the gossiped one), and with the publicly documented Link behavior that tempo changes do not move the bar grid.
Possible resolutions the spec could state
beatOriginrefers to a client-side timeline whose origin is not replaced by gossip adoptions (only its tempo/time mapping is), so app phase is continuous; the §6 session timeline's beatOrigin is purely a priority stamp. The spec currently has no concept of a client timeline distinct from the session timeline.What observation would settle it
Run a reference peer that continuously prints its app beat/phase at quantum 4 (LinkHut's session display, or the candidate contract's
statusline), nudge the tempo by 1 bpm mid-bar, and watch whether the printed phase is continuous across the change or snaps by the predicted ±q/2. A second observation on a non-modifying member of the session distinguishes resolution 1 (continuous on both) from 2 (jump on both) and from a hybrid (continuous on the modifier only).A capture is not needed — this is about endpoint-visible behavior, not wire bytes — but if the answer is resolution 1, chapter 02 needs a new section specifying how the client timeline evolves on adoption, join, and modification, since that's what chapter 03 §6's
Δis actually computed from.Until resolved, link-wire-rs implements §9 literally against the session timeline (cross-peer alignment is unaffected; local beat continuity across tempo changes is not guaranteed).