The make-room enabler: a ModifyEvent that moves a metric event's span (different Musical position or duration) is now applied to the graph and the owning voice re-sorted by ascending position (id-tiebroken, the order an insert maintains), so invariant 3 (VoiceEventsSortedNonOverlap) holds. The deferral documented in DECISIONS.md is lifted. modify_event computes one sanction — the move is a valid metric move (PlacementVerdict::Moved) and the replacement is well-formed — and uses it to gate both the graph mutation (graph_replace_event) and the voice_occupancy update, so the canonical index and the graph never diverge. The verdict is read from voice_occupancy, the graph-independent placement index, so reduce() and reduce_onto() agree on it; a move with a non-positive span or one that would overlap a live sibling is refused as a clean NoOp(EventDurationInvalid) rather than skipped silently. A non-metric move stays deferred, and a malformed (empty) pitched replacement is neither materialized nor allowed to move occupancy. Tests: trim frees the voice slot (a later insert fits); a move onto a sibling is refused; a trim materializes in the graph (reduce_onto, invariants hold); a non-metric event is not rewritten onto the musical grid; a malformed move does not free the slot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAtEiJtt9yKVV1zjKYmZhS |
||
|---|---|---|
| .. | ||
| anomaly.rs | ||
| causal.rs | ||
| conflict.rs | ||
| decode.rs | ||
| effect.rs | ||
| encode.rs | ||
| envelope.rs | ||
| fuzz.rs | ||
| lib.rs | ||
| migrate.rs | ||
| opset.rs | ||
| payload.rs | ||
| reduce.rs | ||
| slot.rs | ||
| stamp.rs | ||
| support.rs | ||
| undo.rs | ||
| v0.rs | ||
| valuegen.rs | ||