23 KiB
Pass 12 — Batch Tracker
Maintained by Agent F (testkit & tripwire), per spec/PHASE2_QUICKSTART.md:
"Ambiguities discovered during Phase 2 implementation go into a Pass 12 batch,
not into code improvisations. … Don't open Pass 12 until at least 3 items
accumulate."
Status: OPEN. Agent H's landing (spelling + decomposition pre-passes) surfaced five candidates, crossing the ≥3 threshold. This file is the running collection; G ratifies (or defers/rejects) the batch when Phase 2's open questions are resolved. F collects, F does not resolve.
Pass 12 tranche 1 landed (2026-07-02): the audit spec-alignment set —
places where spec text trailed an already-made disposition (catalog, crate
DECISIONS, PHASE2_QUICKSTART) — was ratified directly into core_spec.tex /
operation_catalog.tex (0.2.0 → 0.3.0); dispositions in
PASS12_RATIFICATION_LOG.md. That tranche resolves none of the rows
below; they remain open for G.
How an item lands here
When implementation hits a behavior the ratified spec does not determine, the
responsible agent records it in their crate's DECISIONS.md with an ID
(P12-<agent><n>) and a one-line rationale, then adds a row below. Improvising in
code instead is the failure mode this batch exists to prevent.
Items
| ID | Source | Summary | Disposition target |
|---|---|---|---|
| P12-H1 | epiphany-core H |
Ratify SpellingAlgorithmId::Default = Temperley line-of-fifths v1 (Pass 11 closed before H landed; the id "default" is the crate's proposal until ratified — not a byte layout, so nothing golden-locks on it). |
G (algorithm-choice ratification) |
| P12-H2 | epiphany-core H |
KeySignatureChange / ClefChange are anchor-only placeholders; context-aware spelling infers tonal context from the melody (line-of-fifths centre of gravity) rather than a declared key. A real key/clef content model would let spelling/decomposition honour declared keys and place cancelling naturals. Flagged as a graph-model gap. |
G (graph model) |
| P12-H3 | epiphany-core H |
Chromatic-run convention (ascending = sharps, descending = flats) is only a tiebreak in the centre-of-gravity rule, so an isolated chromatic run with no tonal context may pick the enharmonic the convention would not. Voice-leading refinement deferred. | G / Pass 12 (spelling) |
| P12-H4 | epiphany-core H |
Decomposition simplifications: single governing meter per region (multi/mid-region meter changes deferred); region origin assumed a barline (anacrusis deferred); compound-meter beat grouping beyond the dyadic default; tuplet nesting and cross-beat tuplet members; double+ augmentation dots (MAX_DOTS = 1). |
G (decomposition scope) |
| P12-H5 | epiphany-core H |
Automatic spelling under aleatoric regions (the spec's open question). H spells pitches region-independently but performs no region-specific aleatoric spelling; defer if the algorithm does not generalise cleanly. | G / Pass 12 (open question) |
epiphany-layout-ir / engrave / render-svg I |
The v0 pipeline was a structural placeholder (one arbitrary glyph per object at y = 0), not real notation. Resolved by I-1 (Phase 2-3): to_constrained now builds real notation — clef-relative noteheads (by NoteValue), spelling-derived accidentals, key/time signatures, rests, barlines, and the staff-line/stem strokes — and the Engraver re-spaces it; the human visual-acceptance gate is met (goldens locked against the stub and the real Engraver). The Ch 7 engraving-boundary question resolved to: notation construction lives in to_constrained, horizontal spacing in the Engraver. |
✅ done | |
epiphany-determinism / epiphany-layout-ir I |
Stable layout-object id derivation (MUSCLOID, Pass-11 item 2.6). Wired: epiphany-determinism now reserves the built-in DomainTag::LAYOUT_OBJECT_ID (MUSCLOID), and layout-ir's provenance derivations (single / multiply-manifested / synthesized) plus the engraving-decision id route through it (no longer borrowing MUSCCONF). Layout ids stay non-canonical, so only data-prov hex in the render goldens changed; no durable/interchanged artifact. See layout-ir/DECISIONS.md and req:layoutir:object-id-derivation. |
✅ done | |
epiphany-layout-ir I |
The bundled BRAVURA_METRICS were approximations disagreeing with the renderer's genuine outlines. Resolved by I-4a: the metrics table is re-extracted from the same SHA-pinned bravura-1.392 font the outlines come from, with bboxes rounded outward so each metric box contains the drawn ink (a render-svg test proves containment); BRAVURA_VERSION = SemVer(1, 392, 0). A coupled barline-placement bug it surfaced (bottom-origin glyph floated) was fixed in the same increment. |
✅ done | |
| P12-K1 | epiphany-ops K |
A v0 RespellPitch carried a ContentHash fingerprint of the spelling, not the PitchSpelling. The v0→v1 migration (Operation Catalog, M1) cannot invert a fingerprint, so it recovers the spelling from the score-graph context (an explicit per-pitch spelling attachment whose canonical bytes hash to the fingerprint) and returns MigrationError::Irreversible (bundle opens read-only) when the context lacks it. Every other representative payload migrates self-contained; this is the lone exception. Confirm the read-only fallback is the intended disposition vs. requiring a v0 corpus that preserves spelling pre-images. |
G / Pass 12 (migration) |
| P12-K2 | epiphany-ops K |
The Transpose op (Operation Catalog, M2 Group 1) carries a minimal chromatic_steps: i32 interval and reduce_onto applies it as a CMN alteration shift only. Faithful interval algebra (diatonic vs. chromatic intervals, octave/nominal renormalization, transposition in non-CMN pitch spaces) is the deferred Chapter 4 tuning-catalog territory. The prototype also clamps the shifted alteration to the i8 range, so an extreme transpose silently saturates instead of renormalizing — another reason the representation needs pinning. Pin the interval representation and transposition semantics when the tuning catalog lands. |
G / Pass 12 (tuning) |
| P12-H6 | epiphany-core H |
Decomposition-attachment precedence: the spec says the decomposition pre-pass uses the "same precedence machinery" as spelling, but spelling's machinery is per-score configurable with priority/timestamp tie-breaks while DecompositionAttachment carries no priority and the graph has no DecompositionPrecedence field. The audit fix implements the fixed default order (UserChosen > Imported > Propagated > Inferred, canonical attachment order as tie-break). Decide whether decomposition precedence is configurable (a canonical-codec change) or fixed. |
G / Pass 12 (decomposition) |
| P12-H7 | epiphany-core H |
Authored decompositions for inference-ineligible events: an authored attachment is exactly how a user would notate an ungriddable event, but the derived-annotation surface (mirroring spelling, which likewise ignores attachments on spelling-unavailable pitches) only resolves overrides where inferred output exists. Needs a spec answer for both pre-passes. | G / Pass 12 (pre-passes) |
| P12-K3 | epiphany-ops K |
Content modification of a SYSTEM_DERIVED pitch: ModifyEvent/ModifyIdentifiedPitch can rewrite a synthetic pitch's intrinsic content in place, silently invalidating the id's content-derivation (Invariant 11). The new reduction-time collision check deliberately does not treat in-place rewrites as mints. Decide whether reduction must refuse them outright. |
G / Pass 12 (identity) |
| P12-K4 | epiphany-ops K |
ResolveConflict beyond the concurrent case: the spec pins outcomes only for concurrent differing resolves; the implementation applies the same rule to causally-later resolves (so an intentional re-resolution cannot supersede) and reads AlreadyApplied for any resolve against a Dismissed conflict. Also: the meta-conflict record cannot name the contested conflict in affected_objects because TypedObjectId has no Conflict kind. Pin the causally-later semantics and decide whether conflict records need an addressable object kind. |
G / Pass 12 (conflict resolution) |
| P12-C1 | epiphany-ops C |
Multi-source cue re-anchoring: the rule table's action is plain "cascade-delete" (implemented: any source deletion cascades, like Tie) but its rationale ("a cue with no source is meaningless") implies truncate-while-any-source-survives. Pin one reading. | G / Pass 12 (re-anchoring) |
| P12-C2 | epiphany-ops C |
Graphic-gesture Range "truncate" is underdetermined. Implemented reading: a dead event-anchored range endpoint moves to its containing region's edge (start→Start, end→End, zero offset). Define "truncate" normatively. | G / Pass 12 (re-anchoring) |
| P12-C3 | epiphany-ops C |
Analytical-annotation range reconstruction: a wall-clock (region-relative) or indeterminate event span cannot be expressed as a stored Range anchor without region-origin resolution, so such annotations orphan. State whether orphaning is the sanctioned outcome or an expressible form is required. |
G / Pass 12 (re-anchoring) |
| P12-C4 | epiphany-ops C |
ReanchorReason has no same-canvas variant: a rank-4 (same-canvas) survivor is recorded ExplicitFallback rather than appending a discriminant. Ratify a beyond-region reason or bless the fallback recording. |
G / Pass 12 (re-anchoring) |
| P12-K5 | epiphany-ops K |
Equivocation resolution's third path — a profile-declared deterministic selection function (e.g. lowest-hash-wins) — is unpinned and unimplemented; the reducer has no policy hook. Referenced from the catalog §ResolveEquivocation rationale. | G / Pass 12 (equivocation) |
| P12-K6 | epiphany-ops K |
ResolveEquivocation edge semantics to pin: cascaded resolves (a promoted candidate that is itself a resolve does not govern a further promotion — single-pass implemented, vs. fixpoint); interaction with HLC-monotonicity quarantine (may a quarantined resolve govern?); a resolve held pending by its own causal gaps still governs promotion (set-level rule) while its effect stays pending; whether the invalid-target/chosen no-op warrants a dedicated PreconditionFailureReason (implemented: reuses TargetMissing). |
G / Pass 12 (equivocation) |
epiphany-ops K |
Advisory-precondition catalog: the spec declares the duration-boundary advisory only for InsertEvent while the implementation also applies it to ModifyEvent's replacement value — state ModifyEvent's bucket explicitly. Also: which advisory checks are blocked on graph-model completion (Instrument declared range; a Region slur-spanning permission flag; which extent shapes constitute a resolvable musical end bound). | G / Pass 12 (validation) | |
| P12-I4 | epiphany-layout-ir I |
Constraint-strength attachment: Ch9 defines ConstraintStrength and says the solver consumes constraints "in normalized form", but neither the normalized form nor Ch7's LayoutConstraint provides a channel for an instance to carry strength. Implemented rule: break strength = BreakKind (Hard→Required, Soft→Preferred{1.0}); other core families Required; Registered conservative Required. Bless the rule or add a strength field. |
G / Pass 12 (solver) |
| P12-I5 | epiphany-layout-ir I |
No renderable status exists for "constraints present but not evaluated": every renderable SolveStatus is documented as "all hard constraints satisfied", leaving a below-conformance passthrough solver no honest report. Implemented encoding: SolvedWithWarnings + satisfied_hard_constraints == false + a warning. Sanction it or define a non-evaluating-tier report shape. |
G / Pass 12 (solver) |
| P12-I6 | epiphany-layout-ir I |
The spacing pass MUST "build collision constraints" but no per-tier minimum emission set is named. Implemented floor: successive-notehead-column no-collision chains + per-glyph region containment + user-break constraints. A normative Minimal-tier floor would make the acceptance surface testable. | G / Pass 12 (solver) |
req:binfmt:opindex) |
epiphany-bundle D |
Operation-index provisional encoding (block-refs + id-sorted entries with u32 block ordinal and u32 in-block offset; golden-locked) awaiting Binary Format companion ratification, together with: the offset's meaning (first content byte within the uncompressed block payload), a normative definition of "stale" (implemented: index block-set ≠ manifest operation_roots under full-ChunkRef equality), the one-slot-per-id invariant, the load-bearing property that the envelope encoding leads with the 16-byte OperationId, and whether the commit-time "grown significantly" SHOULD gets a threshold or stays implementation-defined. Ratified as-implemented; refresh threshold pinned implementation-defined (open question retained in the companion). |
✅ done |
req:binfmt:ext-blobs) |
epiphany-layout-ir E |
Provisional canonical byte form for the EditBarrier/BarrierScope/BarrierCondition tree and the two ExtensionDeclaration blobs (push_set framing, u64 LE lengths; golden-locked). Ratified as-implemented. |
✅ done |
req:binfmt:condition-depth) |
epiphany-layout-ir E |
The spec places no bound on BarrierCondition recursion; the decoder needs one against adversarial bytes. MAX_CONDITION_DEPTH = 64 implemented — the companion pins 64 as the normative bound (decoders MUST reject deeper; writers MUST NOT emit deeper). |
✅ done |
req:binfmt:object-kind-open) |
epiphany-layout-ir E |
Barrier ObjectKind byte form = the TypedObjectId 16-bit discriminant (2 LE bytes) with open-value decode (unknown kinds never match, preserving append-only forward compat). Representation and open-value stance ratified. |
✅ done |
| P12-E4 | epiphany-editor-core E |
Barrier matching for operations with no graph target (SetMetadata, DeclareTransaction — implemented: score-wide barriers only) and for opaque Registered operations (implemented: fully conservative match) is unspecified. |
G (Ch. 8) |
| P12-E5 | epiphany-editor-core E |
The unsafe-edit tombstone MUST has no defined mechanism: the manifest-side form (drop declaration + preserved roots? an explicit tombstone record?), interaction with required = true, and whether crossing immediately deactivates the extension's remaining barriers (implemented: yes, recorded via extensions_requiring_tombstone() for the next bundle write). |
G (Ch. 8) |
| P12-K8 | epiphany-ops K |
Create score / canvas remain unavailable K1 slots: the document root and canvas are inline singletons (no TypedObjectId::Canvas; the root is never op-minted — genesis is Score::empty + bundle creation). Turning them into operations needs an addressable root/canvas object model. Decide: define one, or ratify genesis as deliberately outside the operation set. |
G (graph model) |
| P12-K9 | epiphany-ops K |
Differing-value re-creates (a live id re-carried with different content: CreateStaff, the carried TimeSignature, container creates) refuse with TargetMissing, which misnames the situation. Decide whether a dedicated PreconditionFailureReason (appended) is warranted. |
G / Pass 12 (vocabulary) |
| P12-K10 | epiphany-ops K |
Undo strand-blocks (StrictInverse refusing to tombstone a minted object still referenced by a live non-member, e.g. a staff with a surviving instance) reuse ConflictKind::TransactionConflict. Decide whether undo refusals deserve their own conflict kind. |
G / Pass 12 (undo) |
| P12-K11 | epiphany-ops K |
An undo's value restorations enter the write chains as ordinary writes by the undo op, so a second undo of the same transaction sees the first as a superseding writer (Conflicted/skip) while absence-restorations repeat idempotently — a documented asymmetry. Decide whether chain writes need distinguished undo provenance so repeated undo is uniformly idempotent. | G / Pass 12 (undo) |
| P12-K12 | epiphany-ops K |
Cross-region slur spanning permission: Region.permits_spanning_slurs (schema major 1, P12-K7) gates the CreateCrossCutting(Slur) advisory, but which region governs a slur whose endpoints lie in different regions is under-specified. Implemented as conservative AND (a boundary is permeable only when both endpoint regions permit); "the start region governs" or "either side" are equally defensible. The advisory is authoring-only and never alters reduction, so the choice is not byte-affecting. Ratify one reading. |
G / Pass 12 (validation) |
epiphany-engrave I |
Page geometry had no graph home: the spec named Canvas.layout_defaults ("paper size, margins") but defined no type, and adding a Canvas field is a schema-major change under the companion's frozen-layout rule. Casting-off therefore uses an engraver-side PageGeometry default (A4 at an 8mm staff: 105x148.5 ss, 7.5 ss margins). Phase A of the schema-major-1 bump defines the type (CanvasLayoutDefaults { page_size: CanvasSize, margins: CanvasMargins } in staff spaces, A4/8mm default) and ratifies its v1 wire form (Binary Format 0.3.0); the graph home lands in code in Phase C (Canvas gains the field), after which the engraver reads it (Phase C′) — until fully landed, solvers MAY default. |
G (graph model, schema-major) | |
req:layoutir:break-satisfaction) |
epiphany-engrave I |
Break-constraint satisfaction predicate: implemented as "a SystemBreakAt/PageBreakAt is satisfied iff the final layout starts a system/page at that slot" (a region-first slot is trivially satisfied). Ch7/Ch9 never define satisfaction for break constraints; ratify the predicate. |
G / Pass 12 (solver) |
req:layoutir:break-origin-attribution) |
epiphany-layout-ir I |
Honouring a user break must attribute the decision to its override (DecisionSource::UserOverride(id)), but constraints carry no override identity; implemented via a ConstrainedLayoutIR.break_origins sidecar populated by to_constrained. Bless the sidecar or widen the normalized constraint record. |
G / Pass 12 (solver) |
req:layoutir:continuation-synthesis) |
epiphany-layout-ir I |
System-spanning strokes split at system boundaries need synthesized provenance for continuation segments; implemented as SynthesisKind::Registered(SYSTEM_CONTINUATION_SYNTHESIS) with a deterministic (original, ordinal) instance key. Add a first-class continuation synthesis kind or bless the registered id. |
G / Pass 12 (provenance) |
epiphany-engrave I |
RS-1 honestly failed the Minimal casting-off threshold under the reference engraver (measured 1.0 vs 0.90): greedy first-fit left a two-measure stub last system (width CV 0.6145 ≥ the 0.5 anchor). Resolved the honest way — an engrave-side casting-off balance pass, no catalog change: casting-off gained a second widow-rebalance phase that moves whole trailing measures from a region's penultimate system into its final one, choosing the shift that minimizes the larger of the two distribution penalties the catalog defines for the break family (width imbalance vs non-final underfill; both share the 0.5 anchor). RS-1 now casts six/four instead of eight/two: casting_off 1.0 → 0.4463, system_break 0.254 → 0.677, every axis ≤ 0.90. ENGRAVER_VERSION 2 → 3; render goldens regenerated; the suite's Xfail row promoted to a plain Pass. The casting_off 0.5 anchor and the Minimal column stood — the engraver improved, not the threshold — so the Quality Metric Catalog is normatively unchanged. |
✅ done | |
epiphany-engrave I |
The Standard-tier spacing floor warned on short healthy scores: 3–8-column entries with a wide clef/key lead measured spacing CV 0.36–0.41 > the 0.32 Standard floor (a spurious diagnostic — the scores all passed Minimal). Resolved by the lead-aware refinement (Quality Metric Catalog 0.1.0 → 0.2.0): spacing_distortion is now scoped to the system's rhythmic columns (slots bearing a notehead or rest), excluding the clef / key-signature / time-signature lead and treating barlines transparently (a note-to-note advance spans them) — so the furniture-width lead gap no longer inflates the CV. The three entries drop to 0.2188 / 0.0819 / 0.0856 (all below the 0.32 floor), and the axis stays honest on real irregularity (RS-3 keeps 0.2188 from a mid-line accidental). Anchor, orientation, range, thresholds, and the eight other axes unchanged; no layout/golden change (measurement-only, ENGRAVER_VERSION untouched). The duration-aware optical-spacing open question (deviation from duration-proportional spacing) stays open — it needs the pipeline's deferred duration-proportional preferred widths. |
✅ done |
Not yet open elsewhere
Agent I (Track A) has contributed P12-I1..I6. Track B's Agent K has
contributed P12-K1..K7; H has contributed P12-H1..H7 (H6/H7 from the 2026-07
spec-compliance audit follow-up, alongside K3/K4). The 2026-07 Push-3 wiring
work added C1..C4 (re-anchoring), D1 (bundle operation index), and E1..E5
(edit barriers). The Phase-3 first tranche (casting-off + K1 schema-fill +
value-restoring undo, 2026-07-02) added C5, K8..K11, and I7..I10.
The second tranche (Quality Metric Catalog 0.1.0 + Reference Suite 0.1.0 +
real engraver metrics + the suite harness + the multi-system click fix,
2026-07-03) added I11..I12. Follow-ups (2026-07-03) resolved both (struck
through above): I11 by an engrave-side casting-off widow-rebalance phase
(engrave v3, no catalog change), and I12 by scoping spacing_distortion to
rhythmic columns (Quality Metric Catalog 0.1.0 → 0.2.0). The only remaining
spacing follow-up is the catalog's duration-aware optical-spacing open
question, which lives in the companion, not as a batch row.
Agent J's Binary Format companion now exists
(spec/binary_format.tex, v0.1.0): it ratified the P12-D1/E1/E2/E3 inputs
(struck through above) and discharged the crates' provisional-codec notes
(core P11-4, ops "provisional canonical encoding", bundle P11-D2/D4/D5). Its
three open questions (SnapshotId derivation, index-refresh threshold, u64/u32
prefix unification at the next schema major) live in the companion itself, not
as batch rows.