Commit Graph

17 Commits

Author SHA1 Message Date
Levi Neuwirth e64a4b7103 G3b packet 1: CreateMeasure reaches the wire, the mint, and the grid oracle
Kind and tag 39 at schema-minor epoch 12, in both unaligned discriminant
spaces. Measure is schema major 0 -- struct_codec! is a plain walk and
TimeAnchor's Codec has no version branching -- so schema_major() gains no arm
and OperationEnvelopeBlock stays at 3.

CreateMeasure is a nested-container mint following CreateStaffInstance, not
G3a's root-level shape: it carries the owning StaffInstanceId beside the
value, and measure_values carries that parent because Measure has no
back-pointer and the graph-removal arm will need it. Append-only, with
referential preconditions on the parent, the signature, and every non-wall-clock
start referent.

The comparable relation is five exact shapes with an identical boundary
selector; ordering across Start/End is unsound while measure length is
unresolved, so it stays unverifiable. Boundary distance needs a musical delta,
which only same-referent same-selector Musical offsets supply. The effective-grid
oracle reconstructs inheritance from metric_grid_chain and meter_change_chain by
write recency -- not by always overlaying per-key on whole-grid -- folds in
prospective overrides, and runs identically in both reduction modes, so an
instance_grid ledger keeps base-free reduction honest.

Three precondition reasons at 16-18. Repairs a live bug found in review: the
materialized-effect decoder stopped at 13, so reasons 14 and 15 already encoded
without decoding, and the generator drawing below(14) could not see them.

Executed against spec/CONTRACT_GENESIS_G3B_MEASURE.md, mutations M1-M33,
M64-M66, M67-M70. text_projection.tex moved into this packet: four tests read
it live, so the companion bump cannot be split from the grammar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-30 12:19:32 -04:00
Levi Neuwirth 6c5e69f19d Genesis G3a: the four root-level entity mints
CreateStaffGroup (35), CreatePartDefinition (36), CreateAnalysisLayer (37),
CreateView (38) -- all schema major 0, all schema-minor epoch 11, on
CreateStaff's set-union mint pattern with byte-identical re-carry
idempotence. Completes the four remaining root-level Score entity vectors.

No epiphany-bundle change of any kind: schema_major() gained NO arm (all
four fall through the catch-all _ => 0), so the op-block accept-set stays at
3 where G2b left it. The OperationKind golden lock stays at [(OperationKind,
u8); 30] -- kinds 35-38 remain outside it by design, per P13-S15's own rung.

Closes the satisfiability half of the from-empty defect: every G3 object
kind previously became Live only through base ingest, so CreateStaff's group
precondition was unsatisfiable from empty. It does NOT make a consistent
group authorable -- §1.1 disposition B rules Staff.group the sole authority
and StaffGroup.members a non-authoritative projection G3a stores without
maintaining, with both stale forms permitted. That ruling now reaches all
four normative sites: both field doc comments (neither field was documented
before), core_spec.tex, and both the new CreateStaffGroup and the existing
CreateStaff sections of operation_catalog.tex.

Carried-value maps for all four families thread through seven sites each,
including the base seed that fails silently -- a re-carry test reducing only
from empty cannot see a missing seed, which is why t5b reduces onto a
populated base.

Verified independently, not relayed. Full gate re-run: 1429 tests / 0
failed, clippy 0, fmt clean, git diff --check clean. Two mutations the agent
had not observed were run here and observed: reverting COMPANION_VERSION to
0.11.0 kills t11 (the agent had only reasoned this), and skipping the
staff_group_values base seed kills t5b. Both reversed by editing back, with
the baseline green.

Two findings recorded rather than silently absorbed:

- valuegen::analysis_layer's name format was accidentally exactly 16 bytes,
  the same width as AnalysisLayerId's encoding, which made a field-swap
  mutation on the two-field AnalysisLayer struct byte-invisible. Shortened
  and documented -- a fixture that makes a mutation undetectable is the same
  failure class as a guard that cannot fail.
- binary_format.tex has NO Revision History row for G2b. G2b's own contract
  required one (touch row 27: "version, Revision History row"); it changed
  the payload-layout row, tag row, accept-set text and §2373 history but
  never bumped the version or added the row. The document went 0.12.0 (G2a)
  -> 0.13.0 (G-minor) -> 0.14.0 (G3a) with the accept-set raise unrecorded.
  G3a adds its own row without retroactively backfilling G2b's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-29 14:07:35 -04:00
Levi Neuwirth 13c3d2fd46 Genesis G2b: SetTuningContext, and the accept-set raise it pays for
Kind and tag 34, schema major 3, minor epoch 10. The last rung before G3, and
the one that closes P13-S13: the tuning context becomes operation-authored, so
it finally has a canonical carrier. The closure argument is the metadata
precedent, not the canonical base - the base embeds no graph values for any
field, and metadata has been durable purely through its operations since M2d.

The payload carries epiphany_core::TuningContextSettings, a five-field subset
of ScoreTuningContext, not the full graph type. ScoreTuningContext's codec
deliberately drops accidental_extensions, so a full-value payload would have
diverged between a live session, where accept stores the envelope as a value,
and the same document reloaded, where the field decodes empty. canonical_value!
could not have caught that: it compares bytes and never the originating value,
so a field that never reached the bytes is structurally invisible to it. The
subset makes the divergence unrepresentable instead of relying on a
normalization step nothing can enforce, and it costs no wire design - the
encoding is byte-identical to the existing five-field walk, which
tuning_context_settings_canonical_bytes_match_score_tuning_context asserts
directly. Reduction leaves accidental_extensions untouched.

SetTuningContext is the sole genesis payload born at major 3, because minimal
stamping is a function of each payload's value, so the accept-set raise is
charged to this one surface: OperationEnvelopeBlock 2 to 3. The doc comment
above it did not merely record the cap, it asserted that no operation payload
embeds the tuning context - a sentence this rung falsifies - so it is rewritten
rather than left beside a corrected constant.

Undo restores the seeded base settings, default or not, and the
never-authored versus authored-to-default distinction stays unobservable. An
earlier draft of the contract had that backwards; PLAN_GENESIS_OPS section 5
trap 5 withdrew it, and SetMetadata is the disproof.

Fixes two undefined references the interrupted run had not yet reached:
operation_catalog.tex referenced sec:evolution:major3, a label defined in
binary_format.tex, which LaTeX cannot resolve across documents. Replaced with
the sectionsc convention already used for every other cross-companion citation
in that file.

Gate: 1409 tests, clippy 0, fmt clean, conformance 8/8 including [7f], both
vector corpora regenerated, all four PDFs at 0 undefined references. The t5 and
t7 mutations were re-run independently and observed to fail as specified; the
remaining eight are not signed off, because the implementing run was stopped
before it reported them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-29 09:30:32 -04:00
Levi Neuwirth 7df5ca17ce Genesis G2a: two settings setters, and the vocabulary's documentation catches up
SetCanvasLayoutDefaults and SetSpellingPrecedence, kinds and tags 32/33, on the
SetMetadata LWW pattern: advisory last-writer-wins, no conflict, no idempotence
short-circuit, seeded from the base so a value-restoring undo of the first
operational write reaches the pre-operational value. Both carried types already
had Codec impls, so the packet designs no wire layout -- canonical_value!
delegation and one push_lp_bytes each, inheriting strict canonical-form
rejection rather than writing it.

Both stamp schema major 0 unconditionally, staying in the catch-all arm. The
op-block accept-set is untouched at 2; epiphany-bundle has an empty diff. The
raise to 3 belongs to G2b, and the chunk schema minor -- a MUST no writer has
ever honoured -- belongs to G-minor, filed as P13-S14 and deliberately not
fixed here.

Most of this commit is documentation the vocabulary had outgrown. G1 shipped
five normative falsehoods because its contract declared the wire surfaces out
of scope, and Push 4a left TransposeInterval out of both of core_spec's
normative listings. So binary_format's payload-layout and tag tables now reach
33 rather than 30, its snapshot-only bullet records that it is empty and why,
two spelled-out payload counts move from eight to nine, and core_spec's
listings gain all four missing kinds. The Pass-12 K8 doctrine is narrowed
rather than reversed: the root and canvas remain structural givens no operation
mints, and only the claim that the score's contents arrive outside the
operation set is superseded.

Two generators were stale by two tranches and fed corpora other suites treat as
exhaustive. layout_stub's is now derived from PAYLOAD_FREE with an explicit
Registered append instead of a hand-written match, so future built-ins follow
structurally; s10 asserts PAYLOAD_FREE union {Registered} so deleting that
append cannot pass.

Companion 0.8.0 -> 0.9.0, binary format 0.11.0 -> 0.12.0, catalog 0.9.0 ->
0.10.0 (covering G1 retroactively). 102 decode vectors, regenerated.

Coordinator verification: 1371 passed / 0 failed, clippy 0 warnings, fmt clean,
conformance 8/8 and 9/9, requirement_labels 6/6, four PDFs at 0 undefined
references, goldens byte-identical, accept-set confirmed still 2. Mutations
s3/s5/s7/s10 re-run independently and each observed to kill its test.

Two findings beyond the contract. max_supported_major is not reachable outside
epiphany-bundle -- mod bundle is private and the symbol is absent from its
pub use -- so the gate's "assert it in code" was unsatisfiable without touching
a crate the contract forbids; verified by reading the unedited source instead.
And core_spec carries two P12-K8 references, not one; the contract named only
the first, so the second is annotated here for consistency.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-28 17:07:46 -04:00
Levi Neuwirth 3b09595196 Genesis G1: CreateInstrument, and the from-empty spine reaches a note
Score::empty plus operations alone now materializes a note-bearing Score. The
chain CreateInstrument -> CreateStaff -> CreateRegion -> CreateStaffInstance ->
CreateVoice -> InsertEvent needed exactly one new link: CreateStaff already
demanded a live Instrument and nothing could create one.

Instrument is a root with no outbound references, so the operation carries no
referential preconditions -- only mint and byte-identical re-carry, on the
CreateStaff template. It designs no wire layout: Instrument joins
canonical_value! and the payload is one push_lp_bytes over the existing Codec,
so strict canonical-form rejection is inherited rather than written. Kind 31 and
tag 31 agree; schema_major is unconditionally 2 (Instrument's major-2 appends
are mandatory, not Option-hidden); bundle.rs is untouched and the op-block
accept-set stays 2, since that raise belongs to G2.

Two cross-cutting items the ruling required. Reduction now writes identity for
the first time, deriving next_counter from the log rather than trusting the
seed -- and the implementation is broader than contracted, covering minted
entity ids as well as operation ids, which is right: both burn counters. And the
from-empty path is pinned to reduce_operation_set_onto, since the base-free mode
skips referential preconditions by design; a test documents that asymmetry as
designed rather than as a bug to fix.

The contract's parallel-safety claim was WRONG and this commit corrects it.
Extending OperationKind is not containable to core+ops: Rust exhaustiveness
forces an arm in editor-core's barriers.rs, and because testkit depends on
editor-core, that one missing arm blocked conformance and requirement_labels
too. Three more downstream sites had 31 or a kind-count baked in as a literal --
layout-ir's barrier decode test, testkit's grammar vocabulary count, and the
textproj corpus generator. The subagent found the first two, reverted its
out-of-bounds edit, and reported rather than working around; the user authorized
the boundary crossing. Each literal now carries a comment saying it must move
with every tag append.

The text projection needed a companion bump, which the contract never
anticipated. Adding create-instrument to the kind production while holding
0.7.0 would leave two incompatible grammars claiming one version -- precisely
what the single-version gate exists to prevent -- so COMPANION_VERSION is now
0.8.0, the first kind appended since the header was gated. Cached projections do
not migrate and are not expected to: a TextProjection chunk is a non-canonical
accelerator, so a stale one is regenerated. The negative "wrong version" vector
had to flip, since 0.8.0 was the version it used as its future-and-therefore-
rejected example; it now names 0.7.0, which tests the deferred migrate-on-read
posture better anyway. Test headers that were literals now assert against the
constant.

Gate, all observed: fmt clean; clippy --workspace --all-targets 0 warnings;
1359 passed / 0 failed; requirement_labels 6/6; conformance 8/8 and 9/9 with
golden-gate, 96 decode vectors and 13 textproj vectors, every verdict agreed.
max_supported_major(OperationEnvelopeBlock) verified still 2. Both PDFs rebuilt.
Mutations i1, i3 and i5 re-run independently rather than taken on report: the
spine collapses to TargetMissing without the instrument, an unseeded
instrument_values misreports a base re-carry as RecreateContentMismatch, and a
seed-returning cursor yields 0 where 12 is required.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-24 21:02:12 -04:00
Levi Neuwirth 9b5339f8f3 Schema major 2 Phase D: the repeat-authoring pair (code tranche)
CreateRepeatStructure / DeleteRepeatStructure land end-to-end,
review-hardened (five-dimension workflow review; 19 findings triaged —
12 verifier agents lost to a session limit were verified by hand).

epiphany-ops:
- CreateRepeatStructureOp { repeat } (lp(RepeatStructure), wire 28,
  BORN AT V2: schema_major always 2 — kind/voltas are unconditional
  fields) and DeleteRepeatStructureOp { repeat } (bare id, wire 29,
  major 0: minimal stamping over a major-0 layout; the discriminant a
  schema-minor append). OperationKindTag 28/29 name-verbatim with
  validating decode; golden tables grown to 30; migrate/v0 identity
  arms; lib re-exports.
- Reduction: set-union mint (live-id => AlreadyApplied without value
  comparison — the cross-cutting discipline; RecreateContentMismatch
  scope unchanged) with the ALL-anchor-sites-live precondition;
  delete-wins tombstone; the "Repeat structure / Anchor" rule-table
  row wired into BOTH the ledger (reanchor_for_tombstone arm, min-id
  survivor, cascade when dry) and the graph (every dead site rewrites
  to the same survivor), agreement asserted by tests incl. the
  DalSegno jump-target case.
- Undo: materialize_graph_tombstones gains RepeatStructure — and the
  pre-existing SPANNER GHOST fix (an undone spanner mint never left
  the graph), regression-locked.
- validate.rs: VoltaEndingsIllFormed advisory (non-empty, 1-based,
  strictly ascending; interactive-only).
- Canonical-base honesty: blake3 re-pinned for the corpus stream
  shift, its comment corrected (the seeded repeat creates all no-op),
  and the property covered surgically by
  the_canonical_base_embeds_no_repeat_values (two reductions
  differing only in repeat v2 content => byte-identical bases).

epiphany-core:
- RepeatStructure::anchor_sites()/anchor_sites_mut(): THE single
  site-set walk (start/end, jump targets, volta spans), consumed by
  reduction, the editor barrier seam, the invariant walk, and the
  cross-reference index — replacing five hand-rolled copies and
  fixing a SIXTH, silently-stale one (indexes.rs had never learned
  the Phase-B kind/volta anchors; regression-locked).
- RepeatStructure joins the CanonicalValue seam (strict per-value
  decode); decode-fuzz corpus gains valid_score_rich_with_repeats
  (DalSegno + voltas) CORPUS-LOCAL — shared render fixtures stay
  repeat-free until E1 (zero golden churn).

editor-core / gui / testkit / layout-ir:
- Barrier seam: repeat_event_refs + subjects_of arms +
  graph_structure_endpoints arm, gated end-to-end by
  repeat_authoring_kinds_derive_subjects_and_gate_on_barriers.
- Generators: operation_payload -> below(30); graph-session gen ->
  below(14); ops fuzz gen_payload -> below(27); layout_stub
  gen_operation_kind_tag unstaled 24..=29 (the Phase-3 tags were
  already missing); GUI name table.

Spec touch-ups (review findings): catalog says "strictly ascending";
the survivor tie-break is now explicit in the core rule-table row and
catalog (identifier-order minimum among multiple survivors;
proximity-aware four-key selection deferred, as for spanners).

Trail: ops + core DECISIONS entries; spec/PASS13_CANDIDATES.md opens
the Pass-13 ledger at three candidates (P13-K1; P13-D1 undo-driven
tombstones bypass ledger re-anchoring — pre-existing class; P13-D2
cue-cascade repair-trail ordering).

Full gate: fmt, clippy -D warnings, rustdoc -D warnings, 30 workspace
suites, conformance scale 1 (8/8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
2026-07-07 20:06:18 -04:00
Levi Neuwirth 794400c4c7 Schema major 2 Phase B: snapshot side + honest stamps (data-model fills)
The nine type bodies fill to the ratified Ch5 shapes and the wire moves
to the Binary Format §Schema Major 2 layouts, review-hardened (high-
effort review; 8 findings, 7 fixed + 1 deferral sharpened).

epiphany-core:
- 19 new leaf types in graph.rs (SlurKind/CurveDirection/
  CurvatureOverride/SpanStyle/LineStyle/SpaceUnit/SubBeam/
  BeamGeometryOverride/SpannerKind+5 payload types/RepeatKind/Volta/
  StaffBracketKind/MetadataEntry/MetadataValue/Timestamp/
  SoundConfiguration/TranspositionInterval/UnpitchedMember) with the
  ratified discriminants (cstyle_enum_codec! reused; tagged unions
  hand-written); nine struct fills appended in wire order.
- The frozen wire forms generalized to a SHARED sub-codec layer
  (enc_/dec_*_v1, v0==v1 for every type major 2 changed; vec framing
  through enc_/dec_vec_v1) used by the new decode_v1_score/
  encode_v1_score AND the rerouted v0 pair — major 2 touched types the
  v0 walk had treated as unchanged (metadata, staves, cross_cutting,
  staff instances transitively). Strict-canonical guards on every
  versioned path; decode_canonical_versioned dispatches {0,1,2} with
  composed default-fill migration.
- Invariants extended to the new reference-bearing fields (REVIEW
  FIX): Beam.sub_beams events, RepeatKind DaCapo/DalSegno anchors,
  Volta spans now covered by CrossCuttingRefsResolve + the anchor
  model walk, with negative tests.
- Tests: v1 migration size-anchor (v1 omits exactly the appended
  default bytes — the frozen encoder cannot drift), a non-default
  round-trip covering every new field and every SpannerKind/RepeatKind
  wire arm, fuzzer corpus gains genuine-v1 forms + the major-2 seam
  with enforced must-decode-Ok on unmutated frozen forms.

epiphany-ops + epiphany-bundle (Phase C's semantic core, landed here
deliberately — the live codec flip makes CrossCutting/Staff/Metadata
payload bytes v2 immediately; shimming nine transitively-embedded
types the major-1 D1 way was throwaway):
- Minimal-stamping OperationKind::schema_major per the ratified rule
  (CrossCutting/CreateStaff/SetMetadata always 2; CreateRegion 2 iff a
  carried instance bears Some(staff_lines_override) else 1;
  CreateStaffInstance/SetStaffLayout 2 iff Some else 0), unit-locked.
- Bundle op-block accept-set [0,2]; SchemaVersion::V2;
  beyond-accept-set tests moved to major 3; testkit V2
  stamp-derivation test.
- the_canonical_base_is_byte_identical_across_data_model_majors:
  pinned blake3 of a seeded reduction — the companion's SHOULD that
  the canonical base never moves across data-model majors.
- The op-payload migrate-on-read deferral restated precisely in
  DECISIONS (no consumer byte-reconstructs op payloads today; the
  first one must bring per-type frozen payload decoders).

Zero golden churn (fixtures deliberately carry v2 defaults).
Instrument::new consolidates the sweep's default fills. Full gate:
fmt, clippy -D warnings, rustdoc -D warnings, 30 workspace suites,
conformance scale 1 (8/8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
2026-07-07 16:30:07 -04:00
Levi Neuwirth 03758bbae0 Schema major 1 Phase D1: Instrument.range + Region flag (snapshot side)
The full-Score snapshot side of the three schema-major-1 data-model fields,
on a struct-level frozen v0 decoder. Snapshot-only: the CreateRegion op
payload stays byte-v0 (D2 owns the op-block migration + read-only).

Data model (epiphany-core):
- PitchRange { lowest, highest: Pitch } in pitch.rs; contains() is frame-guarded
  (decides only within a shared PitchSpaceId) and treats a reversed range as
  undecidable (None), not "everything out of range".
- Instrument.range: Option<PitchRange>; Region.permits_spanning_slurs: bool.

Codec:
- struct_codec! for PitchRange; Instrument -> {id,name,range}; Region appends
  permits_spanning_slurs.
- Replaced the Phase-C byte-splice with a struct-level decode_v0_score: a
  hand-written 19-field Score walk using dec_canvas_v0/dec_region_v0/
  dec_instruments_v0 for the two changed fields (nested in Vecs) and the current
  Codec for the other 17. Removed the now-unused Reader::pos().
- Region.canonical_bytes_v0() (+ enc_region_v0) is the frozen v0 op-payload
  surface: CreateRegionOp embeds it so the op-envelope block stays byte-v0.

Advisory preconditions (epiphany-ops/validate.rs):
- PitchOutsideInstrumentRange: pitch-in-range via voice->instance->staff->
  instrument (honoring instrument_override); "if any"/indeterminate-frame pass.
- Slur-spanning suppressed only when BOTH endpoint regions permit (AND
  semantics; documented pending spec ratification of which region governs).

Tests: the three frozen-decoder fixtures (non-default v1 round-trip; a mirror
v0 encoder synthesizing genuine v0 bytes that migrate default-filling all three
fields, anchored by an independent byte-length check; the nested-Vec multi-
region case); the byte-v0 CreateRegion payload; the frame-aware/reversed-range
contains(); and 5 advisory tests. ~29 construction sites updated. 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
2026-07-06 11:14:52 -04:00
Levi Neuwirth 0316160395 Phase 3 tranche 1: casting-off, K1 schema-fill, value-restoring undo
The chosen Phase-3 attack, run as two parallel waves. 829 workspace
tests pass; clippy -D warnings, fmt, and rustdoc clean; all three
spec documents build with zero undefined references.

Casting-off (epiphany-engrave/src/casting.rs, wired into the
Engraver):
- Greedy first-fit system breaking per region at measure-start
  barline columns; a measure that would overflow the content width
  starts a new system. Hard SystemBreakAt/PageBreakAt always
  honoured; soft breaks honoured unless pathological (skipped with
  the spec's warning + IrOverride-recorded decision).
- Vertical stacking from real content extents with the inter-system
  gap read from the vertical band model; page overflow starts the
  next page. World frame: pages stacked vertically, coordinates
  baked into glyphs/strokes, so the SVG renderer, hit-testing, and
  the GUI viewport are unchanged.
- Real ResolvedPage/ResolvedSystem trees (1-based page numbers,
  content bounding boxes, staves from staff-line segments, measures
  from barline columns); every chosen break appends an
  EngravingDecision with MUSCLOID EngravedBreak provenance,
  UserOverride-attributed via the new ConstrainedLayoutIR
  break_origins sidecar; staff lines split per system with
  synthesized continuation provenance.
- Break-constraint evaluation flips: satisfied iff the layout breaks
  at the slot. The two single-system tests invert deliberately
  (a hard break is now honoured; a user break is honoured and
  attributed instead of warned). Geometric constraints evaluate in
  the pre-casting spaced frame (documented).
- Page geometry is engraver-side PageGeometry (A4 portrait at an
  8 mm staff: page 105 x 148.5 staff spaces, margins 7.5, content
  90 x 133.5; arithmetic documented) — Canvas.layout_defaults has no
  graph home and is a schema-major addition (P12-I7).
  ENGRAVER_VERSION = 2. Goldens regenerated: ten_measure_single_staff
  engraves as 2 systems (viewBox 84x20.6, was 103x11);
  valid_score_rich as 3 systems; stub goldens byte-identical.

K1 schema-fill (Operation Catalog 0.4.0 -> 0.5.0, ratified first;
wire discriminants strictly appended):
- CreateStaff (24 / tag InsertStaff 24): set-union mint of a global
  Staff; CreateStaffInstance now preconditions that its referenced
  staff is live.
- SetTimeSignature (25): value-carrying meter-change LWW keyed by
  (region, resolved position); the carried TimeSignature mints
  set-union; StructuralFieldCollision on meter_sequence.
- SetTempoSegment (26): LWW keyed by (scope, resolved start) over
  the score or region tempo map; a write that would malform the map
  refuses with the appended PreconditionFailureReason 11
  (TempoMapMalformed).
- SetStaffLayout (27): LWW advisory over the staff instance's three
  inline layout fields.
- Create score/canvas remain deliberately unavailable slots: the
  root and canvas are inline singletons with no addressable object
  model (P12-K8), not force-designed.

Value-restoring undo (P11-C8 narrowed; catalog §UndoTransaction
rewritten and per-primitive undo notes updated):
- Canonical-order write chains (base-seeded) across all eleven LWW
  families. StrictInverse restores each written key to its
  chain-predecessor value iff the transaction's write is still the
  key's last writer, else refuses the whole undo with a
  TransactionConflict naming the superseder; BestEffort restores the
  still-last keys. Clean compensations are Applied; only minted-
  object tombstone repairs ride AppliedWithRepair (no new repair
  vocabulary). Mixed mint+overwrite transactions compose; strand
  guards refuse tombstoning mints still referenced by live
  non-members.
- Undo-of-undo pinned and tested: restorations are chain writes, so
  undoing the undo's transaction restores the undone value, and a
  second undo of the same transaction conflicts (absence-restores
  repeat idempotently — documented asymmetry, P12-K11).
- Permutation invariance pinned across five delivery orders; the
  convergence generators gain the new ops and a tx-then-undo flow.
- Still deferred in normative text: delete resurrection (needs a
  system-derived tag outside the ratified closed set), Transpose
  inversion (P12-K2), Cascade dependent closure.

Trackers: Binary Format companion 0.1.0 -> 0.2.0 (appended wire/tag
tables, PreconditionFailureReason 11, payload layouts, history row —
a schema-minor evolution under its own rules); nine new Pass-12 rows
(C5, K8-K11, I7-I10); core-spec OperationKind listing gains the four
kinds; revision-history rows in core spec and companion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
2026-07-02 21:55:26 -04:00
Levi Neuwirth 3e91a8302a Push 4: Binary Format companion, F1 benches, subquadratic reduction order
The audit's fourth push: the biggest outstanding Phase-2 item plus the
performance gate. 793 workspace tests pass; clippy -D warnings, fmt,
and rustdoc (deny-warnings) clean; all three spec documents build with
zero undefined references.

Binary Format companion (spec/binary_format.tex, v0.1.0 — Agent J's
deliverable, 43 pages):
- Twelve chapters transcribed from the golden-locked implementation:
  encoding conventions (the three prefix/endianness regimes, a
  normative no-varint rule, reject-never-normalize decode discipline),
  identifiers imported from the core spec's Canonical Byte-Layout
  Reference, primitive value encodings, the whole-Score positional
  codec ratified as the schema-major-0 wire form, operation wire
  forms (envelope field order with the normative id-leads property,
  the OperationPayload 0..=3 and OperationKind 0..=23 tables,
  effects/conflict/anomaly/MaterializedState vocabulary), the bundle
  physical layout (64-byte header, 256-byte superblock, chunk
  preimages and framing, ChunkRef, manifest body order), the
  operation-index payload, and the extension-blob/edit-barrier byte
  forms.
- Ratifies P12-D1 (req:binfmt:opindex), P12-E1 (req:binfmt:ext-blobs),
  P12-E2 (req:binfmt:condition-depth, MAX_CONDITION_DEPTH = 64
  normative), and P12-E3 (req:binfmt:object-kind-open) — batch rows
  struck through; discharges the provisional-codec notes in core
  (P11-4), ops, and bundle (P11-D2/D4/D5) DECISIONS with ratification
  cross-references.
- Pins the frozen-layout schema-evolution keystone: within schema
  major 0 every positional struct layout is frozen; a field-set change
  is a schema-major change with migration — formally grounding the
  data-model-expansion staging decision. Open questions kept honest
  in-document: SnapshotId derivation, index-refresh threshold, u64/u32
  prefix unification at the next major.
- Not yet delivered from J's charter: the cross-implementation decoder
  test and the wire-format fuzzer (follow-up harnesses).

F1 benches (crates/epiphany-testkit/benches/, per the F0 decision):
- criterion 0.5.1 (workspace dev-dependency; MSRV 1.77 respected with
  documented transitive pins: clap 4.5.53, half 2.4.1).
- reduction bench at 1K/10K/50K envelopes with the Chapter-10 budget
  (>10,000 envelopes/second cold) written in the bench as a Pass/Xfail
  gate; bundle benches for the typical-edit commit (<=50 ms; measured
  ~14.7 ms on real disk after catching that tmpfs neuters fsync) and
  the open/bootstrap read (<=200 ms; measured ~60 us).
- CI: quick budget gates in the conformance job, full gates nightly.

Subquadratic canonical_reduction_order (the F-surfaces/K-fixes
handshake, closing K's 10K-envelope acceptance gate):
- The bench documented the failure (50K at ~1.7K env/s, a 29 s cold
  reduction; two O(n^2) loops); the fix replaces pair enumeration with
  threshold/frontier readiness per replica plus explicit-dot dependent
  lists and a stamp-tuple binary heap — O((n + sum(context)) log n),
  never materializing covered pairs.
- Byte-identical order: same edge relation, same ready predicate, same
  total order; the old implementation is retained as a test-only
  oracle with element-for-element order-equality property tests over
  fuzz sets, adversarial sets, and directed shapes (2,000-envelope
  full-coverage chains, dot cycles, duplicate-id stamp ties),
  mutation-tested for sensitivity.
- Measured: 1K 155K->674K env/s, 10K 12.5K->257K, 50K 1.7K->87K; all
  three scale points now pass and the 50K row is promoted from Xfail.

Also: fixed nine rustdoc private/unresolved intra-doc links that had
accumulated across the pushes (the CI deny-doc-warnings job would have
failed on them).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
2026-07-02 19:02:07 -04:00
Levi Neuwirth d93baac3ba Agent K M2d review follow-up: harden the Group-4 score-settings ops
Address the five-finding review of M2d (1e4ab24) plus the two-finding
follow-up review; all fixes are code/test/doc only, no spec change needed
(the catalog/core-spec already classify metadata as advisory LWW).

- SetMetadata is now a true advisory LWW: it silently last-writer-wins in
  canonical order and records no conflict, matching operation_catalog
  §set-user-system-break. Dropped the StructuralFieldCollision, the
  `last_metadata` working slot, and the `env` parameter; rewrote the
  conflict test as `concurrent_differing_set_metadata_is_advisory_lww`
  (no conflict, state stays clean, permutation-independent resolution).
- SetMetricGrid / SetUserPageBreak / SetUserSystemBreak share a new
  `layout_region_slot` precondition backed by a `staff_based_regions`
  index: the target must be live and staff-based (FreeGraphic regions have
  neither a metric-grid nor a break slot). The index is read from base-free
  state, so reduce() and reduce_onto() reach the same verdict for missing,
  tombstoned, and FreeGraphic regions.
- SetMetricGrid now rejects a grid whose meter_sequence names an undeclared
  time signature, rather than installing an invariant-violating grid.
- User breaks materialize under the canonical LWW key: `apply_break_lww`
  drops any existing anchor resolving to the same position before adding,
  so the graph break list stays in lockstep with the resolved-position
  ledger map (shared `resolved_anchor_position`). Applied to page and
  system breaks alike.
- Coverage: SetMetadata/SetMetricGrid/CreateVoice/DeleteVoice added to the
  tag-distinctness test; layout_stub `gen_operation_kind_tag` extended to
  every normative tag; the MaterializedState decode test populates
  page_breaks; four direct regression tests pin each fixed bug; the stale
  SetMetadata/score_metadata doc comments now say advisory LWW.

Gates: build/fmt/clippy -D warnings clean; cargo test --workspace green
(533); conformance_suite scale 1 passes. Stages only core/ops/testkit; the
unrelated Agent-I working tree is left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 21:45:51 -04:00
Levi Neuwirth 1e4ab24779 Agent K M2d (Group 4): score-settings operations
Fourth broad-K0 subsystem group — three value-typed LWW field-overwrite ops, one
per settings cluster (additive: OperationKind variants 21-23; framework frozen):

- SetMetadata { metadata: ScoreMetadata } — overwrite the score-singleton
  metadata (title/composer/copyright); concurrent differing ⇒
  StructuralFieldCollision.
- SetMetricGrid { region, grid: Option<MetricGrid> } — overwrite a region's
  default metric grid (LWW keyed by region; concurrent differing ⇒ collision).
- SetUserPageBreak { region, anchor, present } — the page-break sibling of
  SetUserSystemBreak: a canonical LWW advisory.

- core: expose ScoreMetadata + MetricGrid via CanonicalValue (no new byte layout
  — they already have whole-score Codec impls).
- SetMetadata / SetMetricGrid mirror the modify ops: the resolved value lives in
  the graph (reduce_onto), with new last_metadata / last_metric_grid LWW working
  state for concurrent-differing detection; MaterializedState records only the
  effect and conflict.
- SetUserPageBreak mirrors SetUserSystemBreak's canonical advisory: a new
  MaterializedState.page_breaks map (encode + decode added, parallel to breaks),
  plus the graph's region user_page_breaks.

Migration: v1-native (no lossy v0 predecessor) -> project/migrate by identity;
the round-trip identity test now covers all four M2 groups.

Coverage:
- testkit operation_payload + ops fuzz gen_payload emit the three kinds, so the
  convergence / determinism / migration-equivalence and MaterializedState
  decode-roundtrip gates exercise them (incl. page_breaks) at scale.
- reduce_onto graph tests: the three settings materialize in graph and ledger
  (metadata title, region default metric grid, region user page break +
  MaterializedState.page_breaks), invariant-clean; plus a concurrent-differing
  SetMetadata conflict test.

Gates: build/fmt/clippy -D warnings clean; cargo test --workspace green (519);
conformance_suite scale 1 passes. Catalog sections + DECISIONS for the M2 groups
land in M2e per the staged plan. The unrelated Agent-I working tree is left
uncommitted; this commit stages only core/ops/testkit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 19:26:31 -04:00
Levi Neuwirth a207077cd7 Agent K M2c (Group 3): structural container CRUD operations
Third broad-K0 subsystem group — six new value-typed ops for the score-graph
containers, reusing M1's disciplines (additive: OperationKind variants 15-20,
new apply arms + reduction methods; framework frozen). Per the project lead's
call, container deletes are EMPTY-ONLY (no cascade): a precondition NoOp unless
the container has no live children, so the caller deletes contents first.

- CreateRegion / DeleteRegion, CreateStaffInstance / DeleteStaffInstance,
  CreateVoice / DeleteVoice. Creates are value-typed mints of an empty container
  (set-union creation); deletes are delete-wins tombstones gated on emptiness.
- core: expose Region / StaffInstance / Voice via CanonicalValue (no new byte
  layout — they already have whole-score Codec impls), with round-trip coverage.
- New PreconditionFailureReason::ContainerNotEmpty (additive discriminant 10;
  encode + decode), reported when an empty-only delete hits a non-empty container.

Reduction (reduce.rs):
- Two child-existence indices, region_instances and instance_voices, drive the
  emptiness checks (a voice's events are read from voice_occupancy), so the
  ledger projection and the graph agree on every delete result. Populated by
  seed_from_graph, the create ops, and implicit voice creation in insert_event.
- CreateStaffInstance / DeleteStaffInstance maintain the region's staff_extent so
  it lists exactly the manifested staves (Chapter 5 RegionExtents); valuegen's
  fresh region uses a far-future wall-clock extent so it never overlaps an
  existing region in both time and staff.

Migration: v1-native (no lossy v0 predecessor) -> project/migrate by identity;
group1_and_group2_kinds_round_trip_by_identity extended to cover Group 3.

Coverage:
- testkit operation_payload + ops fuzz gen_payload emit the six kinds, so the
  convergence / determinism / migration-equivalence gates exercise the
  bookkeeping projection at scale.
- A reduce_onto graph test materializes a region -> staff instance -> voice
  subtree (invariant-clean), asserts the empty-only delete refuses a non-empty
  container with ContainerNotEmpty, and verifies an ordered teardown clears the
  subtree from both graph and ledger.

Gates: build/fmt/clippy -D warnings clean; cargo test --workspace green (criterion
1 green with the new container objects in the corpus); conformance_suite scale 1
passes. Catalog sections + DECISIONS for these ops land in M2e per the staged
plan. The unrelated Agent-I working tree is left uncommitted; this commit stages
only core/ops/testkit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 18:10:15 -04:00
Levi Neuwirth c47f4b5cec Agent K M2a review follow-up: graph-materialization fixes for the leaf-field ops
From the M2a review (no bug in the bookkeeping reduction; the gap was that the
Group-1 ops' *graph* materialization — reduce_onto — was unexercised by the
gates, which hid two invalid-graph edges). Fixes are graph-materialization only;
the bookkeeping projection, and therefore convergence/determinism, is unchanged.

- DeleteIdentifiedPitch of a single-pitch note's last pitch left an empty
  (Chapter-5-invalid) PitchedEvent via EventArena::get_mut (which bypasses
  insert's well-formedness guard). It now degrades the note to a Rest of the
  same id/voice/position/duration; InsertIdentifiedPitch into a rest is the dual
  (rest -> one-pitch note), keeping the graph consistent with the bookkeeping
  that mints/tombstones the pitch object either way.
- ModifyEvent now skips placement-changing (move) and malformed-empty pitched
  replacements in the graph rather than corrupting invariant 3
  (VoiceEventsSortedNonOverlap) via get_mut; voice re-sort stays deferred and the
  LWW bookkeeping still records the modify.

Coverage: graph_edit_session (criterion 1, reduce_onto + check_invariants) now
emits all five Group-1 kinds, so the real-Score gate exercises their graph
mutations at scale; plus two targeted reduce_onto regression tests (note->rest,
rest->note) in tests/graph_reduction.rs.

Docs: DECISIONS.md records the note<->rest equivalence and the ModifyEvent
placement deferral (catalog section prose routed to M2e); Transpose / P12-K2 note
the i8-saturation caveat; valuegen::pitch_value_nth no longer implies spelling()
is injective.

Gates: build/fmt/clippy -D warnings clean; cargo test --workspace green (ops
graph_reduction 13, ops lib 51); conformance_suite scale 1 passes. The unrelated
Agent-I working tree is left untouched; this commit stages only ops/testkit/spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:25:48 -04:00
Levi Neuwirth 1658fd18f3 Agent K M2a (Group 1): event & pitch leaf-field operations
First broad-K0 subsystem group — five new value-typed ops reusing M1's proven
disciplines (additive: new OperationKind variants 8–12, new apply arms +
reduction methods; framework frozen):

- ModifyEvent { event: Event } — field-overwrite LWW by EventId; concurrent
  differing ⇒ StructuralFieldCollision.
- Transpose { targets, chromatic_steps } — order-dependent; pitch ids preserved;
  canonical footprint = effect-log entry; reduce_onto applies a minimal CMN
  alteration shift (rich interval algebra deferred — P12-K2).
- InsertIdentifiedPitch / DeleteIdentifiedPitch — pitch-within-event mint /
  delete-wins tombstone.
- ModifyIdentifiedPitch { pitch, value: Pitch } — field-overwrite LWW (the pitch
  VALUE, distinct from RespellPitch's spelling-only overwrite).

Design (honesty rule): the modify/transpose ops record effect + conflict
canonically — the resolved values live in the graph (reduce_onto), since
MaterializedState is bookkeeping, not a second graph; respell stays special
because spelling is a bookkeeping-owned annotation. LWW diff uses new
`last_event_modify`/`last_pitch_modify` working maps (synced through
WorkingSnapshot/snapshot/restore).

- core: expose Pitch + IdentifiedPitch via CanonicalValue (no new layout).
- The five kinds are v1-native (no lossy v0 predecessor): project/migrate them by
  identity; only the original kinds reconstruct from a lossy v0 form.
- Generators (testkit operation_payload, ops fuzz gen_payload) now emit the new
  kinds, so the convergence / determinism / migration-equivalence gates exercise
  them at scale; plus targeted migrate identity + reduce LWW/mint/delete tests.

Gates: build/fmt/clippy -D warnings clean; cargo test --workspace green (ops lib
51 tests); conformance_suite scale 1 passes. Catalog sections + DECISIONS for
these ops land in M2e per the staged plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:56:01 -04:00
Levi Neuwirth c9cafe9290 Agent K M2 prep: close review findings #4 (spelling domain) and #1 (migrate tests)
- valuegen::spelling(nth) is now injective over the full u8 (nominal = nth % 7,
  octave = nth / 7), so distinct selectors always give distinct PitchSpellings.
  Removes the silent mod-7 collapse footgun the M1 review flagged; no call-site
  changes needed (existing hex-looking selectors now genuinely differ).
- migrate.rs gains a unit-test module covering the reconstruction branches the
  corpus-driven equivalence gate never reaches: Tie/Beam create round-trip
  exactly; Spanner create is Irreversible (anchor-based, not event-ref —
  documented in operation_catalog §CreateCrossCutting); the tuplet-compensation
  variants migrate (ReplaceWithRest preserves rest id+duration, voice recovered
  at reduction per finding #3); respell recovers from context else Irreversible
  (P12-K1).

Gates green: build/fmt/clippy -D warnings; cargo test --workspace (ops migrate
tests + all criteria). No production logic changed beyond the spelling() token
domain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:20:55 -04:00
Levi Neuwirth 4556ede9f0 Phase 2 (Agent K) M1: value-typed operation payloads + Operation Catalog scaffold
Foundation milestone for Track B's Operation Catalog: shift epiphany-ops from
the v0 identifier-only payload projections to durable value-typed payloads, and
scaffold the companion spec. Scope: the representative §6.10 set (7 primitives +
2 meta-ops); the slice-driven K0 expansion follows.

Core (the K↔J seam):
- epiphany-core exposes a public `CanonicalValue` trait (canonical_bytes /
  decode_canonical) delegating to the existing private `Codec` machinery, for
  Event/Rest/PitchSpelling/Tie/Slur/Beam/Spanner/RegionTimeModel/TimeAnchor.
  No new byte layout — a value's bytes equal what the whole-score codec emits,
  so all goldens / criterion 4 stay byte-identical.

Ops (value-typed payloads, frozen reduction rules):
- InsertEventOp{staff_instance,event:Event}, RespellPitchOp{pitch,spelling:
  PitchSpelling}, CreateCrossCuttingOp{structure:CrossCuttingValue},
  ChangeRegionTimeModelOp{...,new_time_model:RegionTimeModel},
  SetUserSystemBreakOp{...,anchor:TimeAnchor}, ReplaceWithRest{rest:Rest}.
  Payloads frame each value's CanonicalValue bytes behind a u32 length prefix.
- reduce.rs: read-sites only moved onto the value (rules, conflicts, ordering,
  promotion, re-anchoring, undo, transactions unchanged); reduce_onto now
  materializes the real event/structure instead of the C4 placeholder.
  MaterializedState.spellings now stores PitchSpelling (encode + decode updated).
- v0.rs: frozen identifier-only shapes (migration regression guard).
- migrate.rs: migrate_v0_envelope(v0, &Score) + project_v1_to_v0 + MigrationError;
  deterministic and equivalence-preserving. Respell spelling recovered from the
  score context; irreversible case is P12-K1.
- valuegen.rs: shared value-type builders (reused by fuzz, migration, tests,
  testkit). Resolves P11-C1; P11-C10 Dismiss recorded.

Testkit (Agent F merge gate):
- migration.rs: reduce(v1)==reduce(migrate(project(v1))) byte-identical, plus
  migration determinism and a non-vacuity guard; wired into acceptance.rs as
  agent_k_migration_equivalence_gate. Generators/harnesses build v1 payloads.

Spec:
- spec/operation_catalog.{tex,pdf}: new companion (independent semver) — framework
  + per-primitive template, the 7+2 representative primitives, the v0→v1 migration
  contract, and K1 framework slots for the remaining K0 primitives. Builds clean.
- PASS12_BATCH.md: P12-K1 (respell fingerprint irreversibility).

Gates: cargo build/fmt/clippy -D warnings clean; cargo test --workspace green
(incl. criteria 1/4/5/6 and the new K gate); conformance_suite scale 1 passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:52:02 -04:00