The canonical op-block side of Region.permits_spanning_slurs: CreateRegion now
encodes at schema major 1, blocks carrying one are stamped major 1, the reader
admits them per-role, and a bundle whose op history is beyond this reader's
accept-set opens read-only. The migrate-on-read primitive is deferred (op
payloads are never reconstructed-to-values from bytes, so it has no consumer).
- ops: CreateRegionOp::encode_canonical embeds the region's full (v1) canonical
bytes; OperationKind/OperationPayload/OperationEnvelope::schema_major report
the payload's binary-format major (CreateRegion => 1, else 0). Removed D1's
transitional Region::canonical_bytes_v0 (dec_region_v0 stays for snapshots).
- bundle: max_supported_major(kind) raises the OperationEnvelopeBlock role to
[0,1] (every other role stays exact-0); the read gate is now
major > max_supported_major(r.kind). StagedChunk::operation_block_versioned +
SchemaVersion::for_major project a derived block major to a version.
- bundle: commit-time canonical-root validation checks structure without the
accept-set (a newer writer's higher-major root is publishable); the accept-set
is a read concern. Both open and commit consult
unsupported_operation_root_major and go read-only (+ the new
IntegrityAnomaly::UnsupportedCanonicalChunkMajor) when a canonical op root
exceeds the accept-set, so the live bundle refuses further commits at once.
- testkit: stage_operation_block derives a block's schema version from its
operations (max schema_major); staged_envelope_blocks routes through it so a
generated CreateRegion stream is never mis-stamped v0.
Tests: CreateRegion payload is v1 and carries the flag; the op reports major 1;
a derived CreateRegion block stamps V1 and reopens read-write; a major-2 block
opens read-only (open and post-commit); the per-role accept-set shape. Full gate
green (workspace tests, clippy -D warnings, fmt, rustdoc -D warnings).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
Stands up the schema-evolution machinery as a tested behavioral no-op, the
load-bearing one-way-door piece the later phases build on.
- SchemaVersion::V1 (bundle/ids.rs) -- infrastructure, an identity today.
- The core dispatch seam Score::decode_canonical_versioned(bytes, major)
(core/codec.rs), with the decode_v0_score / migrate_v0_score scaffold. It is
the identity at major 1's introduction (v0 layout == v1 layout), with
explicit "Phase C/D freeze this by value + default-fill the new field"
contracts baked into the doc comments so the freeze is a clean edit later.
Unit-tested by versioned_decode_is_identity_across_majors.
A first-pass review caught that the initial gate widening over-reached: it
admitted major 1 for every chunk kind, but the bundle's own op-block decoder
(block::decode_block) and manifest decoder are unversioned, so a spec-valid
major-1 op block would have passed the gate and then been mis-read rather than
migrated / opened read-only. The accept-set ran ahead of the decoders.
Corrected: the gates stay EXACT to major 0 in this phase -- the manifest gate
to Manifest::SCHEMA.major (the manifest never grows a v1 layout in this bump),
the generic-chunk gate to SUPPORTED_SCHEMA_MAJOR = 0. Admission of major 1 is
raised PER CHUNK ROLE by the phase that adds that role's versioned decode or
discard path (snapshot -> C, op block -> D, layout cache -> E), never as a
blanket accept-set ahead of a decoder that can read it. The roundtrip
seam-exercise was reverted too (it conflated the canonical-base MaterializedState
role with the acceleration-snapshot Score role); the acceleration-snapshot read
path + the first usable_* wrapper land in Phase C.
So Phase B is version infrastructure + the dispatch seam only; the gate
widening, usable_* wrappers, and ops symmetry move to the phases that exercise
them.
863 workspace tests pass; clippy -D warnings, fmt --check, rustdoc -D warnings
all clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
Two audit pushes whose code edits interleave line-by-line in the same
files (reduce.rs, bundle.rs, the DECISIONS logs), committed together so
the tree at every commit builds. Gate: 784 workspace tests pass, clippy
-D warnings clean, fmt clean.
Push 1 — the true MUST violations, all fixed:
- bundle: zstd read support on both read paths, output bounded by the
declared uncompressed_length, typed decompression errors, explicit
CompressedManifest rejection (zstd 0.13 workspace dep; write path
stays uncompressed per the Phase-3 deferral).
- ops: system-derived counter collision check — mint registry seeded
from the base graph, canonical-order pre-walk, halt via the new
PendingReason::HaltedBySystemCollision (discriminant 4, additive)
with transaction-atomicity and causal-dependent closure; neither
input set occupies a collided counter. canonical_pitch_bytes made
pub in core for the MUSCSPCH preimage.
- ops: Transpose skips tombstoned targets per the catalog; missing
targets still refuse the whole operation.
- ops: marker re-anchoring recorded as a RepairRecord in the
triggering operation's effect; ResolveConflict meta-conflicts name
both resolvers; base-free pitch-id freshness; reserved effect
vocabulary annotated.
- core: decomposition pre-pass honors authored attachments
(resolve_decomposition, spec-default precedence); inversion
tolerance typed as a TempoIntegration-class Tolerance.
- CONFORMANCE.md: the determinism conformance statement required by
Appendix D — all seven declarations.
Push 3 — wiring the types-only machinery:
- layout-ir/engrave: to_constrained emits real constraints (successive
notehead no-collision chains, per-glyph region containment, soft
user-break constraints); ConstraintStrength{Required, Preferred}
with strength-by-rule; Preferred violations surface as warnings, not
failures; StubSolver reworked honest-but-renderable. SVG goldens
byte-identical; snapshot constraint counts regenerated (0->90/15).
- layout-ir: to_logical projects user system/page breaks as anchored
EngravingOverrides with paired UserOverride-sourced decisions
(OverrideKind::SystemBreak/PageBreak carry TimeAnchor, ratified in
the spec alongside).
- layout-ir/ops/editor-core: edit-barrier bridge — decode mirrors for
the whole barrier tree (reject-never-normalize, NFC revalidation,
MAX_CONDITION_DEPTH = 64), golden-locked blob codec for the
ExtensionDeclaration fields, a barrier gate in apply and
apply_transaction backed by a Score oracle and real containment
contexts, and apply_unsafe recording the crossed extensions in
extensions_requiring_tombstone() for the next bundle write.
- ops: ResolveEquivocation meta-operation per the newly ratified
catalog entry — payload discriminant 3 (appended), set-level
earliest-resolve-governs promotion, ResolveConflict-mirrored
meta-conflicts, permutation-invariance fuzz; the missing golden
locks on the OperationKind/OperationPayload wire tables added.
- ops/editor-core: validation modes — ValidationMode + a non-canonical
advisory layer (validate.rs), an authoring gate before minting, and
reduction pinned as replay mode by construction (canonical bytes
untouched).
- bundle: the operation index (opindex.rs) — provisional golden-locked
payload, binary-search locate, staleness defined as full-ChunkRef
set equality against operation_roots, and the reject-and-rebuild
discipline (a defective index is never bundle corruption).
- ops: re-anchoring rule table completed — the four-key "nearest"
ordering computed from base-free ledger indices; markers re-anchor
to the nearest live event in the same staff instance (replacing the
Push-1 region-start stand-in); cue-source cascade; graphic-gesture
Events/Range/Free rows; comment and analytical-annotation orphaning.
Zero appended discriminants.
Spec enablers ratified with Push 3: catalog §ResolveEquivocation
(0.3.0 -> 0.4.0) and anchored break overrides; 16 new Pass-12 rows
filed (C1-C4, K5-K7, I4-I6, D1, E1-E5). The data-model payload
expansion (SlurKind, beam geometry, voltas, instrument bodies,
metadata) is deliberately staged to the Binary Format companion — the
positional graph codec has no value-level versioning, so filling those
structs is a schema-major break that should land once, with J.
Also carries the pre-existing editor-track increment: the atomic
tuplet overwrite (CascadeDeleteTuplets prunes decomposition
attachments naming the cascaded tuplet).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
E-D (layout-ir, honest solver tier): add SolverTier::Stub (a non-conformance rung
below Minimal) and have StubSolver report it instead of falsely claiming the
Minimal conformance tier; the passthrough evaluates no constraints and computes
no quality metrics.
E-C (layout-ir, constraint/reference validation): ConstrainedLayoutIR::validate()
now also checks the LayoutConstraint vector — NoCollision/Align/PositionWithin
must name glyphs in the set, SystemBreakAt/PageBreakAt must name existing slots,
PositionWithin regions must be finite/non-negative — rejecting dangling
references instead of silently accepting them.
E-B (layout-ir, content-sensitive ScoreVersion): derive ScoreVersion from the
whole score's canonical bytes (Agent B's whole-score codec) rather than the
layout projection's object identities, so a pure content edit that changes no
identifier still changes the version — required for correct incremental-layout
cache invalidation.
D-A (bundle, extension-root preservation): Bundle::commit now enforces
preservation — after the builder closure runs, every prior extension declaration
it did not re-declare (by extension_id) is carried forward verbatim, so an
extension-unaware writer cannot silently orphan an unknown extension's
preserved_chunk_roots. An extension-aware writer that re-declares its id keeps
control.
Each fix has a regression test; per-crate DECISIONS updated. (Item-6 remainder:
D-B operation-block summaries next; E-A real time-axis deferred per request.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>