G3b packet 1 repair: three defects the white-box tests could not see

Base-free c3 ordering was unsound three ways: it never checked that the two
measures share a StaffInstance, never checked they are live, and ordered by
OperationId, whose Ord is (replica, counter) authoring identity rather than
canonical reduction order. It now reads the parent from measure_values,
requires both Live, and compares the minters' stamp reduction tuples -- the
same tuple chain_recency uses. The old test discarded the parent, so it
encoded the bug.

An empty effective grid refused instead of abstaining. Pin 6c case 1 makes a
missing active signature vacuous for agreement and abstaining for boundary
distance, but agreement grouped GoverningElement::None with a disagreeing
Unique, and the boundary clause collapsed None into Indeterminate and then
failed closed. None and Indeterminate are now separate outcomes. The
white-box selector test passed throughout, so the new coverage is end-to-end.

Simultaneous prospective whole-grid and per-key restorations had no tie-break:
Recency::Prospective is a unit variant, so both compared equal and the strict
> handed it to the whole grid -- while real restoration records the meter
change after the whole grid under the same undo operation. The per-key write
now governs on equal recency, scoped to the prospective family only; a plain
>= would delete the whole grid's entry when both sides are Base.

The aggregate test asserts the oracle against the materialized post-undo
graph, not against itself.

Executed against spec/CONTRACT_GENESIS_G3B_MEASURE.md pins 6, 6c, 7, 9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
This commit is contained in:
Levi Neuwirth 2026-07-30 13:12:04 -04:00
parent e64a4b7103
commit b92023a032
1 changed files with 569 additions and 39 deletions

View File

@ -4600,9 +4600,38 @@ impl<'a> Reducer<'a> {
}
None
} else {
// Repair (spec/CONTRACT_GENESIS_G3B_MEASURE.md pin 6 c3, review
// fix): c3 requires both measures in the SAME
// `StaffInstance.measures` — the graph-aware branch above
// enforces this implicitly (it only returns from within one
// instance's Vec), so the base-free branch must check it
// explicitly via `measure_values`'s carried parent id.
let (parent_a, _) = self.measure_values.get(&a)?;
let (parent_b, _) = self.measure_values.get(&b)?;
if parent_a != parent_b {
return None;
}
// Both measures must be LIVE — a retained-but-tombstoned entry
// (pin 10.4's retention discipline) must not contribute to the
// ordering.
if !matches!(
self.objects.get(&TypedObjectId::Measure(a)),
Some(ObjectState::Live)
) || !matches!(
self.objects.get(&TypedObjectId::Measure(b)),
Some(ObjectState::Live)
) {
return None;
}
// Order by the minters' canonical reduction-order stamp tuple —
// NOT by `OperationId`'s own `Ord`, which is `(replica,
// counter)` authoring identity (ids.rs:409), not reduction
// order. This is the same tuple `chain_recency` compares.
let oa = self.minted_by.get(&TypedObjectId::Measure(a))?;
let ob = self.minted_by.get(&TypedObjectId::Measure(b))?;
Some(oa.cmp(ob))
let ta = self.env_of(*oa)?.stamp.reduction_tuple();
let tb = self.env_of(*ob)?.stamp.reduction_tuple();
Some(ta.cmp(&tb))
}
}
@ -4844,7 +4873,23 @@ impl<'a> Reducer<'a> {
let per_key_is_prospective = meter_change_overrides.contains_key(&key);
let per_key_recency = self.chain_recency(per_key_chain, per_key_is_prospective);
if per_key_recency > whole_recency {
// Repair (spec/CONTRACT_GENESIS_G3B_MEASURE.md pin 6c, review
// fix): `Recency::Prospective` is a unit variant, so two
// simultaneous prospective restorations (a whole-grid
// restoration and a per-key restoration under the same undo)
// compare EQUAL, and a strict `>` would hand governance to the
// whole grid even though restoration actually applies the
// per-key write AFTER the whole grid, under the same undo
// operation id. Ratified tie-break: on equal recency, the
// per-key write governs — but ONLY for the prospective family.
// Do NOT widen this to a plain `>=`: when both sides are
// `Recency::Base` the per-key chain has no write, so `value`
// below resolves to `None` and the overlay would DELETE the
// whole grid's entry at this key — a regression.
if per_key_recency > whole_recency
|| (per_key_recency == Recency::Prospective
&& whole_recency == Recency::Prospective)
{
let value: Option<MeterChange> = if per_key_is_prospective {
meter_change_overrides.get(&key).cloned().flatten()
} else {
@ -5065,40 +5110,54 @@ impl<'a> Reducer<'a> {
if let Some(sig) = op.measure.time_signature {
match self.governing_time_signature(&sequence, &op.measure.start) {
GoverningElement::Unique(active) if active == sig => {}
GoverningElement::Unique(_) | GoverningElement::None => {
GoverningElement::Unique(_) => {
return OperationEffect::NoOp {
reason: NoOpReason::PreconditionFailedUnderReduction {
reason: PreconditionFailureReason::MeasureMeterMismatch,
},
};
}
// Pin 6c case 1 / pin 7: no active signature governs this
// start — agreement is vacuous, not a violation. Fall
// through (no refusal).
GoverningElement::None => {}
GoverningElement::Indeterminate => return unverifiable(),
}
}
// Pin 9 clause 3: boundary distance — vacuous for the first measure.
// `None` (no active signature governs the previous start) and
// `Indeterminate` (selection cannot be resolved) are NOT the same
// outcome: pin 6c case 1 makes `None` an abstention (allow the
// mint), while `Indeterminate` still fails closed with
// `MeasureOrderUnverifiable` (pin 7). Collapsing them was the bug.
if let Some(prev_start) = &predecessor {
let prev_duration: Option<MusicalDuration> =
match self.governing_time_signature(&sequence, prev_start) {
GoverningElement::Unique(sig) => self
match self.governing_time_signature(&sequence, prev_start) {
GoverningElement::Unique(sig) => {
let expected = self
.time_signature_values
.get(&sig)
.map(|ts| ts.measure_duration().clone()),
GoverningElement::None | GoverningElement::Indeterminate => None,
};
match (
self.anchor_musical_delta(prev_start, &op.measure.start),
prev_duration,
) {
(Some(delta), Some(expected)) if delta == expected => {}
(Some(_), Some(_)) => {
return OperationEffect::NoOp {
reason: NoOpReason::PreconditionFailedUnderReduction {
reason: PreconditionFailureReason::MeasureMeterMismatch,
},
};
.map(|ts| ts.measure_duration().clone());
match (
self.anchor_musical_delta(prev_start, &op.measure.start),
expected,
) {
(Some(delta), Some(expected)) if delta == expected => {}
(Some(_), Some(_)) => {
return OperationEffect::NoOp {
reason: NoOpReason::PreconditionFailedUnderReduction {
reason: PreconditionFailureReason::MeasureMeterMismatch,
},
};
}
_ => return unverifiable(),
}
}
_ => return unverifiable(),
// Pin 6c case 1 / pin 7: no active signature governs the
// previous measure's start — boundary consistency abstains.
// Not a violation; allow the mint.
GoverningElement::None => {}
GoverningElement::Indeterminate => return unverifiable(),
}
}
@ -17391,15 +17450,43 @@ mod tests {
/// packet 2, reuses these same functions).
#[test]
fn g3b_comparable_offsets_and_anchor_shapes() {
let op_set = OperationSet::new();
let mut r = Reducer::new(&op_set);
let ev_a = EventId::new(ReplicaId(1), 1);
let ev_b = EventId::new(ReplicaId(1), 2);
let m_a = MeasureId::new(ReplicaId(1), 10);
let m_b = MeasureId::new(ReplicaId(1), 11);
let rg_a = RegionId::new(ReplicaId(1), 20);
let rg_b = RegionId::new(ReplicaId(1), 21);
let instance = StaffInstanceId::new(ReplicaId(1), 99);
// Repair 1 (spec/CONTRACT_GENESIS_G3B_MEASURE.md, review fix): the
// base-free c3 branch now reads real minter envelopes' stamp
// reduction tuples, so the op set must actually carry them — a
// stamp order that AGREES with OperationId order here (replica 1,
// counters 1 then 2, physical 1_000 then 2_000), so this base case
// does not itself distinguish the two orderings (that is
// `g3b_c3_order_follows_stamp_not_operation_id`, below).
let measure_a = g3b_wallclock_measure(m_a, 1_000);
let measure_b = g3b_wallclock_measure(m_b, 2_000);
let mut op_set = OperationSet::new();
op_set.accept_all(vec![
g3b_measure_env(1, 1, 1_000, instance, measure_a.clone()),
g3b_measure_env(1, 2, 2_000, instance, measure_b.clone()),
]);
let mut r = Reducer::new(&op_set);
r.objects
.insert(TypedObjectId::Measure(m_a), ObjectState::Live);
r.objects
.insert(TypedObjectId::Measure(m_b), ObjectState::Live);
r.minted_by.insert(
TypedObjectId::Measure(m_a),
OperationId::new(ReplicaId(1), 1),
);
r.minted_by.insert(
TypedObjectId::Measure(m_b),
OperationId::new(ReplicaId(1), 2),
);
r.measure_values.insert(m_a, (instance, measure_a));
r.measure_values.insert(m_b, (instance, measure_b));
// c1: same Event id, comparable by offset.
let e1 = TimeAnchor::Event {
@ -17461,21 +17548,11 @@ mod tests {
assert_eq!(r.anchor_musical_delta(&ms1, &ms_end), None);
// c3: distinct Measure ids, both Start+Zero, in the same instance's
// (base-free) mint order — via `minted_by` OperationId comparison.
let instance = StaffInstanceId::new(ReplicaId(1), 99);
r.objects
.insert(TypedObjectId::Measure(m_a), ObjectState::Live);
r.objects
.insert(TypedObjectId::Measure(m_b), ObjectState::Live);
r.minted_by.insert(
TypedObjectId::Measure(m_a),
OperationId::new(ReplicaId(1), 1),
);
r.minted_by.insert(
TypedObjectId::Measure(m_b),
OperationId::new(ReplicaId(1), 2),
);
let _ = instance; // c3 does not need the parent directly here; minted_by suffices.
// (base-free) mint order — via the minters' stamp reduction-tuple
// comparison (Repair 1: NOT `OperationId`'s own `Ord`, and both
// measures must share the SAME `StaffInstanceId` in `measure_values`
// and be Live — see `g3b_c3_requires_same_staff_instance` and
// `g3b_c3_requires_live_measures` below for the negative cases).
let c3_a = TimeAnchor::Measure {
id: m_a,
position: MeasurePosition::Start,
@ -17586,6 +17663,185 @@ mod tests {
assert_eq!(r.anchor_musical_delta(&wc1, &wc2), None);
}
/// Repair 1 regression (spec/CONTRACT_GENESIS_G3B_MEASURE.md pin 6 c3):
/// two measures anchored via `Measure{pos: Start, off: Zero}` are NOT
/// c3-comparable when `measure_values` records them under DIFFERENT
/// `StaffInstanceId`s — the graph-aware branch enforces "same instance"
/// implicitly (it only returns from within one instance's `Vec`); the
/// base-free branch must check it explicitly.
///
/// **Mutation:** drop the `parent_a != parent_b` check from
/// `measure_vector_order`'s base-free branch. This row must observe red.
#[test]
fn g3b_c3_requires_same_staff_instance() {
let m_a = MeasureId::new(ReplicaId(1), 30);
let m_b = MeasureId::new(ReplicaId(1), 31);
let instance_a = StaffInstanceId::new(ReplicaId(1), 199);
let instance_b = StaffInstanceId::new(ReplicaId(1), 200);
let measure_a = g3b_wallclock_measure(m_a, 1_000);
let measure_b = g3b_wallclock_measure(m_b, 2_000);
let mut op_set = OperationSet::new();
op_set.accept_all(vec![
g3b_measure_env(1, 1, 1_000, instance_a, measure_a.clone()),
g3b_measure_env(1, 2, 2_000, instance_b, measure_b.clone()),
]);
let mut r = Reducer::new(&op_set);
r.objects
.insert(TypedObjectId::Measure(m_a), ObjectState::Live);
r.objects
.insert(TypedObjectId::Measure(m_b), ObjectState::Live);
r.minted_by.insert(
TypedObjectId::Measure(m_a),
OperationId::new(ReplicaId(1), 1),
);
r.minted_by.insert(
TypedObjectId::Measure(m_b),
OperationId::new(ReplicaId(1), 2),
);
// The load-bearing part of this fixture: the two measures are
// recorded under DIFFERENT parent instances.
r.measure_values.insert(m_a, (instance_a, measure_a));
r.measure_values.insert(m_b, (instance_b, measure_b));
let c3_a = TimeAnchor::Measure {
id: m_a,
position: MeasurePosition::Start,
offset: AnchorOffset::Zero,
};
let c3_b = TimeAnchor::Measure {
id: m_b,
position: MeasurePosition::Start,
offset: AnchorOffset::Zero,
};
assert_eq!(
r.anchors_comparable_order(&c3_a, &c3_b),
None,
"c3: measures anchored in DIFFERENT StaffInstances must not be comparable"
);
}
/// Repair 1 regression: a c3 comparison against a measure that is not
/// `Live` (e.g. retained-but-tombstoned, pin 10.4's retention
/// discipline) must not contribute to the ordering.
///
/// **Mutation:** drop the `Live` conjunct from `measure_vector_order`'s
/// base-free branch. This row must observe red.
#[test]
fn g3b_c3_requires_live_measures() {
let m_a = MeasureId::new(ReplicaId(1), 32);
let m_b = MeasureId::new(ReplicaId(1), 33);
let instance = StaffInstanceId::new(ReplicaId(1), 201);
let measure_a = g3b_wallclock_measure(m_a, 1_000);
let measure_b = g3b_wallclock_measure(m_b, 2_000);
let mut op_set = OperationSet::new();
op_set.accept_all(vec![
g3b_measure_env(1, 1, 1_000, instance, measure_a.clone()),
g3b_measure_env(1, 2, 2_000, instance, measure_b.clone()),
]);
let mut r = Reducer::new(&op_set);
r.objects
.insert(TypedObjectId::Measure(m_a), ObjectState::Live);
// b is tombstoned, not live, but its value is still retained (pin
// 10.4's uniform retention discipline).
r.objects.insert(
TypedObjectId::Measure(m_b),
ObjectState::Tombstoned {
deleted_by: OperationId::new(ReplicaId(9), 9),
minted_by: OperationId::new(ReplicaId(1), 2),
},
);
r.minted_by.insert(
TypedObjectId::Measure(m_a),
OperationId::new(ReplicaId(1), 1),
);
r.minted_by.insert(
TypedObjectId::Measure(m_b),
OperationId::new(ReplicaId(1), 2),
);
r.measure_values.insert(m_a, (instance, measure_a));
r.measure_values.insert(m_b, (instance, measure_b));
let c3_a = TimeAnchor::Measure {
id: m_a,
position: MeasurePosition::Start,
offset: AnchorOffset::Zero,
};
let c3_b = TimeAnchor::Measure {
id: m_b,
position: MeasurePosition::Start,
offset: AnchorOffset::Zero,
};
assert_eq!(
r.anchors_comparable_order(&c3_a, &c3_b),
None,
"c3: a Tombstoned measure must not contribute to the ordering"
);
}
/// Repair 1 regression: `measure_vector_order`'s base-free branch must
/// order by the minters' canonical reduction-order STAMP tuple, not by
/// `OperationId`'s own `(replica, counter)` `Ord` (ids.rs:409). Here `m_a`
/// was minted with the LARGER counter but the EARLIER physical stamp, and
/// `m_b` with the SMALLER counter but the LATER physical stamp — the two
/// orderings disagree, so this is the only fixture that distinguishes
/// them.
///
/// **Mutation:** revert the base-free branch to `oa.cmp(ob)` on the raw
/// `OperationId`s. This row must observe red (it would report
/// `Some(Ordering::Greater)`, the opposite of the correct answer).
#[test]
fn g3b_c3_order_follows_stamp_not_operation_id() {
let m_a = MeasureId::new(ReplicaId(1), 34);
let m_b = MeasureId::new(ReplicaId(1), 35);
let instance = StaffInstanceId::new(ReplicaId(1), 202);
let measure_a = g3b_wallclock_measure(m_a, 1_000);
let measure_b = g3b_wallclock_measure(m_b, 2_000);
let mut op_set = OperationSet::new();
op_set.accept_all(vec![
// m_a: OperationId counter 2, but the EARLIER physical stamp.
g3b_measure_env(1, 2, 100, instance, measure_a.clone()),
// m_b: OperationId counter 1, but the LATER physical stamp.
g3b_measure_env(1, 1, 200, instance, measure_b.clone()),
]);
let mut r = Reducer::new(&op_set);
r.objects
.insert(TypedObjectId::Measure(m_a), ObjectState::Live);
r.objects
.insert(TypedObjectId::Measure(m_b), ObjectState::Live);
r.minted_by.insert(
TypedObjectId::Measure(m_a),
OperationId::new(ReplicaId(1), 2),
);
r.minted_by.insert(
TypedObjectId::Measure(m_b),
OperationId::new(ReplicaId(1), 1),
);
r.measure_values.insert(m_a, (instance, measure_a));
r.measure_values.insert(m_b, (instance, measure_b));
let c3_a = TimeAnchor::Measure {
id: m_a,
position: MeasurePosition::Start,
offset: AnchorOffset::Zero,
};
let c3_b = TimeAnchor::Measure {
id: m_b,
position: MeasurePosition::Start,
offset: AnchorOffset::Zero,
};
// OperationId order says m_b (counter 1) < m_a (counter 2); stamp
// order says m_a (physical 100) < m_b (physical 200). The correct
// answer follows the stamp: c3_a (m_a) is Less than c3_b (m_b).
assert_eq!(
r.anchors_comparable_order(&c3_a, &c3_b),
Some(Ordering::Less),
"c3 ordering must follow the minters' stamp reduction tuple, not OperationId::cmp"
);
}
fn g3b_region_anchor(region: RegionId, n: i32) -> TimeAnchor {
TimeAnchor::Region {
id: region,
@ -17858,6 +18114,190 @@ mod tests {
);
}
/// Repair 3 (spec/CONTRACT_GENESIS_G3B_MEASURE.md pin 6c, review fix): a
/// SINGLE undo restoring both a whole-grid write and a per-key
/// meter-change write, at the SAME position, under the SAME undo
/// operation id — so both restorations tie under `Recency::Prospective`
/// when evaluated in flight (pin 9c's aggregate safety check, before the
/// undo commits). The ratified tie-break — per-key governs on a tie, but
/// ONLY within the prospective family — must make that PROSPECTIVE
/// prediction agree with what restoration ACTUALLY installs once it runs
/// for real: production code collects and applies every
/// `metric_grid_chain` restoration before it begins `meter_change_chain`
/// restorations (`reduce.rs` ~6419-6457), so the per-key predecessor is
/// written to the graph strictly AFTER the whole-grid predecessor, and
/// must be what's left standing.
#[test]
fn g3b_grid_oracle_aggregate_prospective_restoration_matches_real_undo() {
let region = RegionId::new(ReplicaId(1), 1);
let instance = StaffInstanceId::new(ReplicaId(1), 2);
let staff = StaffId::new(ReplicaId(1), 3);
let sig_whole_baseline = TimeSignatureId::new(ReplicaId(1), 10);
let sig_perkey_baseline = TimeSignatureId::new(ReplicaId(1), 11);
let sig_whole_new = TimeSignatureId::new(ReplicaId(1), 12);
let sig_perkey_new = TimeSignatureId::new(ReplicaId(1), 13);
let pos0 = g3b_region_anchor(region, 0);
let key = MusicalPosition(RationalTime::from_int(0));
let tx = TransactionId::new(ReplicaId(1), 900);
let create_region = prim_env(
1,
0,
0,
CausalContext::new(),
OperationKind::CreateRegion(CreateRegionOp {
region: crate::valuegen::region(region),
}),
);
let create_instance = prim_env(
1,
1,
1,
seen_r1(0),
OperationKind::CreateStaffInstance(CreateStaffInstanceOp {
region,
instance: crate::valuegen::staff_instance(instance, staff),
}),
);
// Pre-transaction baseline: a whole-grid write, then a per-key write
// that overlays it at pos0 — these establish the PREDECESSORS the
// undo below will restore to.
let set_grid_baseline = prim_env(
1,
2,
2,
seen_r1(1),
OperationKind::SetMetricGrid(SetMetricGridOp {
region,
grid: Some(MetricGrid {
meter_sequence: vec![MeterChange {
anchor: pos0.clone(),
time_signature: sig_whole_baseline,
}],
}),
}),
);
let set_time_sig_baseline = prim_env(
1,
3,
3,
seen_r1(2),
OperationKind::SetTimeSignature(SetTimeSignatureOp {
region,
anchor: pos0.clone(),
time_signature: Some(crate::valuegen::time_signature(sig_perkey_baseline, 3)),
}),
);
// The transaction: overwrites BOTH the whole grid and the per-key
// change, at the SAME position, in one undoable unit.
let decl = declare_transaction(1, 10, 10, seen_r1(3), tx);
let tx_set_grid = tx_member(
1,
11,
11,
seen_r1(10),
tx,
OperationKind::SetMetricGrid(SetMetricGridOp {
region,
grid: Some(MetricGrid {
meter_sequence: vec![MeterChange {
anchor: pos0.clone(),
time_signature: sig_whole_new,
}],
}),
}),
);
let tx_set_time_sig = tx_member(
1,
12,
12,
seen_r1(11),
tx,
OperationKind::SetTimeSignature(SetTimeSignatureOp {
region,
anchor: pos0.clone(),
time_signature: Some(crate::valuegen::time_signature(sig_perkey_new, 3)),
}),
);
let undo = undo_env(1, 20, 20, seen_r1(12), tx, UndoPolicy::StrictInverse);
let mut set = OperationSet::new();
set.accept_all(vec![
create_region,
create_instance,
set_grid_baseline,
set_time_sig_baseline,
decl,
tx_set_grid,
tx_set_time_sig,
undo,
]);
let identity = IdentityContext::new(ReplicaId(1));
let out = reduce_operation_set_onto(&set, &Score::empty(identity));
// What restoration ACTUALLY installs: read the materialized graph.
let region_value = out
.score
.canvas
.regions
.iter()
.find(|r| r.id == region)
.expect("region present");
let installed = region_value
.content
.staff_based()
.and_then(|c| c.default_metric_grid.as_ref())
.expect("a default metric grid after restoration")
.meter_sequence
.clone();
assert_eq!(
installed,
vec![MeterChange {
anchor: pos0.clone(),
time_signature: sig_perkey_baseline
}],
"actual restoration installs the whole-grid predecessor, then \
overlays the per-key predecessor at pos0 (applied strictly \
after) the per-key value must be what the graph shows"
);
// The oracle's PROSPECTIVE prediction, evaluated as pin 9c's
// aggregate safety check would (BEFORE the undo commits): both the
// whole-grid and per-key restorations are simultaneously "in
// flight" under the SAME undo, so both tie under
// `Recency::Prospective`.
let op_set = OperationSet::new();
let r = Reducer::new(&op_set);
let prospective_grid = Some(MetricGrid {
meter_sequence: vec![MeterChange {
anchor: pos0.clone(),
time_signature: sig_whole_baseline,
}],
});
let mut meter_change_overrides = BTreeMap::new();
meter_change_overrides.insert(
key,
Some(MeterChange {
anchor: pos0.clone(),
time_signature: sig_perkey_baseline,
}),
);
let predicted = r.effective_grid(
instance,
Some(region),
Some(&prospective_grid),
&meter_change_overrides,
);
assert_eq!(
predicted, installed,
"the aggregate prospective evaluation must agree with what \
restoration actually installs (pin 6c's ratified tie-break: on \
equal recency, the per-key write governs but ONLY for the \
prospective family)"
);
}
/// (M30b) The effective-grid oracle consults ONLY the ledgers
/// (`instance_grid`, `metric_grid_chain`, `meter_change_chain`) — never
/// `self.graph` — so graph-aware and base-free reduction run the
@ -18380,4 +18820,94 @@ mod tests {
immediate-violation gap"
);
}
/// Repair 2 (spec/CONTRACT_GENESIS_G3B_MEASURE.md pin 6c case 1 / pin 7):
/// creating a measure against a WHOLLY EMPTY effective grid — no
/// `local_metric_grid`, no region `default_metric_grid`, and no
/// `SetMetricGrid`/`SetTimeSignature` writes anywhere — must Applied. No
/// active signature means agreement is vacuous and the boundary clause
/// abstains; neither is a violation.
///
/// The original defect wrongly grouped `GoverningElement::None` with a
/// disagreeing `Unique` in the agreement match (`MeasureMeterMismatch`),
/// and collapsed `None` into `Indeterminate` for the boundary clause's
/// `prev_duration`, so the final `_ => unverifiable()` arm fired
/// (`MeasureOrderUnverifiable`) once a computable delta met a `None`
/// duration.
///
/// This is deliberately END-TO-END: the white-box selector test
/// (`g3b_grid_oracle_governing_selection`) already exercises
/// `governing_by_anchor`'s `None` outcome correctly in isolation — the
/// bug was entirely in how `create_measure`'s caller handled that
/// `None`, so only a real mint through the caller proves the fix.
#[test]
fn g3b_create_measure_applies_against_an_empty_effective_grid_end_to_end() {
let region = RegionId::new(ReplicaId(1), 60);
let instance = StaffInstanceId::new(ReplicaId(1), 61);
let staff = StaffId::new(ReplicaId(1), 62);
// No SetMetricGrid, no SetTimeSignature: the effective grid is
// empty for the whole life of this test.
let mut envs = g3b_region_and_instance_envs(1, region, instance, staff);
fn measure(id: u64, start: TimeAnchor, sig: Option<TimeSignatureId>) -> Measure {
Measure {
id: MeasureId::new(ReplicaId(1), id),
start,
time_signature: sig,
explicit_number: None,
number_visibility: epiphany_core::MeasureNumberVisibility::Auto,
}
}
// The first measure of the instance: clauses 1 & 3 are vacuous for
// it regardless of Repair 2 (pin 9's pickup exemption), so it does
// not by itself distinguish the bug from the fix.
let first = g3b_measure_env(
1,
70,
70,
instance,
measure(300, g3b_region_anchor(region, 0), None),
);
// The SECOND measure: comparable to, and a nonzero Musical delta
// from, the first (c4 — same Region id and edge), AND declaring a
// time signature — even though nothing ever wrote the grid. Under
// the original bug, the agreement clause alone already refused
// (`MeasureMeterMismatch`) before the boundary clause was reached;
// dropping `time_signature` to `None` would additionally exercise
// the boundary-only half of the same defect.
let second = g3b_measure_env(
1,
71,
71,
instance,
measure(
301,
TimeAnchor::Region {
id: region,
edge: RegionEdge::Start,
offset: AnchorOffset::Musical(MusicalDuration(RationalTime::from_int(4))),
},
Some(TimeSignatureId::new(ReplicaId(1), 999)),
),
);
envs.extend([first.clone(), second.clone()]);
let mut set = OperationSet::new();
set.accept_all(envs);
let state = set.reduce();
assert_eq!(
g3b_effect_of(&state, first.id),
Some(OperationEffect::Applied)
);
assert_eq!(
g3b_effect_of(&state, second.id),
Some(OperationEffect::Applied),
"an empty effective grid (no active signature anywhere) must be \
vacuous agreement and an abstaining boundary NOT \
MeasureMeterMismatch and NOT MeasureOrderUnverifiable \
(pin 6c case 1 / pin 7)"
);
}
}