Found by starting the implementation: the grammar could not derive an ordinary
pitched note.
`value` had no alternative for a sequence at all, though value-projection clause 5
required one. Adding it exposed why it was missing. A sequence whose first element
is a fieldless variant is shape-identical to a struct, and `()` is both the empty
sequence and the absent option. Both collisions are reachable from the first
pitched note in any score: `PitchedEvent` carries `articulations` and `ornaments`,
sequences over the zero-field `ArticulationMark` and `OrnamentMark`, beside an
optional `DynamicMark`. One `insert-event` line holds both.
The collision is irreducible without new syntax, and new syntax buys nothing --
`req:textproj:strict-parse` already obliges a parser to reject a duplicate in a
set-typed field, which it cannot do without knowing the field is set-typed. The
parser consults the schema either way. So `req:textproj:schema-directed` states
what the ratified rules already required, `value` collapses to `"(" value* ")"` or
a leaf -- all shape can honestly say -- and the requirement assigns meaning by the
expected type. The binary form is schema-directed for the same reason and pays the
same price: its bytes do not say what they are either.
Three consequences stated: a struct with no fields is the bare symbol, as a
fieldless variant is; a byte string is not a sequence, so an opaque extension
payload and a `SoundConfiguration` project as byte strings, never as lists of
integers; and the grammar's repetitions now carry a notation rule -- adjacent
elements separated by exactly one space -- without which `"(transpose (" bytes* ")"`
spelled two targets as one undelimited run of hex.
Checker gains a seventh test, mutation-verified by restoring the 0.4.0 `value`
production and by stripping the schema-directed citations. It asserts the
symbol-headed struct alternative is *absent*: a grammar claiming to tell a struct
from a sequence by shape would be lying.
Gate green -- clippy 0, 1038 tests, doc 0, conformance 8/8, no golden churn, three
spec documents build clean with no undefined references.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 0.3.0 audit raised two normative issues and one process issue. All three
land here, plus a fourth defect that reviewing the fix turned up.
The grammar contradicted its own escape requirement. `req:textproj:string-escapes`
obliges a writer to escape the backslash and a parser to reject a bare one, while
`unescaped` admitted it. Escapes are now four two-character sequences and
`unescaped` excludes U+0022, U+005C, U+000A, U+0009 by codepoint.
"Keep the binary order" was not available for every sequence. It holds only where
the binary order reads data the projection preserves, and two sequences fail that
test: `blob_roots` sorts by the full `BlobRef` encoding (offset, compressed
length, compression), and an extension's preserved chunk roots sort by
`ChunkRef`'s order, keyed on kind, then content hash, then *offset*. Under the
blanket rule, relocating a chunk -- which changes no semantics -- would change the
text, and two entries indistinguishable after erasure would produce duplicate
lines. `req:textproj:derived-ordering` orders and de-duplicates exactly those two
by projected form, and states that every other sequence keeps the binary order:
profile and extension declarations sort on semantic `(id, version)` keys, and
envelopes on canonical operation order.
The "machine-checked" grammar was checked by a throwaway script -- true of one run
and of nothing durable, the same evidence gap P2-P4 kept exposing.
`text_projection_grammar.rs` is the committed form: no nonterminal undefined or
unreachable, the escape rule admits exactly its four sequences, and the operation
and chunk productions are *derived* from `OperationKindTag::PAYLOAD_FREE` and
`ChunkKind` through an exhaustive match, so a kind added to the enum and not to
the grammar fails to compile. Every locator finds its production by name; writing
it exposed four bugs in itself, three of them column-anchored checks that a reflow
would have silently switched off.
Reviewing the escape fix found it reintroduced the audited defect in disguise: a
quoted terminal `"\\"` reads as two backslashes, so every escape became three
characters long. Both characters are now codepoints. Relatedly the mono font's
`Ligatures={TeX}` rendered U+0022 as a right curly quote and `--` as an en dash,
so the grammar misprinted the very delimiters it defines; `core_spec.tex` already
omitted it. Both are asserted against.
All six tests mutation-verified: the anchor asserted present before substitution,
then the named test observed to fail. Gate green -- clippy 0, 1037 tests, doc 0,
conformance 8/8, no golden churn, three spec documents build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
857 workspace tests pass; clippy -D warnings, fmt, and rustdoc clean;
both new companions build with zero undefined references.
Quality Metric Catalog v0.1.0 (spec/quality_metric_catalog.tex, new):
- Formal definitions for all nine normative quality metrics, each with
a raw measurement over resolved geometry and a clamped-linear
normalization n = min(1, raw/R_worst) with pinned anchors.
- The vacuous-geometry rule (a metric over absent geometry evaluates
to 0.0; the notated-but-unrendered honesty edge is an open
question), all-1.0 default tie-breaking weights, and the per-tier
threshold table — Minimal's uniform 0.90 deliberately fails the
all-worst placeholder, forcing real measurement.
- Pins QualityMetricKind (referenced but never defined by the core
spec) and the registered SolverProfile catalog (Draft selects the
Minimal threshold column; Standard/Publication select Standard).
- QualityFloorApproached fires at 0.8x the applicable threshold and
is status-neutral by requirement.
Reference Suite v0.1.0 (spec/reference_suite.tex, new):
- Six entries referenced by deterministic builder + seed (RS-1
ten_measure_single_staff, RS-2 valid_score_rich, RS-3..6 corpus
fixtures), each with the declared A4-at-8mm-staff solve geometry
(Canvas.layout_defaults has no graph home yet, P12-I7).
- All entries required at Minimal; the same set is the pre-declared
Standard bar (no implementation claims Standard yet). Fixed-
expectation tests deliberately unused in v0.1.
Real metrics in the engraver (engrave/src/quality.rs, new;
layout-ir/src/quality.rs = the catalog constants transcribed):
- QualityMetricVector::unmeasured() replaced with computed values:
collision sweep with the catalog's same-slot-cluster and stroke
exclusions, per-system spacing CV, vertical gap deviations,
system-break slack, page fill, casting-off width CV, symbol
density; slur/beam vacuously 0.0 (no drawn geometry exists).
- Bit-identical across repeated solves (tested); floor warnings never
change solve status; malformed inputs keep unmeasured(). The two
all-worst test pins now assert real values; the StubSolver's
unmeasured() stays (Stub genuinely computes nothing).
Reference-suite harness (testkit reference_suite module + tests):
- Each RS entry asserts the four-condition Minimal pass (hard
constraints, byte/bit determinism, well-formed Minimal report,
every axis within threshold) under the F1 Pass/Xfail discipline,
with the measured table printed per run.
- HONEST FINDING, day one: RS-1 fails Minimal casting-off (measured
1.0 vs 0.90) — greedy first-fit leaves a two-measure stub last
system (width CV 0.6145). Encoded as an asserted Xfail row (fails
on XPASS) and filed as P12-I11 (engrave balance pass, or catalog
revision). P12-I12: the Standard spacing floor warns on short
scores with wide lead measures.
Multi-system click-to-insert fix (editor-core):
- Casting-off exposed two inversion breaks: position_anchors fed a
non-monotonic cross-system anchor list into a monotonic inverter
(system-2 clicks resolved to system-1 times), and
nearest_manifestation found only system 1's staff-line segment
(system-2 clicks got system-1 pitch geometry).
- Fixed with a containing-system lookup over the resolved pages tree
(containment, else nearest by vertical distance), per-system staff
resolution, and per-system anchor filtering; degenerate-geometry
fallback preserves the flat path, so all 84 pre-existing
editor-core tests pass unmodified.
- Five regression tests through the real Engraver over the wrapped
ten-measure fixture, each shown to fail without the fix; testkit
gains dev-only dependencies on editor-core and engrave.
Trackers: P12-I11/I12 filed; DECISIONS entries in engrave, layout-ir,
and testkit; Phase-3 memory updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
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
Land the two real pre-passes as canonical *derived annotations* (pure
functions of the materialized Score + profile, recomputed on
materialization, never serialized into canonical Score bytes), exposed via
`derive_annotations`:
- Spelling: a Temperley-style line-of-fifths centre-of-gravity preference
rule (key-free, deterministic), preserving authored CMN letters and only
inferring spelling for chromatic/integer input. `resolve_spelling`
layers authored overrides above the inferred default (the RespellPitch
precedence rule). `spell` now takes `&Pitch` and delegates to
`simplest_spelling`.
- Decomposition: metric greedy-aligned splitting on a 1/4096 integer grid
(barline + dyadic-boundary ties), with exact sounding->notated tuplet
conversion before gridding. Components reconstruct the event duration
(invariant 15).
- A per-event-kind eligibility `TaxonomyReport` so "ineligible" is always
explicit and counted, never silently absent.
Test infrastructure (Agent F): a 29-fixture representative corpus +
taxonomy harness (corpus.rs), the H spelling/decomposition merge gate
(prepass_harness.rs), a discrete `tests/prepass.rs` CI target, conformance
stage [7b], a dedicated CI job, and the Pass-12 batch tracker.
Review hardening folded in (nine findings):
- Guard `decompose_metric` against a zero-length measure (was a
divide-by-zero panic; now reported ungriddable).
- Resolve spelling-override priority via `Reverse` instead of negation
(was an i32::MIN overflow).
- Verify spelling *register* (octave), not just pitch class, in the gate.
- Close the decomposition under-emission gap: the unusual-outcome
taxonomy buckets are an exact per-fixture whitelist (classify_corpus
step 5b).
- Generalize `accidental_ids` to a glyph stack so authored extreme
alterations (triple-sharp+) reconstruct exactly instead of being clamped.
- Per-fixture spread checks in the non-vacuity tripwire and broad-bucket
coverage, so no single rich fixture can carry a signal (partial-stub
resistance); added a `mixed_rhythm` fixture for margin.
- Pin the integer-grid note-value math to the canonical rational helpers
via an exhaustive test; cross-reference comments.
- Replace the O(n^2) tuplet innermost-resolution scan with an id index.
fmt + clippy -D warnings clean; 199 tests pass; conformance suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011giSRaHCFCGm1Z2SWv6JHt