Commit Graph

5 Commits

Author SHA1 Message Date
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 ff9bd0fd06 G-minor: the chunk schema minor becomes a derived record
Implements the schema-minor MUST (binary_format.tex, Schema Versioning) that no
writer had ever honoured: a writer raises the chunk minor when it emits a
discriminant appended after the minor it otherwise declares, so an
unknown-discriminant decode failure is attributable to version skew rather than
corruption.

introduced_minor() lands on the five vocabularies with post-baseline variants -
OperationKind, OperationKindTag, OperationPayload, ReanchorReason, and
PreconditionFailureReason - each exhaustive with no wildcard arm, so a future
variant cannot compile without being assigned an epoch. The tag epochs live
inside operation_kind_tag_vocabulary! rather than beside it, because a sibling
match is the parallel list that macro exists to prevent. The sentinel is
Option<u16>, not 0, since 0 is a real baseline minor for V1-V3 and conflating
them would make the max read correctly only by accident.

An envelope's required minor is the max over every discriminant it actually
emits; a block's is the max over its envelopes; major and minor derive
independently. Baselines are not normalised - V0 keeps minor 1.

The manifest seam keeps epiphany-bundle opaque: no ops or layout-ir dependency,
and the aggregate version is supplied by the producer rather than derived, with
CommitContext carrying the previous one so unchanged barrier content preserves
it. The version rides the superblock slot that already exists; Manifest gains no
field, which would have been schema-major and would have defeated the rung.
bundle.rs's superblock check stays major-only - tightening it to full-version
equality is a conformance regression, and s11 locks that.

textproj carries the manifest SchemaVersion and never derives it, so
COMPANION_VERSION moves 0.9.0 to 0.10.0 with the corpus regenerated. Not because
of op-block stamping, which remains projection-invisible. A new normative
requirement records the carry-never-derive rule in the companion itself; its
rationale names layout-ir, which textproj genuinely lacks, rather than the
operation vocabulary, which it has.

Gate [7f] adds an independent oracle over decodable in-tree barrier fixtures,
requiring exact equality rather than >=. Equality is load-bearing: >= catches
under-stamping but not stale over-stamping after the sole maximum contributor is
removed. Undecodable blobs are reported not-checkable, never as a pass.

Also repairs binary_format.tex's stale claim that OperationKind and
OperationKindTag append at 30 with a history stopping at 29, while 30-33 are
taken and the normative tables already carry them.

Gate: 1399 tests, clippy 0, fmt clean, conformance 8/8 with [7f] at four
fixtures checked and one not-checkable, 102 decode vectors byte-identical
(verified, not assumed), 13 text-projection vectors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-28 21:04:34 -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 244bd6bd96 Text Projection: the document layer, and the equation closes
`epiphany-textproj` projects a bundle to its canonical text and reads it back:
the header, identity, profile, extension, canonical-base and blob productions,
plus the pipeline that joins them to the operation layer already in place.

Free functions, not `TextValue` impls. `epiphany-bundle` does not depend on
`epiphany-core`, so the trait and the manifest types are both foreign to this
crate and the orphan rule forbids the impls. That looked like it would force a new
dependency edge until the productions were read properly: no document line
contains a `value` position, so none is needed. The bundle stays independent of
the music model, as designed.

Blobs are handled on both sides and neither is the obvious one. The emit side
filters `blob_roots` through a real reachability predicate that today provably
returns empty -- never the field wholesale, which is the plausible wrong answer.
The parse side rejects any blob line outright, while still parsing the production
in isolation so both halves are ready together. A trip-wire source-scans
epiphany-core and epiphany-ops for `BlobId` and names both obligations when it
fires; it asserts it actually scanned files, so it cannot pass vacuously.

Two rejections were added on the agents' own reading of the spec rather than the
brief, and both are right. Envelopes must already be in canonical reduction order
(text_projection.tex:458) -- without that, two texts differing only in envelope
order would denote one document. And a hand-built `TextDocument` carrying blobs is
refused rather than silently dropped, since serializing it would produce a bundle
whose next projection loses those bytes.

Section order is enforced as a sequence, not a set: a repeated or out-of-order
section is a rejection, never something the parser sorts back into place.
`derived-ordering` sorts and de-duplicates blob lines and extension chunk roots by
*rendered* form, proved against fixtures that are unsorted in memory rather than
already canonical -- the blind spot that let five outbound normalizations ship
untested in the operation layer.

Serialization stages payloads and lets `commit` assign every offset, hash and
chunk id, which is exactly what `req:textproj:derive-or-carry` assumes; nothing is
hashed by hand. The emitted bundle deliberately carries no accelerators.

And the three directions compose: `project(serialize(parse(T))) == T` holds
end-to-end over the companion's own worked example. Eight checks re-mutated
independently of the agents' reports, each killed by its own named test; all eight
document productions diffed against the grammar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 15:05:28 -04:00