The ratified reasoning is untouched — every changed token is a bare line
number. What the contract argues, pins, and disposes of is unchanged;
only its pointers move, so a reader following them lands on the code the
sentences describe.
Ten, not the two first found: reduce.rs:4119 and :4117 both name
CreateStaff's group precondition, now :4372; invariants.rs:1126/:1135
name invariant 10's two resolution arms, now :1188/:1196; graph.rs:819
and :1614 name the two fields, now :848 and :1650, cited twice each.
core_spec.tex:5578 appears twice and needed two different corrections —
at the prose it quotes the doc line "Visual grouping: which staff group",
now :5585; in the pin-4b table it parallels :4231's struct declaration,
so it wants `pub struct Staff {`, now :5564. A single substitution would
have been wrong in one of the two places.
Forward, not by amendment: the contract is ratified and pushed, and its
hash is cited by the ledger and the genesis plan.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
1. The ruling had no normative/API surface. §1.1 assigns authority, but Pin 3
still said "nothing else in core" and the touch table listed graph.rs under
"Not touched" -- leaving the contract and P13-S16 as the only carriers of
supposedly normative meaning while core_spec.tex remained ambiguous. A
normative ruling that lives only in a contract is not normative.
Added pin 4b requiring all four sites, and narrowed pin 3 to the codec
surface ("no other CODE change in core"):
- graph.rs:819 Staff.group -- sole authority. Note this field has NO doc
comment today; it must be written, not amended.
- graph.rs:1614 StaffGroup.members -- non-authoritative projection, must
not be read for membership, may be stale in both directions. Also
currently undocumented.
- core_spec.tex at the Staff/StaffGroup declarations -- the same rule
normatively. This is the document that declared both fields without
ranking them, so it is where the ambiguity actually lives.
- operation_catalog.tex -- explicit stale-form semantics in the new
CreateStaffGroup section AND the existing CreateStaff section (:1104).
CreateStaff needs it because it is the operation that creates the
missing form, and its section currently promises nothing.
Each cross-references P13-S16. graph.rs moves into the touch table
(documentation only; no type changes anywhere in core). Added t14 to guard
the two field doc comments, sliced per-field on t12's discipline since a
file-wide grep cannot fail here.
2. t8b's two mutations were assigned to the reducer that runs FIRST in each
order, so neither could reach the state its assertion names:
- Missing form (CreateStaffGroup then CreateStaff): create_staff_group
cannot append a staff that does not exist yet. Mutation moved to
create_staff -- append the newly minted staff to g.members.
- Spurious form (CreateStaff then CreateStaffGroup): create_staff has no
later group to repair. Mutation moved to create_staff_group -- reject or
normalize away the non-empty carried members.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
Ruling, ratified 2026-07-29:
- Staff.group is the SOLE authority for membership.
- StaffGroup.members is a non-authoritative denormalized projection.
- G3a stores it but neither maintains nor trusts it.
- BOTH stale forms are permitted, equally: a missing member (s.group ==
Some(g) while g.members omits s) and a spurious member (g.members contains
s while s.group is None or names a different group). The earlier draft
named only the first, which left the spurious form reading as a bug rather
than a permitted state.
Withdraws the earlier draft's argument for B. "B adds no semantics while A
does" was wrong: B assigns authority to a field the specification left
unranked, which IS a semantic change. What B defers is enforcement, not
meaning. The honest advantage is narrower -- B adds semantics without adding
machinery, leaving the mint a mint.
Files P13-S16, which makes the contract's "filed gap" claim true; it was
false when written, since no such entry existed. The entry records both
stale forms, the disposition-A fix, candidate invariant 21, the re-carry
comparison question A must answer, and the standing instruction that
consumers read Staff.group and never StaffGroup.members for membership.
Remaining repairs:
- PLAN_GENESIS_OPS.md still claimed G3a "closes the staff-group half".
Narrowed to satisfiability, matching the contract.
- Boundary accounting normalized to six crossings across both documents: one
exhaustive-match site plus five literal/prose sentinels. The two classes
are counted together but named apart because they fail differently -- the
match site refuses to compile, while every sentinel stays green while
meaning something narrower than it says.
- t4 now runs four independent mutations, one per struct_codec! layout;
these are four separate layouts and one mutation signs one of them.
Collapsing is permitted only if the implementation consolidates them
behind a shared mechanism, and must be stated if it does.
- t8 split: t8 asserts satisfiability only; new t8b pins BOTH asymmetric
authoring orders as states the ruling permits, with a mutation each. A
test pinning one order leaves the other free to change silently.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
Five review findings, all confirmed against the tree before repair.
1. StaffGroup/Staff authorship cycle. CreateStaff requires its group live
(reduce.rs:4117); CreateStaffGroup requires its members live. With mints
only, NO authoring order yields a bidirectionally consistent group, and the
inconsistency is unrepairable in-packet -- there is no ModifyStaffGroup and
deletes are deferred. Invariant 10 checks resolution in both directions
independently (invariants.rs:1126, :1135) and agreement in neither; the
spec declares both fields (core_spec.tex:4231, :5578) without saying which
is authoritative. Added as §1.1, UNRESOLVED, with two dispositions and a
recommendation -- the shape G2b's accidental_extensions pin took. t8's
claim is narrowed accordingly: what closes is that CreateStaff's group
precondition becomes satisfiable, not that a consistent group becomes
authorable. The unqualified "defect closes" framing was wrong.
2. Pin 4 promised byte-identical re-carry without the machinery that makes it
work. Comparing "the same value" requires retaining it, and the object map
holds only Live/Tombstoned. Exactly three carried-value maps exist
(reduce.rs:999-1007), each threaded through seven sites; G3a needs four
more, so 28 touch points the first draft omitted entirely. Added pin 4a
with the seven sites enumerated. Site 4, base seeding, is the one that
fails silently: a re-carry test that only reduces from empty cannot see a
missing seed at all. Added t5b (base re-carry) and t13 (each of the four
seeds killed separately), modelled on G1's documented instrument_values
precedent at reduce.rs:13264.
3. The companion bump was stale. The tree is at 0.11.0 (G2b bumped it,
textproj/src/lib.rs:47); the contract repeated G2b's 0.10.0 -> 0.11.0.
Corrected to 0.11.0 -> 0.12.0 at both sites, with the negative vector
rejecting 0.11.0.
4. Mutation table repairs:
- t4's mutation was impossible. A create op's encode_canonical is a single
push_lp_bytes line over one carried value, so there is nothing in the op
to reorder. Moved to the carried type's struct_codec! declaration
(codec.rs:1790), which is where the layout actually lives and is
self-consistent across both halves -- exactly trap 4's shape.
- t6 omitted PartDefinition.staves. Now three independent mutations, one
per referential loop; an uncovered loop is a loop that deletes green.
- t9 moved fixture and production together, which proves only that a bad
score fails a checker. Fixture now constant and already dangling; only
production mutates, one skipped check at a time.
- t12 could not fail: grepping the whole file matches the implementation
body, which contains the identifiers the doc comment is meant to gain.
Now slices the invariant-10 doc block and searches only that.
5. Touch table missed testkit/src/layout_stub.rs:1373, whose 30..=34 range
restates the coverage claim in prose. Added there and to the plan's trap 6,
now five boundary-crossing literals rather than four.
Editor authorization recorded as granted narrowly: barriers.rs for the four
exhaustive subjects_of arms only, barrier.rs for the invalid-tag literal,
comment, and assertions 35 -> 39 only. No other change in either crate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
Folds the three open PLAN_GENESIS_OPS.md §6 rulings, ratified 2026-07-29:
1. Deletes: mints only, all five deferred out of G3. The former "live
dependents -> container-not-empty" sentence is superseded outright --
ContainerNotEmpty concerns owned children (effect.rs:156), while the G3
hazard is dangling inbound references from independently-live objects,
which would need a new typed reason and its own epoch.
2. decomposition_attachments: derived, not authored.
3. Measure.time_signature: resolution-plus-agreement. An optional explicit
declaration at the measure start, neither an override of the metric grid
nor a cache of it. Invariant 20 (G3b) covers agreement and boundary
consistency only, not reference resolution.
G3 splits: G3a (four root-level mints, kinds/tags 35-38, epoch 11, all
schema major 0) and G3b (CreateMeasure, kind/tag 39, epoch 12, carrying
invariant 20 and a new precondition reason at discriminant 16).
Correction of record: an earlier scoping claimed invariant 10 "covers
cross-cutting refs, not this". False -- read off the variant's doc comment
rather than the check body. Invariant 10 already resolves a staff's group, a
group's members, a part's staves, a view's active layers, and measure and
grid time-signature references (invariants.rs:1122-1156, :1180-1212), tested
at :3596. Its doc comment names none of them, so G3a owes a prose
reconciliation and no enum entry.
Also corrects five drifted citations that a touch table exists to prevent:
reduce.rs 3850->4075 and 2342->2559, barriers.rs 437->313, barrier.rs
1105->1156, text_projection_grammar.rs 307->315; and "five siblings" ->
"four siblings" now that CreateMeasure is its own packet.
Verified against the tree, not assumed: all four carried types are schema
major 0 (no versioned walk exists; both decode_v0_score and the live walk
read them through plain Codec::dec), TypedObjectId already carries all five
variants, and struct_codec! generates TextValue as well as Codec -- so G3a
moves no wire bound, appends no typed id, and needs no textvalue_graph.rs
work. It does close a live defect: every G3 object kind becomes Live only
through base ingest, so CreateStaff's group precondition is currently
unsatisfiable under from-empty reduction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV