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
This commit is contained in:
parent
039a8ddec8
commit
13c3d2fd46
|
|
@ -644,3 +644,45 @@ declaration order (`core_spec` §"Extension Declarations"), which had chunks bef
|
|||
kinds and barriers.
|
||||
|
||||
Still no implementation. The companion is now complete enough to implement against.
|
||||
|
||||
## Genesis tranche G2b: op-block accept-set raised to [0, 3] (2026-07-28)
|
||||
|
||||
`spec/CONTRACT_GENESIS_G2B_TUNING.md` charges this crate alone with the raise:
|
||||
`max_supported_major(OperationEnvelopeBlock)` → 3. `SetTuningContext`
|
||||
(`epiphany-ops`) is the sole genesis-tranche payload born at schema major 3 —
|
||||
its carried `epiphany_core::TuningContextSettings` has mandatory (not
|
||||
`Option`-hidden) appends past major 2, so a block carrying one is now born at
|
||||
v3. This is the one-way door the governing plan (`PLAN_GENESIS_OPS.md` §4)
|
||||
warned against burying inside routine work: G2 was split into G2a/G2b
|
||||
precisely so the two major-0 setters (G2a) would not carry this raise, and it
|
||||
lands alone here instead.
|
||||
|
||||
**The doc comment above `max_supported_major` asserted a rationale this rung
|
||||
falsifies, and it had to move with the number, not just the number itself.**
|
||||
The prior text ("Schema major 3 ... does not raise this role: no operation
|
||||
payload embeds the tuning context, so no op block is ever born at v3") is now
|
||||
false. Rewritten, and mutation-verified (`accept_set_doc_no_longer_claims_
|
||||
no_payload_embeds_the_tuning_context`, `bundle.rs`) that the exact stale
|
||||
sentence is absent from the source — not merely superseded by newer prose
|
||||
elsewhere in the file, which a reader skimming only the doc comment could
|
||||
still miss. This is the `binary_format.tex:2373` lesson applied to Rust doc
|
||||
comments, not just the LaTeX companion.
|
||||
|
||||
**One existing test needed a value bump as a direct consequence, not a
|
||||
touch-table item.** `committing_an_unsupported_major_op_root_makes_the_live_
|
||||
bundle_read_only` staged a block at major 3 to exercise the "beyond the
|
||||
accept-set" read-only path; major 3 is now *inside* the accept-set, so the
|
||||
test silently stopped testing what its name claims (it would still pass,
|
||||
vacuously, for the wrong reason) — caught by running the full bundle suite
|
||||
after the raise, not by the touch table, which did not name this test.
|
||||
Moved to major 4, with a comment explaining why 3 no longer works. This is
|
||||
exactly the kind of collateral fix the touch table's "floor, not ceiling"
|
||||
framing anticipates: a file not listed can still need an edit when a change
|
||||
elsewhere makes its assumption stale.
|
||||
|
||||
**No `epiphany-bundle` participation in the never-authored/authored-to-default
|
||||
question (pin 5).** This crate has no visibility into operation semantics —
|
||||
`edit_barriers` and the op-block bytes are opaque to it — so pin 5's
|
||||
seeded-undo discipline is entirely `epiphany-ops`'s concern. This crate's
|
||||
only stake is that the raise is now paid for real, once, by the value that
|
||||
actually needs it.
|
||||
|
|
|
|||
|
|
@ -50,23 +50,26 @@ pub const SUPPORTED_SCHEMA_MAJOR: u16 = 0;
|
|||
/// bound of its per-role accept-set `[0, max]` (Binary Format companion
|
||||
/// §"Schema Major 1", "The accept-set gate").
|
||||
///
|
||||
/// `OperationEnvelopeBlock` admits major 2 (schema major 2 fills the
|
||||
/// `OperationEnvelopeBlock` admits major 3 (schema major 2 fills the
|
||||
/// cross-cutting/staff/metadata bodies its payloads embed; major 1 embedded a
|
||||
/// v1 `CreateRegion`; the reader treats the block bytes opaquely, so it
|
||||
/// parses a higher-major block without decoding the payload). Schema major 3
|
||||
/// (Push 4b tranche 3b-i) does **not** raise this role: no operation payload
|
||||
/// embeds the tuning context, so no op block is ever born at v3. `Snapshot`
|
||||
/// admits major 3 for the acceleration full-`Score` form (decoded through
|
||||
/// the core versioned seam); the canonical BASE carried under the same kind
|
||||
/// must stay major 0, enforced per role. Every other role stays at
|
||||
/// [`SUPPORTED_SCHEMA_MAJOR`] until its own versioned path lands — the
|
||||
/// layout cache, the operation index, and the manifest (carried opaquely,
|
||||
/// parses a higher-major block without decoding the payload). **Schema major
|
||||
/// 3 is raised by genesis tranche G2b** (`spec/CONTRACT_GENESIS_G2B_TUNING.md`):
|
||||
/// `SetTuningContext` is the sole operation payload that embeds the tuning
|
||||
/// context (`epiphany_core::TuningContextSettings`, born at major 3
|
||||
/// unconditionally), so an op block carrying one is now born at v3 — this
|
||||
/// superseded the earlier Push 4b tranche 3b-i note claiming no payload ever
|
||||
/// would. `Snapshot` admits major 3 for the acceleration full-`Score` form
|
||||
/// (decoded through the core versioned seam); the canonical BASE carried
|
||||
/// under the same kind must stay major 0, enforced per role. Every other role
|
||||
/// stays at [`SUPPORTED_SCHEMA_MAJOR`] until its own versioned path lands —
|
||||
/// the layout cache, the operation index, and the manifest (carried opaquely,
|
||||
/// never grows a versioned layout). A chunk above its role's max is not
|
||||
/// admitted; for a **canonical** role that means the bundle opens read-only
|
||||
/// (a lower-major-only reader meeting a newer op block), not a hard reject.
|
||||
pub fn max_supported_major(kind: ChunkKind) -> u16 {
|
||||
match kind {
|
||||
ChunkKind::OperationEnvelopeBlock => 2,
|
||||
ChunkKind::OperationEnvelopeBlock => 3,
|
||||
// The payload-polymorphic Snapshot role: the acceleration
|
||||
// full-`Score` form is decoded through the core versioned seam
|
||||
// (`Score::decode_canonical_versioned`, majors {0,1,2,3}). The
|
||||
|
|
@ -1380,14 +1383,17 @@ mod tests {
|
|||
// CreateRegion), and schema major 2 to major 2 (a block bearing a v2
|
||||
// cross-cutting/staff/metadata value); every other role stays exact-0
|
||||
// until its own versioned path lands, and the manifest stays major 0
|
||||
// forever. Schema major 3 (Push 4b tranche 3b-i, §"Schema Major 3")
|
||||
// raises only the snapshot role — no operation payload embeds the
|
||||
// tuning context, so the op-block role's admission is untouched.
|
||||
// forever. Genesis tranche G2b (`spec/CONTRACT_GENESIS_G2B_TUNING.md`)
|
||||
// raises the op-block role to major 3: `SetTuningContext` is the sole
|
||||
// operation payload that embeds the tuning context, born at major 3
|
||||
// unconditionally, so a block carrying one is now born at v3 — this
|
||||
// supersedes the earlier Push 4b tranche 3b-i note claiming no
|
||||
// payload ever would.
|
||||
assert_eq!(SchemaVersion::V1.major, 1);
|
||||
assert_eq!(SchemaVersion::V2.major, 2);
|
||||
assert_eq!(SchemaVersion::V3.major, 3);
|
||||
// The op-block role admits [0, 2] — unchanged by schema major 3.
|
||||
assert_eq!(max_supported_major(ChunkKind::OperationEnvelopeBlock), 2);
|
||||
// (t4) The op-block role admits [0, 3] as of genesis tranche G2b.
|
||||
assert_eq!(max_supported_major(ChunkKind::OperationEnvelopeBlock), 3);
|
||||
// The snapshot role admits the major-3 acceleration form (decoded
|
||||
// through the core versioned seam); the canonical BASE stays major 0
|
||||
// per role (`mis_stamped_canonical_base`). The remaining roles stay
|
||||
|
|
@ -1401,16 +1407,45 @@ mod tests {
|
|||
assert_eq!(Manifest::SCHEMA.major, 0);
|
||||
}
|
||||
|
||||
/// (t10) `CONTRACT_GENESIS_G2B_TUNING.md` pin 3: the doc comment above
|
||||
/// `max_supported_major` used to assert a rationale G2b falsifies (no
|
||||
/// operation payload reaches the tuning context, so no op block would
|
||||
/// ever reach schema major 3). That claim must be gone from the source,
|
||||
/// not merely superseded in prose elsewhere; a stale rationale beside a
|
||||
/// corrected constant is exactly how `binary_format.tex:2373` decayed.
|
||||
///
|
||||
/// **Mutation:** restore the stale sentence into the doc comment; must
|
||||
/// fail.
|
||||
#[test]
|
||||
fn accept_set_doc_no_longer_claims_no_payload_embeds_the_tuning_context() {
|
||||
let source = include_str!("bundle.rs");
|
||||
let stale_claim: String = ["no operation payload ", "embeds the tuning context"].concat();
|
||||
let stale_consequence: String = ["no op block is ever ", "born at v3"].concat();
|
||||
assert!(
|
||||
!source.contains(&stale_claim),
|
||||
"the doc comment above max_supported_major must not assert the falsified claim anymore"
|
||||
);
|
||||
assert!(
|
||||
!source.contains(&stale_consequence),
|
||||
"the doc comment above max_supported_major must not assert the falsified consequence anymore"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committing_an_unsupported_major_op_root_makes_the_live_bundle_read_only() {
|
||||
// A commit publishes an op block beyond this reader's accept-set (a
|
||||
// forward-compat write): structural validation lets it through, but the
|
||||
// LIVE bundle must go read-only at once — not only on the next reopen —
|
||||
// so no further commit runs against canonical history it cannot parse.
|
||||
//
|
||||
// Major 4, not 3: genesis tranche G2b raised the op-block accept-set
|
||||
// to [0, 3] (`SetTuningContext` is born at major 3), so major 3 is now
|
||||
// admitted and this test's "future major" must move past it to stay
|
||||
// an actual test of the read-only-on-overflow path.
|
||||
let mut bundle = fresh_bundle();
|
||||
let block = StagedChunk::operation_block_versioned(
|
||||
crate::block::encode_block(&[vec![1u8, 2, 3]]),
|
||||
SchemaVersion::new(3, 0),
|
||||
SchemaVersion::new(4, 0),
|
||||
);
|
||||
bundle
|
||||
.commit(&[block], |ctx| {
|
||||
|
|
@ -1426,7 +1461,7 @@ mod tests {
|
|||
);
|
||||
assert!(bundle.anomalies().iter().any(|a| matches!(
|
||||
a,
|
||||
IntegrityAnomaly::UnsupportedCanonicalChunkMajor { schema_major: 3 }
|
||||
IntegrityAnomaly::UnsupportedCanonicalChunkMajor { schema_major: 4 }
|
||||
)));
|
||||
// A further commit against the now-read-only bundle is refused.
|
||||
let more = StagedChunk::operation_block_versioned(
|
||||
|
|
|
|||
|
|
@ -1441,3 +1441,53 @@ in the *containing* `Canvas` walk (`dec_canvas_v0` default-fills the field;
|
|||
itself. So neither op gains a `schema_major()` arm — both fall into the
|
||||
existing `_ => 0` catch-all in `epiphany-ops`. This is unlike G1's
|
||||
`CreateInstrument`, whose carried `Instrument` has mandatory major-2 appends.
|
||||
|
||||
## Genesis tranche G2b — `TuningContextSettings`, the subset type
|
||||
## (2026-07-28)
|
||||
|
||||
`spec/CONTRACT_GENESIS_G2B_TUNING.md` §1 (RATIFIED, resolving
|
||||
`spec/PLAN_GENESIS_OPS.md` §3's open pin) adds `SetTuningContext` to
|
||||
`epiphany-ops`, the G2b rung of the genesis ladder. Unlike G1 and G2a, its
|
||||
carried payload is **not** the full `ScoreTuningContext` — it is a new type,
|
||||
`epiphany_core::TuningContextSettings`, holding exactly the five fields
|
||||
`ScoreTuningContext`'s `Codec` actually walks onto the wire
|
||||
(`default_pitch_space`, `default_tuning_system`, `reference`, `smufl`,
|
||||
`overrides`), in that same order.
|
||||
|
||||
**Why a new type, not the full value or a normalizing construction check.**
|
||||
`ScoreTuningContext`'s `Codec` deliberately drops `accidental_extensions` on
|
||||
encode and default-fills it to `Vec::new()` on decode (the field is staged out
|
||||
of schema major 3). `OperationSet::accept` stores the authored envelope as a
|
||||
**value**, so a `SetTuningContext` carrying the full `ScoreTuningContext`
|
||||
would reduce with `accidental_extensions` intact on the authoring replica and
|
||||
empty on any replica that received the document through serialization — the
|
||||
same document in two graph states, depending only on whether you just
|
||||
authored it. `canonical_value!`'s generated `decode_canonical` cannot catch
|
||||
this: it compares decode → `finish()` → re-encode bytes, never the
|
||||
originating value, so a field that never reaches the bytes is invisible to
|
||||
it.
|
||||
|
||||
Normalization (clearing the field at construction) and reject-on-non-empty
|
||||
were both rejected: normalization makes correctness depend on remembering to
|
||||
clear a field at every construction site, enforced by nothing the compiler or
|
||||
codec can see — a shape this track has been burned by twice already (four
|
||||
stale literal sites at Push 4a, six found during G2a); reject-on-non-empty
|
||||
turns an in-memory-only field into an authoring error for callers who never
|
||||
opted into persistence. The subset type makes the divergence
|
||||
**unrepresentable**: a field that does not exist cannot be set wrongly.
|
||||
|
||||
**No new byte layout.** `TuningContextSettings`'s `Codec` is byte-identical to
|
||||
`ScoreTuningContext`'s existing five-field walk by construction — same five
|
||||
fields, same order, same per-field codecs — asserted directly by
|
||||
`tuning_context_settings_canonical_bytes_match_score_tuning_context`
|
||||
(`codec.rs`). This is a type-level narrowing, not a new wire form, so
|
||||
`canonical_value!` still applies and the G1/G2a payload template is
|
||||
unchanged. One more `canonical_value!` line makes it reachable per-value.
|
||||
|
||||
`SetTuningContext` **does** gain a real `schema_major()` arm returning 3,
|
||||
unconditionally — the opposite of G2a. `ScoreTuningContext`'s wire form is
|
||||
born at schema major 3 and its appends (`smufl`, `overrides`) are mandatory,
|
||||
not `Option`-hidden, so there is no lower-major layout for this payload to
|
||||
fall back to. This is the sole surface among the nine genesis-tranche
|
||||
settings/creates that drags `OperationEnvelopeBlock`'s accept-set from 2 to 3
|
||||
(`epiphany-bundle`'s `DECISIONS.md`).
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ use crate::graph::{
|
|||
SpannerKind, Staff, StaffBasedContent, StaffBracketKind, StaffExtent, StaffGroup,
|
||||
StaffGroupKind, StaffInstance, StaffLineConfiguration, StemDirection, SubBeam,
|
||||
TempoMapReference, TextLineDefinition, Tie, TieClass, TimeExtent, TimeSignature,
|
||||
TimeSignatureDisplay, Timestamp, Tuplet, TupletRatio, UnpitchedMember, ViewDefinition, Voice,
|
||||
VoiceOrigin, Volta,
|
||||
TimeSignatureDisplay, Timestamp, TuningContextSettings, Tuplet, TupletRatio, UnpitchedMember,
|
||||
ViewDefinition, Voice, VoiceOrigin, Volta,
|
||||
};
|
||||
use crate::ids::{
|
||||
AnalysisLayerId, AnalyticalAnnotationId, BarlineAlignmentGroupId, BeamId, ChordSymbolId,
|
||||
|
|
@ -1984,6 +1984,40 @@ impl Codec for ScoreTuningContext {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// `TuningContextSettings` (genesis tranche G2b,
|
||||
// `spec/CONTRACT_GENESIS_G2B_TUNING.md` §1): the authored subset of
|
||||
// `ScoreTuningContext` that `SetTuningContext` carries — exactly its five
|
||||
// wire-bearing fields, in the codec's existing order. This walk is
|
||||
// **byte-identical** to `ScoreTuningContext`'s `enc`/`dec` above by
|
||||
// construction (same five fields, same order, same per-field codecs) — a
|
||||
// type-level narrowing, not a new wire form. The identity is asserted by a
|
||||
// round-trip test just below
|
||||
// (`tuning_context_settings_canonical_bytes_match_score_tuning_context`).
|
||||
impl Codec for TuningContextSettings {
|
||||
fn enc(&self, out: &mut Vec<u8>) {
|
||||
self.default_pitch_space.enc(out);
|
||||
self.default_tuning_system.enc(out);
|
||||
self.reference.enc(out);
|
||||
self.smufl.enc(out);
|
||||
self.overrides.enc(out);
|
||||
}
|
||||
fn dec(r: &mut Reader<'_>) -> Result<Self> {
|
||||
let default_pitch_space = Codec::dec(r)?;
|
||||
let default_tuning_system = Codec::dec(r)?;
|
||||
let reference = Codec::dec(r)?;
|
||||
let smufl = Codec::dec(r)?;
|
||||
let overrides = Codec::dec(r)?;
|
||||
Ok(TuningContextSettings {
|
||||
default_pitch_space,
|
||||
default_tuning_system,
|
||||
reference,
|
||||
smufl,
|
||||
overrides,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Schema major 2: the cross-cutting bodies filled (appended fields); the
|
||||
// frozen prior layouts are read by the `dec_*_v1` sub-decoders.
|
||||
struct_codec!(Slur {
|
||||
|
|
@ -3545,6 +3579,12 @@ canonical_value! {
|
|||
// own — see the contract's "Why these two" table.
|
||||
CanvasLayoutDefaults,
|
||||
SpellingPrecedence,
|
||||
// Genesis tranche G2b (`CONTRACT_GENESIS_G2B_TUNING.md` §1) —
|
||||
// SetTuningContext embeds this authored subset, not the full
|
||||
// `ScoreTuningContext`. Its `Codec` above is byte-identical to
|
||||
// `ScoreTuningContext`'s five-field walk; this makes that layout
|
||||
// reachable per-value, exactly as `ScoreTuningContext` above is.
|
||||
TuningContextSettings,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -3873,6 +3913,65 @@ mod tests {
|
|||
assert!(back.accidental_extensions.is_empty());
|
||||
}
|
||||
|
||||
/// Genesis tranche G2b (`CONTRACT_GENESIS_G2B_TUNING.md` §1, pin/touch-row
|
||||
/// 3): `TuningContextSettings`'s canonical encoding is **byte-identical**
|
||||
/// to `ScoreTuningContext`'s existing five-field walk — a type-level
|
||||
/// narrowing, not a new wire form. Proven directly against non-default
|
||||
/// values in all five fields, not just the default.
|
||||
///
|
||||
/// **Mutation:** reorder two fields in `TuningContextSettings::enc` (e.g.
|
||||
/// swap `smufl` and `overrides`) without making the matching change in
|
||||
/// `ScoreTuningContext::enc`; must fail.
|
||||
#[test]
|
||||
fn tuning_context_settings_canonical_bytes_match_score_tuning_context() {
|
||||
use crate::accidental::{SmuflVersion, SmuflVersionRequirement};
|
||||
use crate::graph::{ScoreTuningContext, TuningContextSettings};
|
||||
use crate::ids::{ReplicaId, VoiceId};
|
||||
use crate::pitch::TuningSystemId;
|
||||
use crate::tuning::{TuningOverride, TuningScope};
|
||||
|
||||
let full = ScoreTuningContext {
|
||||
smufl: SmuflVersionRequirement {
|
||||
minimum: SmuflVersion::from_decimal(1, "12").unwrap(),
|
||||
authored_against: SmuflVersion::from_decimal(1, "18").unwrap(),
|
||||
},
|
||||
overrides: vec![TuningOverride {
|
||||
scope: TuningScope::Voice(VoiceId::new(ReplicaId(1), 7)),
|
||||
pitch_space: None,
|
||||
tuning_system: Some(TuningSystemId::new("tet-19")),
|
||||
reference: None,
|
||||
}],
|
||||
..ScoreTuningContext::default()
|
||||
};
|
||||
let settings = TuningContextSettings {
|
||||
default_pitch_space: full.default_pitch_space.clone(),
|
||||
default_tuning_system: full.default_tuning_system.clone(),
|
||||
reference: full.reference.clone(),
|
||||
smufl: full.smufl,
|
||||
overrides: full.overrides.clone(),
|
||||
};
|
||||
|
||||
let mut full_bytes = Vec::new();
|
||||
full.enc(&mut full_bytes);
|
||||
let mut settings_bytes = Vec::new();
|
||||
settings.enc(&mut settings_bytes);
|
||||
assert_eq!(
|
||||
settings_bytes, full_bytes,
|
||||
"TuningContextSettings must encode byte-identically to ScoreTuningContext's five-field walk"
|
||||
);
|
||||
|
||||
let decoded =
|
||||
TuningContextSettings::dec(&mut Reader::new(&settings_bytes)).expect("decodes");
|
||||
assert_eq!(decoded.default_pitch_space, settings.default_pitch_space);
|
||||
assert_eq!(
|
||||
decoded.default_tuning_system,
|
||||
settings.default_tuning_system
|
||||
);
|
||||
assert_eq!(decoded.reference, settings.reference);
|
||||
assert_eq!(decoded.smufl, settings.smufl);
|
||||
assert_eq!(decoded.overrides, settings.overrides);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generator_scores_round_trip() {
|
||||
for seed in 0..200u64 {
|
||||
|
|
|
|||
|
|
@ -1705,6 +1705,49 @@ impl Default for ScoreTuningContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// The **authored subset** of [`ScoreTuningContext`] that `SetTuningContext`
|
||||
/// (genesis tranche G2b, `spec/CONTRACT_GENESIS_G2B_TUNING.md` §1) carries:
|
||||
/// exactly the five fields `ScoreTuningContext`'s `Codec` actually walks onto
|
||||
/// the wire (`codec.rs`'s hand-written `impl Codec for ScoreTuningContext`),
|
||||
/// in that same order. `accidental_extensions` is **not** a field of this
|
||||
/// type — it is deliberately absent, not cleared or normalized.
|
||||
///
|
||||
/// **Why a new type rather than reusing `ScoreTuningContext` directly.**
|
||||
/// `ScoreTuningContext`'s `Codec` drops `accidental_extensions` on encode and
|
||||
/// default-fills it to `Vec::new()` on decode: the field is staged out of
|
||||
/// schema major 3 and stays in-memory-only. If an operation carried the full
|
||||
/// `ScoreTuningContext`, `OperationSet::accept` would store the authored
|
||||
/// envelope as a **value** (with `accidental_extensions` intact), while a
|
||||
/// document reloaded from bytes would decode the same envelope with that
|
||||
/// field reconstructed as empty — two divergent graph states from one
|
||||
/// document, observable only by whether you just authored it or reloaded it.
|
||||
/// A field that never reaches the wire cannot be caught by
|
||||
/// `canonical_value!`'s decode → `finish()` → re-encode byte comparison,
|
||||
/// because that comparison never touches the originating value.
|
||||
///
|
||||
/// This type makes the divergence **unrepresentable**: it has no
|
||||
/// `accidental_extensions` field to diverge on. `SetTuningContext`'s
|
||||
/// reduction writes exactly these five fields onto `score.tuning_context` and
|
||||
/// leaves `accidental_extensions` untouched, preserving whatever the graph
|
||||
/// already held. This is a **type-level narrowing, not a new wire form** —
|
||||
/// `TuningContextSettings`'s canonical encoding is byte-for-byte identical to
|
||||
/// `ScoreTuningContext`'s existing five-field walk (asserted in
|
||||
/// `codec.rs`), so the packet designs no new layout.
|
||||
///
|
||||
/// When a later schema major lands `accidental_extensions` on the wire, this
|
||||
/// type gains the field like any other major payload change — the same cost
|
||||
/// normalization would have paid, but without making the never-authored /
|
||||
/// authored-to-default distinction depend on a clearing discipline enforced
|
||||
/// by nothing the compiler can see.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct TuningContextSettings {
|
||||
pub default_pitch_space: PitchSpaceId,
|
||||
pub default_tuning_system: TuningSystemId,
|
||||
pub reference: ReferencePitch,
|
||||
pub smufl: crate::accidental::SmuflVersionRequirement,
|
||||
pub overrides: Vec<crate::tuning::TuningOverride>,
|
||||
}
|
||||
|
||||
/// The root object of a score (Chapter 5 §"Top-Level Score Structure").
|
||||
///
|
||||
/// This carries the full Chapter 5 top-level shape. The invariant-bearing
|
||||
|
|
|
|||
|
|
@ -160,8 +160,8 @@ pub use graph::{
|
|||
SoundConfiguration, SpaceUnit, SpanStyle, Spanner, SpannerKind, Staff, StaffBasedContent,
|
||||
StaffBracketKind, StaffExtent, StaffGroup, StaffGroupKind, StaffInstance,
|
||||
StaffLineConfiguration, StemDirection, SubBeam, TempoMapReference, TextLineDefinition, Tie,
|
||||
TieClass, TimeExtent, TimeSignature, TimeSignatureDisplay, Timestamp, Tuplet, TupletRatio,
|
||||
UnpitchedMember, ViewDefinition, Voice, VoiceOrigin, Volta,
|
||||
TieClass, TimeExtent, TimeSignature, TimeSignatureDisplay, Timestamp, TuningContextSettings,
|
||||
Tuplet, TupletRatio, UnpitchedMember, ViewDefinition, Voice, VoiceOrigin, Volta,
|
||||
};
|
||||
|
||||
pub use tempo::{
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use crate::graph::{
|
|||
AnnotationAnchor, DecompositionSource, EventOrderingDAG, GestureAnchoring, KeySignature,
|
||||
MetadataValue, RegionContent, RegionTimeModel, RepeatKind, ScoreTuningContext,
|
||||
SoundConfiguration, SpaceUnit, SpannerKind, StaffGroupKind, TieClass, TimeSignature,
|
||||
TimeSignatureDisplay, Timestamp, TupletRatio, VoiceOrigin,
|
||||
TimeSignatureDisplay, Timestamp, TuningContextSettings, TupletRatio, VoiceOrigin,
|
||||
};
|
||||
use crate::textvalue::{kebab, Sexp, TextError, TextValue};
|
||||
use crate::textvalue_impls::class_of;
|
||||
|
|
@ -447,6 +447,38 @@ impl TextValue for ScoreTuningContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// The **authored subset** of `ScoreTuningContext` that `SetTuningContext`
|
||||
/// carries (genesis tranche G2b, `spec/CONTRACT_GENESIS_G2B_TUNING.md` §1) —
|
||||
/// the same five fields, in the same order, as `ScoreTuningContext`'s
|
||||
/// projection above; `accidental_extensions` has no field here to project.
|
||||
impl TextValue for TuningContextSettings {
|
||||
fn project(&self) -> Sexp {
|
||||
Sexp::List(vec![
|
||||
Sexp::Symbol(kebab("TuningContextSettings")),
|
||||
self.default_pitch_space.project(),
|
||||
self.default_tuning_system.project(),
|
||||
self.reference.project(),
|
||||
self.smufl.project(),
|
||||
self.overrides.project(),
|
||||
])
|
||||
}
|
||||
fn parse(s: &Sexp) -> Result<Self, TextError> {
|
||||
let fields = s.expect_struct(&kebab("TuningContextSettings"), 5)?;
|
||||
let default_pitch_space = TextValue::parse(&fields[0])?;
|
||||
let default_tuning_system = TextValue::parse(&fields[1])?;
|
||||
let reference = TextValue::parse(&fields[2])?;
|
||||
let smufl = TextValue::parse(&fields[3])?;
|
||||
let overrides = TextValue::parse(&fields[4])?;
|
||||
Ok(TuningContextSettings {
|
||||
default_pitch_space,
|
||||
default_tuning_system,
|
||||
reference,
|
||||
smufl,
|
||||
overrides,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Tagged unions.
|
||||
// ===========================================================================
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
//! container is what the barrier's **scope** is for, and scope is matched
|
||||
//! against the target's real containment, precisely.
|
||||
//! * **Score-level operations** (`SetMetadata`, the transaction descriptor,
|
||||
//! `SetCanvasLayoutDefaults`, `SetSpellingPrecedence`) name no graph object:
|
||||
//! only a score-wide barrier (empty
|
||||
//! `SetCanvasLayoutDefaults`, `SetSpellingPrecedence`, `SetTuningContext`)
|
||||
//! name no graph object: only a score-wide barrier (empty
|
||||
//! `affected_object_kinds`, `WholeScore`/`TuningContext`/`Registered` scope)
|
||||
//! can match them.
|
||||
//! * **Extension-defined operations** (`OperationKind::Registered`) carry a
|
||||
|
|
@ -469,7 +469,11 @@ pub(crate) fn subjects_of(kind: &OperationKind, score: &Score) -> BarrierSubject
|
|||
OperationKind::SetMetadata(_)
|
||||
| OperationKind::DeclareTransaction(_)
|
||||
| OperationKind::SetCanvasLayoutDefaults(_)
|
||||
| OperationKind::SetSpellingPrecedence(_) => BarrierSubjects::ScoreWide,
|
||||
| OperationKind::SetSpellingPrecedence(_)
|
||||
// Genesis tranche G2b (`CONTRACT_GENESIS_G2B_TUNING.md`, one-time
|
||||
// authorization): another score-singleton field overwrite naming no
|
||||
// resolvable region or object, exactly like `SetMetadata`.
|
||||
| OperationKind::SetTuningContext(_) => BarrierSubjects::ScoreWide,
|
||||
OperationKind::CreateRepeatStructure(op) => one(
|
||||
TypedObjectId::RepeatStructure(op.repeat_structure_id()),
|
||||
repeat_context(score, &op.repeat),
|
||||
|
|
|
|||
|
|
@ -1153,28 +1153,28 @@ mod tests {
|
|||
tag: 7
|
||||
})
|
||||
);
|
||||
// Operation-kind tag 34 is one past the vocabulary (the Phase-3 ops
|
||||
// Operation-kind tag 35 is one past the vocabulary (the Phase-3 ops
|
||||
// tranche appended 24..=27, the repeat pair 28/29, `TransposeInterval`
|
||||
// 30, genesis G1's `CreateInstrument` 31, genesis G2a's
|
||||
// `SetCanvasLayoutDefaults` 32 and `SetSpellingPrecedence` 33;
|
||||
// encodings are append-only).
|
||||
// `SetCanvasLayoutDefaults` 32 and `SetSpellingPrecedence` 33,
|
||||
// genesis G2b's `SetTuningContext` 34; encodings are append-only).
|
||||
//
|
||||
// This assertion named 30 until Push 5 / P4, 31 until genesis G1, and
|
||||
// 32 until genesis G2a — each time, by then, the number had become a
|
||||
// real kind, so the test was pinning a bug: a barrier that prohibited
|
||||
// the new operation encoded fine and would not read back. It must be
|
||||
// bumped by every tranche that appends a tag, and it is deliberately a
|
||||
// literal rather than `PAYLOAD_FREE.len()` so the bump is a conscious
|
||||
// act.
|
||||
// This assertion named 30 until Push 5 / P4, 31 until genesis G1, 32
|
||||
// until genesis G2a, and 34 until genesis G2b — each time, by then,
|
||||
// the number had become a real kind, so the test was pinning a bug: a
|
||||
// barrier that prohibited the new operation encoded fine and would
|
||||
// not read back. It must be bumped by every tranche that appends a
|
||||
// tag, and it is deliberately a literal rather than
|
||||
// `PAYLOAD_FREE.len()` so the bump is a conscious act.
|
||||
let mut bytes = vec![0u8];
|
||||
bytes.extend(set_blob(&[]));
|
||||
bytes.extend(set_blob(&[vec![34u8]]));
|
||||
bytes.extend(set_blob(&[vec![35u8]]));
|
||||
bytes.push(0);
|
||||
assert_eq!(
|
||||
EditBarrier::decode_canonical_bytes(&bytes),
|
||||
Err(BarrierDecodeError::InvalidTag {
|
||||
kind: "OperationKindTag",
|
||||
tag: 34
|
||||
tag: 35
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1888,3 +1888,72 @@ surface for a leak to appear on even before checking the hash. `gen_payload`
|
|||
arms appended, reshuffling the seeded RNG stream exactly as at Phase D, Push
|
||||
4a, and G1; the digest moved and was re-pinned with that reasoning recorded
|
||||
in the test's own comment.
|
||||
|
||||
## Genesis tranche G2b — `SetTuningContext`, kind/tag 34, the accept-set raise
|
||||
## (2026-07-28)
|
||||
|
||||
`spec/CONTRACT_GENESIS_G2B_TUNING.md` lands the third rung: the sole genesis
|
||||
payload born at schema major 3, and the one surface among the nine
|
||||
settings/creates that drags `OperationEnvelopeBlock`'s accept-set from 2 to 3.
|
||||
Its carried type is **not** the full `ScoreTuningContext` but the new
|
||||
`epiphany_core::TuningContextSettings` — the authored subset of exactly the
|
||||
five wire-bearing fields; see that crate's `DECISIONS.md` for why (the
|
||||
never-authored / authored-to-default divergence a full-value payload would
|
||||
have introduced).
|
||||
|
||||
`set_tuning_context` copies `set_metadata` structurally — advisory LWW, no
|
||||
conflict, no idempotence short-circuit — but writes onto `score.tuning_context`
|
||||
**field by field** rather than by whole-struct assignment, because the payload
|
||||
carries a different (narrower) type than the field it targets:
|
||||
`accidental_extensions` is left untouched in both the ordinary apply path and
|
||||
undo's restoration-apply path. This is the one place in this rung's reduction
|
||||
code that could not be a literal copy-paste of `set_metadata`/
|
||||
`set_canvas_layout_defaults`/`set_spelling_precedence` — those three write
|
||||
`score.<field> = op.<value>.clone()` in one line because the carried type
|
||||
*is* the field's type; here it is a strict subset, so the assignment is five
|
||||
lines, one per subset field, deliberately omitting `accidental_extensions`.
|
||||
|
||||
**Pin 5, verified rather than assumed.** An earlier draft of the governing
|
||||
contract required undo to distinguish "never authored" from
|
||||
"authored-to-default" — `spec/PLAN_GENESIS_OPS.md` §5 trap 5, withdrawn
|
||||
2026-07-28 before this packet started. `tuning_context_chain` is seeded from
|
||||
`Score::empty`'s default in `seed_from_graph`, exactly as `metadata_chain` is,
|
||||
so the first undo of a `SetTuningContext` write yields
|
||||
`Restore(Some(Predecessor::Base(seeded)))` regardless of whether the seed (or
|
||||
the write) happened to equal the type default. Test t7 asserts the two cases
|
||||
(default seed, non-default seed) restore identically — proving the
|
||||
distinction unobservable, not merely failing to test for it.
|
||||
|
||||
`schema_major()` gains a real, unconditional arm returning 3 — the opposite of
|
||||
G2a's "no arm" pin, because `ScoreTuningContext`'s `smufl`/`overrides` appends
|
||||
are mandatory (not `Option`-hidden), so no lower-major layout for this payload
|
||||
exists (a `CreateInstrument`-shaped arm, not a `CreateRegion`-shaped one).
|
||||
|
||||
**The accept-set raise is entirely `epiphany-bundle`'s change** (see that
|
||||
crate's `DECISIONS.md`); this crate's contribution is making `SetTuningContext`
|
||||
the value that requires it.
|
||||
|
||||
**Boundary crossings, budgeted as the contract's one-time authorization.**
|
||||
`editor-core/src/barriers.rs::subjects_of` gains kind 34 in the same
|
||||
score-wide `SetMetadata(_) | DeclareTransaction(_) | ...` arm G2a's two kinds
|
||||
joined — a score-level field overwrite with no resolvable region or object.
|
||||
`layout-ir/src/barrier.rs`'s "one past the vocabulary" literal moves 34→35.
|
||||
`testkit/src/generators.rs`'s `rng.below` bound and
|
||||
`testkit/tests/text_projection_grammar.rs`'s kind count both move by one.
|
||||
`testkit/src/layout_stub.rs`'s derived `PAYLOAD_FREE`-based draw needed no
|
||||
edit — confirmed, not assumed, per the contract's explicit ask (touch row 21)
|
||||
— because it derives from the vocabulary macro rather than being hand-extended.
|
||||
|
||||
**Decode vector pinned to literal bytes** (`vectors.rs`,
|
||||
`set_tuning_context_envelope_decode_vector_is_pinned_to_literal_bytes`), not
|
||||
merely round-tripped — the plan's trap 4 (a self-consistent discriminant
|
||||
reorder once passed 1283 tests and 8/8 conformance). Mutation-verified: a
|
||||
one-byte corruption of the pinned discriminant produces
|
||||
`InvalidTag { kind: "OperationKind", tag: 99 }`, observed directly rather than
|
||||
inferred.
|
||||
|
||||
**No pruning or compaction is implemented, enabled, or prepared here** — pin
|
||||
9's explicit non-goal. The op log is now the sole canonical carrier of the
|
||||
tuning context (the canonical base embeds no graph value for any `Score`
|
||||
field), which is exactly why a future prune must stay blocked on disposition
|
||||
C; this packet adds no `fn prune` and no scaffolding toward one.
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ use epiphany_core::{
|
|||
CanvasLayoutDefaults, EventId, Instrument, InstrumentId, MetricGrid, MusicalPosition,
|
||||
OperationId, PitchId, PitchSpelling, RegionId, RegionTimeModel, RepeatStructureId, ReplicaId,
|
||||
ScoreMetadata, SpellingPrecedence, Staff, StaffInstance, StaffInstanceId,
|
||||
StaffLineConfiguration, TimeAnchor, TimeSignature, TranspositionInterval, TupletId,
|
||||
TypedObjectId, Voice, VoiceId, WallClockTime,
|
||||
StaffLineConfiguration, TimeAnchor, TimeSignature, TranspositionInterval,
|
||||
TuningContextSettings, TupletId, TypedObjectId, Voice, VoiceId, WallClockTime,
|
||||
};
|
||||
use epiphany_determinism::{CanonicalDecode, CanonicalEncode};
|
||||
|
||||
|
|
@ -596,6 +596,9 @@ fn operation_kind(r: &mut Reader<'_>) -> Result<OperationKind> {
|
|||
33 => OperationKind::SetSpellingPrecedence(SetSpellingPrecedenceOp {
|
||||
precedence: value::<SpellingPrecedence>(r, "SpellingPrecedence")?,
|
||||
}),
|
||||
34 => OperationKind::SetTuningContext(SetTuningContextOp {
|
||||
settings: value::<TuningContextSettings>(r, "TuningContextSettings")?,
|
||||
}),
|
||||
tag => {
|
||||
return Err(EnvelopeDecodeError::InvalidTag {
|
||||
kind: "OperationKind",
|
||||
|
|
@ -895,6 +898,11 @@ pub(crate) mod tests {
|
|||
precedence: valuegen::spelling_precedence(1),
|
||||
})
|
||||
}
|
||||
OperationKindTag::SetTuningContext => {
|
||||
OperationKind::SetTuningContext(SetTuningContextOp {
|
||||
settings: valuegen::tuning_context_settings(1),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ fn pitch(n: u64) -> PitchId {
|
|||
|
||||
/// Generates a random payload over the shared id space.
|
||||
fn gen_payload(rng: &mut SplitMix64) -> OperationPayload {
|
||||
let kind = match rng.below(31) {
|
||||
let kind = match rng.below(32) {
|
||||
0 => {
|
||||
let voice = VoiceId::new(ReplicaId(7), rng.below(3));
|
||||
let position = MusicalPosition(RationalTime::from_int(rng.below(4) as i32));
|
||||
|
|
@ -300,6 +300,10 @@ fn gen_payload(rng: &mut SplitMix64) -> OperationPayload {
|
|||
30 => OperationKind::SetSpellingPrecedence(crate::payload::SetSpellingPrecedenceOp {
|
||||
precedence: valuegen::spelling_precedence(rng.below(3) as u8),
|
||||
}),
|
||||
// Genesis tranche G2b: the sole genesis payload born at schema major 3.
|
||||
31 => OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: valuegen::tuning_context_settings(rng.below(3) as u8),
|
||||
}),
|
||||
_ => OperationKind::SetStaffLayout(SetStaffLayoutOp {
|
||||
staff_instance: StaffInstanceId::new(ReplicaId(7), rng.below(3)),
|
||||
instrument_override: None,
|
||||
|
|
|
|||
|
|
@ -132,8 +132,9 @@ pub use payload::{
|
|||
ModifyIdentifiedPitchOp, OperationKind, OperationKindTag, OperationPayload, PositionRemapping,
|
||||
ResolveConflictPayload, ResolveEquivocationPayload, RespellPitchOp, SetCanvasLayoutDefaultsOp,
|
||||
SetMetadataOp, SetMetricGridOp, SetSpellingPrecedenceOp, SetStaffLayoutOp, SetTempoSegmentOp,
|
||||
SetTimeSignatureOp, SetUserPageBreakOp, SetUserSystemBreakOp, TransactionCategory,
|
||||
TransactionDescriptor, TransposeIntervalOp, TransposeOp, TupletCompensation,
|
||||
SetTimeSignatureOp, SetTuningContextOp, SetUserPageBreakOp, SetUserSystemBreakOp,
|
||||
TransactionCategory, TransactionDescriptor, TransposeIntervalOp, TransposeOp,
|
||||
TupletCompensation,
|
||||
};
|
||||
pub use reduce::{
|
||||
canonical_reduction_order, GraphMaterialization, MaterializedState, ObjectState, PendingReason,
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ fn project_kind(kind: &OperationKind) -> V0OperationKind {
|
|||
OperationKind::SetSpellingPrecedence(op) => {
|
||||
V0OperationKind::SetSpellingPrecedence(op.clone())
|
||||
}
|
||||
// Genesis tranche G2b: born past v0; projected verbatim.
|
||||
OperationKind::SetTuningContext(op) => V0OperationKind::SetTuningContext(op.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,6 +352,8 @@ fn migrate_kind(kind: &V0OperationKind, context: &Score) -> Result<OperationKind
|
|||
V0OperationKind::SetSpellingPrecedence(op) => {
|
||||
OperationKind::SetSpellingPrecedence(op.clone())
|
||||
}
|
||||
// Genesis tranche G2b: identity round-trip (no lossy v0 form).
|
||||
V0OperationKind::SetTuningContext(op) => OperationKind::SetTuningContext(op.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ use epiphany_core::{
|
|||
OperationId, Pitch, PitchId, PitchSpelling, Region, RegionId, RegionTimeModel, RepeatStructure,
|
||||
RepeatStructureId, Rest, ScoreMetadata, Slur, Spanner, SpellingPrecedence, Staff, StaffId,
|
||||
StaffInstance, StaffInstanceId, StaffLineConfiguration, TempoSegment, Tie, TimeAnchor,
|
||||
TimeSignature, TransactionId, TranspositionInterval, TupletId, TypedObjectId, Voice, VoiceId,
|
||||
TimeSignature, TransactionId, TranspositionInterval, TuningContextSettings, TupletId,
|
||||
TypedObjectId, Voice, VoiceId,
|
||||
};
|
||||
use epiphany_determinism::{
|
||||
sorted_canonical, CanonicalDecode, CanonicalEncode, CanonicalSet, DecodeError,
|
||||
|
|
@ -263,6 +264,15 @@ pub enum OperationKind {
|
|||
/// Overwrite the score's spelling precedence (later-in-canonical-order
|
||||
/// wins).
|
||||
SetSpellingPrecedence(SetSpellingPrecedenceOp),
|
||||
// --- Genesis tranche G2b (`spec/CONTRACT_GENESIS_G2B_TUNING.md`): the
|
||||
// sole genesis payload born at schema major 3. Discriminant extends
|
||||
// additively past 33.
|
||||
// ---
|
||||
/// Overwrite the score's tuning context settings (later-in-canonical-
|
||||
/// order wins). Carries [`TuningContextSettings`], the authored subset of
|
||||
/// `ScoreTuningContext` — `accidental_extensions` is not on the wire and
|
||||
/// is left untouched by reduction (contract §1).
|
||||
SetTuningContext(SetTuningContextOp),
|
||||
}
|
||||
|
||||
impl OperationKind {
|
||||
|
|
@ -291,6 +301,13 @@ impl OperationKind {
|
|||
| OperationKind::CreateStaff(_)
|
||||
| OperationKind::SetMetadata(_)
|
||||
| OperationKind::CreateInstrument(_) => 2,
|
||||
// Genesis tranche G2b (contract pin 2): unconditionally 3.
|
||||
// `ScoreTuningContext`'s `smufl`/`overrides` appends are
|
||||
// mandatory, not `Option`-hidden, so there is no lower-major
|
||||
// layout for this payload — a `CreateInstrument`-shaped arm, not
|
||||
// a `CreateRegion`-shaped one. The sole surface among the nine
|
||||
// genesis settings/creates that raises the op-block accept-set.
|
||||
OperationKind::SetTuningContext(_) => 3,
|
||||
// Value-dependent: the embedded StaffLineConfiguration rides an
|
||||
// Option; None encodes byte-identically to the prior major.
|
||||
OperationKind::CreateRegion(op) => {
|
||||
|
|
@ -368,6 +385,9 @@ impl OperationKind {
|
|||
// by the same accident as above, not by rule.
|
||||
OperationKind::SetCanvasLayoutDefaults(_) => 32,
|
||||
OperationKind::SetSpellingPrecedence(_) => 33,
|
||||
// Genesis tranche G2b; appended past 33. Coincides with tag 34 by
|
||||
// the same accident as G1/G2a, not by rule.
|
||||
OperationKind::SetTuningContext(_) => 34,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -424,6 +444,9 @@ impl OperationKind {
|
|||
OperationKind::SetCanvasLayoutDefaults(_) | OperationKind::SetSpellingPrecedence(_) => {
|
||||
Some(9)
|
||||
}
|
||||
// Minor 10 (Genesis tranche G2b), ratified
|
||||
// `spec/PLAN_GMINOR_SCHEMA_MINOR.md` §4.
|
||||
OperationKind::SetTuningContext(_) => Some(10),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -471,6 +494,7 @@ impl OperationKind {
|
|||
OperationKind::CreateInstrument(_) => OperationKindTag::CreateInstrument,
|
||||
OperationKind::SetCanvasLayoutDefaults(_) => OperationKindTag::SetCanvasLayoutDefaults,
|
||||
OperationKind::SetSpellingPrecedence(_) => OperationKindTag::SetSpellingPrecedence,
|
||||
OperationKind::SetTuningContext(_) => OperationKindTag::SetTuningContext,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -516,6 +540,7 @@ impl CanonicalEncode for OperationKind {
|
|||
OperationKind::CreateInstrument(op) => op.encode_canonical(out),
|
||||
OperationKind::SetCanvasLayoutDefaults(op) => op.encode_canonical(out),
|
||||
OperationKind::SetSpellingPrecedence(op) => op.encode_canonical(out),
|
||||
OperationKind::SetTuningContext(op) => op.encode_canonical(out),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -570,6 +595,8 @@ pub enum OperationKindTag {
|
|||
SetCanvasLayoutDefaults,
|
||||
/// Genesis tranche G2a.
|
||||
SetSpellingPrecedence,
|
||||
/// Genesis tranche G2b.
|
||||
SetTuningContext,
|
||||
}
|
||||
|
||||
/// The discriminant of [`OperationKindTag::Registered`], the one tag that
|
||||
|
|
@ -684,6 +711,7 @@ operation_kind_tag_vocabulary! {
|
|||
CreateInstrument = 31 => "create-instrument" @ Some(8),
|
||||
SetCanvasLayoutDefaults = 32 => "set-canvas-layout-defaults" @ Some(9),
|
||||
SetSpellingPrecedence = 33 => "set-spelling-precedence" @ Some(9),
|
||||
SetTuningContext = 34 => "set-tuning-context" @ Some(10),
|
||||
}
|
||||
|
||||
impl CanonicalEncode for OperationKindTag {
|
||||
|
|
@ -1650,6 +1678,30 @@ impl CanonicalEncode for SetSpellingPrecedenceOp {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Genesis tranche G2b (`spec/CONTRACT_GENESIS_G2B_TUNING.md`): the sole
|
||||
// genesis payload born at schema major 3. LWW field-overwrite, the same
|
||||
// `SetMetadata` discipline. ---
|
||||
|
||||
/// Overwrite the score's tuning context settings (operation_catalog
|
||||
/// §SetTuningContext). Carries [`TuningContextSettings`] — the **authored
|
||||
/// subset** of `ScoreTuningContext` (contract §1): exactly the five
|
||||
/// wire-bearing fields, in the codec's existing order.
|
||||
/// `accidental_extensions` is not a field of the carried type and is never
|
||||
/// touched by reduction, which writes only these five fields onto
|
||||
/// `score.tuning_context`. The score-singleton field-overwrite is *advisory*
|
||||
/// last-writer-wins — the latest write in canonical order silently wins and
|
||||
/// no conflict is recorded, exactly `SetMetadata`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct SetTuningContextOp {
|
||||
pub settings: TuningContextSettings,
|
||||
}
|
||||
|
||||
impl CanonicalEncode for SetTuningContextOp {
|
||||
fn encode_canonical(&self, out: &mut Vec<u8>) {
|
||||
push_lp_bytes(out, &self.settings.canonical_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
/// Set, replace, or (`None`) remove the single meter change at the anchor's
|
||||
/// resolved musical position in a region's default metric grid
|
||||
/// (operation_catalog §"Meter and Tempo Overwrites"). Carries the full
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ use epiphany_core::{
|
|||
SpellingDirective, SpellingPrecedence, SpellingScope, SpellingSource, Staff, StaffId,
|
||||
StaffInstance, StaffInstanceId, StaffLineConfiguration, TempoMap, TempoSegment, TempoShape,
|
||||
TimeAnchor, TimeSignature, TimeSignatureId, TransactionId, TransposeRefusal,
|
||||
TranspositionInterval, TypedObjectId, Voice, VoiceId, VoiceOrigin,
|
||||
TranspositionInterval, TuningContextSettings, TypedObjectId, Voice, VoiceId, VoiceOrigin,
|
||||
};
|
||||
use epiphany_determinism::CanonicalEncode;
|
||||
|
||||
|
|
@ -63,7 +63,8 @@ use crate::payload::{
|
|||
InsertIdentifiedPitchOp, ModifyCrossCuttingOp, ModifyEventOp, ModifyIdentifiedPitchOp,
|
||||
OperationKind, OperationPayload, RespellPitchOp, SetCanvasLayoutDefaultsOp, SetMetadataOp,
|
||||
SetMetricGridOp, SetSpellingPrecedenceOp, SetStaffLayoutOp, SetTempoSegmentOp,
|
||||
SetTimeSignatureOp, SetUserPageBreakOp, TransposeIntervalOp, TransposeOp, TupletCompensation,
|
||||
SetTimeSignatureOp, SetTuningContextOp, SetUserPageBreakOp, TransposeIntervalOp, TransposeOp,
|
||||
TupletCompensation,
|
||||
};
|
||||
use crate::stamp::StampTuple;
|
||||
use crate::support::{ObjectKind, SerializedCanonicalInputs};
|
||||
|
|
@ -851,6 +852,11 @@ enum ValueRestoration {
|
|||
SpellingPrecedence {
|
||||
value: Option<SpellingPrecedence>,
|
||||
},
|
||||
/// Genesis tranche G2b. The subset type — `accidental_extensions` is not
|
||||
/// part of it and is never touched by restoration.
|
||||
TuningContext {
|
||||
value: Option<TuningContextSettings>,
|
||||
},
|
||||
MetricGrid {
|
||||
region: RegionId,
|
||||
value: Option<MetricGrid>,
|
||||
|
|
@ -972,6 +978,11 @@ struct Reducer<'a> {
|
|||
// the pre-operational value.
|
||||
canvas_layout_defaults_chain: WriteChain<CanvasLayoutDefaults>,
|
||||
spelling_precedence_chain: WriteChain<SpellingPrecedence>,
|
||||
// Genesis tranche G2b (`CONTRACT_GENESIS_G2B_TUNING.md` pin 5): same
|
||||
// discipline, carrying the **subset** type — `accidental_extensions`
|
||||
// never participates in undo, because no operation ever writes it and
|
||||
// undo must leave it exactly as it found it.
|
||||
tuning_context_chain: WriteChain<TuningContextSettings>,
|
||||
break_chain: BTreeMap<(RegionId, MusicalPosition), WriteChain<(TimeAnchor, bool)>>,
|
||||
page_break_chain: BTreeMap<(RegionId, MusicalPosition), WriteChain<(TimeAnchor, bool)>>,
|
||||
// Meter/tempo overwrite chains (Phase-3 tranche): `Some` = a set/replace at
|
||||
|
|
@ -1085,6 +1096,7 @@ struct WorkingSnapshot {
|
|||
metadata_chain: WriteChain<ScoreMetadata>,
|
||||
canvas_layout_defaults_chain: WriteChain<CanvasLayoutDefaults>,
|
||||
spelling_precedence_chain: WriteChain<SpellingPrecedence>,
|
||||
tuning_context_chain: WriteChain<TuningContextSettings>,
|
||||
break_chain: BTreeMap<(RegionId, MusicalPosition), WriteChain<(TimeAnchor, bool)>>,
|
||||
page_break_chain: BTreeMap<(RegionId, MusicalPosition), WriteChain<(TimeAnchor, bool)>>,
|
||||
meter_change_chain: BTreeMap<(RegionId, MusicalPosition), WriteChain<Option<MeterChange>>>,
|
||||
|
|
@ -1367,6 +1379,7 @@ impl<'a> Reducer<'a> {
|
|||
metadata_chain: WriteChain::new(),
|
||||
canvas_layout_defaults_chain: WriteChain::new(),
|
||||
spelling_precedence_chain: WriteChain::new(),
|
||||
tuning_context_chain: WriteChain::new(),
|
||||
break_chain: BTreeMap::new(),
|
||||
page_break_chain: BTreeMap::new(),
|
||||
meter_change_chain: BTreeMap::new(),
|
||||
|
|
@ -1469,6 +1482,17 @@ impl<'a> Reducer<'a> {
|
|||
.seed(score.canvas.layout_defaults);
|
||||
self.spelling_precedence_chain
|
||||
.seed(score.spelling_precedence.clone());
|
||||
// Genesis tranche G2b (contract pin 5): same discipline, seeded with
|
||||
// only the five wire-bearing fields — the subset the chain's value
|
||||
// type carries. `accidental_extensions` is not part of the seed and
|
||||
// is never touched by undo.
|
||||
self.tuning_context_chain.seed(TuningContextSettings {
|
||||
default_pitch_space: score.tuning_context.default_pitch_space.clone(),
|
||||
default_tuning_system: score.tuning_context.default_tuning_system.clone(),
|
||||
reference: score.tuning_context.reference.clone(),
|
||||
smufl: score.tuning_context.smufl,
|
||||
overrides: score.tuning_context.overrides.clone(),
|
||||
});
|
||||
for segment in &score.tempo_map.segments {
|
||||
self.tempo_segment_chain
|
||||
.entry((None, resolved_anchor_position(&segment.start)))
|
||||
|
|
@ -2833,6 +2857,7 @@ impl<'a> Reducer<'a> {
|
|||
self.set_canvas_layout_defaults(env, op)
|
||||
}
|
||||
OperationKind::SetSpellingPrecedence(op) => self.set_spelling_precedence(env, op),
|
||||
OperationKind::SetTuningContext(op) => self.set_tuning_context(env, op),
|
||||
},
|
||||
OperationPayload::ResolveConflict(op) => self.resolve_conflict(env, op),
|
||||
OperationPayload::UndoTransaction(op) => self.undo_transaction(env, op),
|
||||
|
|
@ -2947,6 +2972,30 @@ impl<'a> Reducer<'a> {
|
|||
OperationEffect::Applied
|
||||
}
|
||||
|
||||
/// Genesis tranche G2b (`CONTRACT_GENESIS_G2B_TUNING.md` pin 5): copies
|
||||
/// `set_metadata` structurally — advisory LWW, no conflict, no
|
||||
/// idempotence short-circuit. Writes exactly the five subset fields onto
|
||||
/// `score.tuning_context` and **leaves `accidental_extensions`
|
||||
/// untouched** — the carried payload has no such field, and preserving
|
||||
/// whatever the graph already held is the ruling (contract §1).
|
||||
fn set_tuning_context(
|
||||
&mut self,
|
||||
env: &OperationEnvelope,
|
||||
op: &SetTuningContextOp,
|
||||
) -> OperationEffect {
|
||||
self.tuning_context_chain
|
||||
.record(env.id, env.transaction, op.settings.clone());
|
||||
if let Some(score) = self.graph.as_mut() {
|
||||
score.tuning_context.default_pitch_space = op.settings.default_pitch_space.clone();
|
||||
score.tuning_context.default_tuning_system = op.settings.default_tuning_system.clone();
|
||||
score.tuning_context.reference = op.settings.reference.clone();
|
||||
score.tuning_context.smufl = op.settings.smufl;
|
||||
score.tuning_context.overrides = op.settings.overrides.clone();
|
||||
// accidental_extensions is intentionally not touched here.
|
||||
}
|
||||
OperationEffect::Applied
|
||||
}
|
||||
|
||||
fn set_metric_grid(
|
||||
&mut self,
|
||||
env: &OperationEnvelope,
|
||||
|
|
@ -5334,6 +5383,16 @@ impl<'a> Reducer<'a> {
|
|||
})
|
||||
}
|
||||
}
|
||||
// Genesis tranche G2b (contract pin 5): mirror the same shape.
|
||||
match self.tuning_context_chain.undo_verdict(tx) {
|
||||
ChainUndoVerdict::NotWritten => {}
|
||||
ChainUndoVerdict::Superseded { by } => superseded.push(by),
|
||||
ChainUndoVerdict::Restore(predecessor) => {
|
||||
restorations.push(ValueRestoration::TuningContext {
|
||||
value: predecessor.map(Predecessor::into_value),
|
||||
})
|
||||
}
|
||||
}
|
||||
for (region, chain) in &self.metric_grid_chain {
|
||||
if !slot_live(TypedObjectId::Region(*region)) {
|
||||
continue;
|
||||
|
|
@ -5574,6 +5633,24 @@ impl<'a> Reducer<'a> {
|
|||
.record(env.id, env.transaction, value);
|
||||
}
|
||||
}
|
||||
// Genesis tranche G2b (contract pin 5): mirror `Metadata`'s
|
||||
// restoration-apply shape; writes only the five subset
|
||||
// fields, leaving `accidental_extensions` untouched.
|
||||
ValueRestoration::TuningContext { value } => {
|
||||
if let Some(value) = value {
|
||||
if let Some(score) = self.graph.as_mut() {
|
||||
score.tuning_context.default_pitch_space =
|
||||
value.default_pitch_space.clone();
|
||||
score.tuning_context.default_tuning_system =
|
||||
value.default_tuning_system.clone();
|
||||
score.tuning_context.reference = value.reference.clone();
|
||||
score.tuning_context.smufl = value.smufl;
|
||||
score.tuning_context.overrides = value.overrides.clone();
|
||||
}
|
||||
self.tuning_context_chain
|
||||
.record(env.id, env.transaction, value);
|
||||
}
|
||||
}
|
||||
ValueRestoration::MetricGrid { region, value } => {
|
||||
self.metric_grid_chain
|
||||
.entry(region)
|
||||
|
|
@ -7551,6 +7628,7 @@ impl<'a> Reducer<'a> {
|
|||
metadata_chain: self.metadata_chain.clone(),
|
||||
canvas_layout_defaults_chain: self.canvas_layout_defaults_chain.clone(),
|
||||
spelling_precedence_chain: self.spelling_precedence_chain.clone(),
|
||||
tuning_context_chain: self.tuning_context_chain.clone(),
|
||||
break_chain: self.break_chain.clone(),
|
||||
page_break_chain: self.page_break_chain.clone(),
|
||||
meter_change_chain: self.meter_change_chain.clone(),
|
||||
|
|
@ -7590,6 +7668,7 @@ impl<'a> Reducer<'a> {
|
|||
self.metadata_chain = s.metadata_chain;
|
||||
self.canvas_layout_defaults_chain = s.canvas_layout_defaults_chain;
|
||||
self.spelling_precedence_chain = s.spelling_precedence_chain;
|
||||
self.tuning_context_chain = s.tuning_context_chain;
|
||||
self.break_chain = s.break_chain;
|
||||
self.page_break_chain = s.page_break_chain;
|
||||
self.meter_change_chain = s.meter_change_chain;
|
||||
|
|
@ -10914,6 +10993,92 @@ mod tests {
|
|||
0,
|
||||
"SetSpellingPrecedence stays in the major-0 catch-all"
|
||||
);
|
||||
|
||||
// (t2) Genesis tranche G2b: the opposite of G2a's pin — SetTuningContext
|
||||
// is *unconditionally* 3, the same "no lower-major layout exists" shape
|
||||
// as CreateInstrument above (not a value-dependent Option-hidden
|
||||
// embedding). Asserted on the minimal (default) settings specifically,
|
||||
// so an arm that becomes value-dependent is caught immediately.
|
||||
assert_eq!(
|
||||
OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: crate::valuegen::tuning_context_settings(0),
|
||||
})
|
||||
.schema_major(),
|
||||
3,
|
||||
"SetTuningContext is unconditionally v3, even for the minimal settings"
|
||||
);
|
||||
}
|
||||
|
||||
/// (t1) Genesis tranche G2b: kind and tag are both 34, and the
|
||||
/// discriminant byte leads the canonical encoding of the op payload.
|
||||
///
|
||||
/// **Mutation:** move either `OperationKind::SetTuningContext`'s
|
||||
/// discriminant or `OperationKindTag::SetTuningContext`'s tag to 35;
|
||||
/// must fail.
|
||||
#[test]
|
||||
fn t1_set_tuning_context_kind_and_tag_are_both_34() {
|
||||
use crate::payload::{OperationKindTag, SetTuningContextOp};
|
||||
let op = OperationKind::SetTuningContext(SetTuningContextOp {
|
||||
settings: crate::valuegen::tuning_context_settings(1),
|
||||
});
|
||||
assert_eq!(op.tag(), OperationKindTag::SetTuningContext);
|
||||
assert_eq!(op.tag().discriminant(), 34);
|
||||
let mut bytes = Vec::new();
|
||||
op.encode_canonical(&mut bytes);
|
||||
assert_eq!(
|
||||
bytes[0], 34,
|
||||
"the discriminant byte must lead the canonical encoding"
|
||||
);
|
||||
}
|
||||
|
||||
/// (t3) Genesis tranche G2b: a block containing exactly one
|
||||
/// `SetTuningContext` stamps schema major 3.
|
||||
///
|
||||
/// **Mutation:** make `schema_major` ignore the `SetTuningContext` arm
|
||||
/// (fold it into the catch-all `_ => 0`); must fail.
|
||||
#[test]
|
||||
fn t3_a_block_with_one_set_tuning_context_stamps_major_3() {
|
||||
let op = OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: crate::valuegen::tuning_context_settings(1),
|
||||
});
|
||||
let envelopes = [op.clone()];
|
||||
let major = envelopes.iter().map(|k| k.schema_major()).max().unwrap();
|
||||
assert_eq!(
|
||||
major, 3,
|
||||
"a block carrying only SetTuningContext stamps major 3"
|
||||
);
|
||||
}
|
||||
|
||||
/// (t8) Genesis tranche G2b: kind 34 carries epoch 10
|
||||
/// (`spec/PLAN_GMINOR_SCHEMA_MINOR.md` §4), and a block containing it
|
||||
/// stamps schema minor 10.
|
||||
///
|
||||
/// **Mutation:** assign epoch 9 instead of 10 in `introduced_minor`'s
|
||||
/// `SetTuningContext` arm; must fail.
|
||||
#[test]
|
||||
fn t8_set_tuning_context_carries_epoch_10() {
|
||||
let op = OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: crate::valuegen::tuning_context_settings(1),
|
||||
});
|
||||
assert_eq!(op.introduced_minor(), Some(10));
|
||||
assert_eq!(op.tag().introduced_minor(), Some(10));
|
||||
|
||||
let env = OperationEnvelope {
|
||||
id: OperationId::new(ReplicaId(1), 1),
|
||||
author: crate::support::AuthorId(0),
|
||||
stamp: crate::stamp::OperationStamp::new(
|
||||
crate::stamp::HybridLogicalClock::new(epiphany_core::WallClockTime(1), 1),
|
||||
OperationId::new(ReplicaId(1), 1),
|
||||
),
|
||||
causal_context: crate::causal::CausalContext::new(),
|
||||
transaction: None,
|
||||
payload: OperationPayload::Primitive(op),
|
||||
};
|
||||
assert_eq!(
|
||||
crate::payload::operation_block_introduced_minor(&[env]),
|
||||
Some(10),
|
||||
"a block containing kind 34 stamps minor 10"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -11007,6 +11172,16 @@ mod tests {
|
|||
// leak to appear on even if the base did stamp one (it does not —
|
||||
// that is `OperationEnvelopeBlock`'s concern in `epiphany-bundle`,
|
||||
// untouched by this packet).
|
||||
//
|
||||
// Re-pinned again at genesis tranche G2b
|
||||
// (`spec/CONTRACT_GENESIS_G2B_TUNING.md`): `gen_payload` gained
|
||||
// `SetTuningContext` (arm 31), and `rng.below(31)` became
|
||||
// `below(32)` — the same reshuffle, same reasoning. `SetTuningContext`
|
||||
// is schema major **3**, unlike its siblings, but that is
|
||||
// `OperationEnvelopeBlock`'s concern (`epiphany-bundle`), not
|
||||
// `MaterializedState`'s: the canonical base still embeds no `Score`
|
||||
// field value for any setting, so there remains no schema-major
|
||||
// surface on this type for a leak to appear on.
|
||||
let mut rng = epiphany_determinism::fuzz::SplitMix64::new(0xBA5E);
|
||||
let envelopes = crate::fuzz::gen_envelope_set(&mut rng, 200);
|
||||
let mut set = OperationSet::new();
|
||||
|
|
@ -11016,7 +11191,7 @@ mod tests {
|
|||
let hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
|
||||
assert_eq!(
|
||||
hex,
|
||||
"7fd6455a6ae304101774ba981d01702ad37c567575707b1e814dad6e153ab07d"
|
||||
"116e88b4013a18864dcd1b09489ab297aa332e14800cf4ca00be9dfa100b4a08"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -12952,6 +13127,55 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// (t9) Regression guard: the from-empty spine still reaches a note, now
|
||||
/// with a `SetTuningContext` authored alongside it — genesis tranche G2b
|
||||
/// does not disturb the spine G1 established. Mutation is t2's (the
|
||||
/// `schema_major` arm), which this test does not re-verify on its own;
|
||||
/// its job is to prove the spine and the new op coexist cleanly.
|
||||
#[test]
|
||||
fn from_empty_spine_reaches_a_note_with_a_tuning_context_authored() {
|
||||
let spine = genesis_spine_envelopes();
|
||||
let settings = crate::valuegen::tuning_context_settings(1);
|
||||
let set_tuning = prim_env(
|
||||
1,
|
||||
12,
|
||||
70,
|
||||
CausalContext::new(),
|
||||
OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: settings.clone(),
|
||||
}),
|
||||
);
|
||||
let mut envelopes = spine.envelopes;
|
||||
envelopes.push(set_tuning);
|
||||
let mut set = OperationSet::new();
|
||||
set.accept_all(envelopes);
|
||||
let identity = IdentityContext::new(ReplicaId(1));
|
||||
let out = reduce_operation_set_onto(&set, &Score::empty(identity));
|
||||
|
||||
for counter in [0, 2, 4, 6, 8, 10, 12] {
|
||||
assert_eq!(
|
||||
effect_at(&out.state, counter),
|
||||
Some(&OperationEffect::Applied),
|
||||
"spine operation at counter {counter} must apply"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
out.score.events.contains(spine.event_id),
|
||||
"the spine still reaches a note"
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.default_pitch_space,
|
||||
settings.default_pitch_space
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.default_tuning_system,
|
||||
settings.default_tuning_system
|
||||
);
|
||||
assert_eq!(out.score.tuning_context.reference, settings.reference);
|
||||
assert_eq!(out.score.tuning_context.smufl, settings.smufl);
|
||||
assert_eq!(out.score.tuning_context.overrides, settings.overrides);
|
||||
}
|
||||
|
||||
/// (i2) Re-carry idempotence: the same `CreateInstrument` twice ->
|
||||
/// `AlreadyApplied`, and a differing second carry under the same live id
|
||||
/// -> `RecreateContentMismatch` — exactly `create_staff`'s discipline
|
||||
|
|
@ -13511,6 +13735,245 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Genesis tranche G2b (`spec/CONTRACT_GENESIS_G2B_TUNING.md`): the sole
|
||||
// genesis payload born at schema major 3, `SetTuningContext`.
|
||||
// =========================================================================
|
||||
|
||||
/// (t5) **The pin's whole reason for existing** (contract §1): an op
|
||||
/// authored **live** and the same op reloaded **from bytes** must
|
||||
/// reduce to identical graph states, with a pre-existing non-empty
|
||||
/// `accidental_extensions` preserved across both. This is what the
|
||||
/// subset type (pin 1) makes structurally impossible to get wrong — a
|
||||
/// full-value `SetTuningContext(ScoreTuningContext)` would diverge here,
|
||||
/// because `OperationSet::accept` stores the authored envelope as a
|
||||
/// **value** (live path keeps whatever `accidental_extensions` the
|
||||
/// constructor set) while a decoded envelope always reconstructs it as
|
||||
/// empty (the wire drops it).
|
||||
///
|
||||
/// **Mutation:** make the payload carry the full `ScoreTuningContext`
|
||||
/// instead of the subset `TuningContextSettings`; must fail. Performed
|
||||
/// and reversed live for this rung (see the report) rather than left in
|
||||
/// the tree, since it requires the type-level change pin 1 forbids.
|
||||
#[test]
|
||||
fn t5_live_authored_and_reloaded_from_bytes_agree_with_extensions_preserved() {
|
||||
use epiphany_core::{AccidentalRegistryId, ScoreAccidentalExtensions};
|
||||
|
||||
// A base graph with a pre-existing, non-empty accidental_extensions —
|
||||
// the field the op never carries and reduction must leave untouched.
|
||||
let mut base = Score::empty(IdentityContext::new(ReplicaId(1)));
|
||||
let pre_existing = vec![ScoreAccidentalExtensions {
|
||||
base: AccidentalRegistryId::new("heji"),
|
||||
additions: Vec::new(),
|
||||
overrides: Vec::new(),
|
||||
}];
|
||||
base.tuning_context.accidental_extensions = pre_existing.clone();
|
||||
|
||||
let settings = crate::valuegen::tuning_context_settings(3);
|
||||
let live_env = prim_env(
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
CausalContext::new(),
|
||||
OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: settings.clone(),
|
||||
}),
|
||||
);
|
||||
|
||||
// Path A: authored live — the envelope object itself, never touching
|
||||
// bytes.
|
||||
let mut live_set = OperationSet::new();
|
||||
live_set.accept_all(vec![live_env.clone()]);
|
||||
let live_out = reduce_operation_set_onto(&live_set, &base);
|
||||
|
||||
// Path B: the identical envelope, round-tripped through canonical
|
||||
// bytes and decoded back before reduction.
|
||||
let bytes = live_env.to_canonical_bytes();
|
||||
let reloaded_env = crate::envdecode::decode_envelope(&bytes).expect("the envelope decodes");
|
||||
let mut reloaded_set = OperationSet::new();
|
||||
reloaded_set.accept_all(vec![reloaded_env]);
|
||||
let reloaded_out = reduce_operation_set_onto(&reloaded_set, &base);
|
||||
|
||||
// The two graph states are identical.
|
||||
assert_eq!(
|
||||
live_out.score.tuning_context, reloaded_out.score.tuning_context,
|
||||
"an op authored live and the same op reloaded from bytes must reduce identically"
|
||||
);
|
||||
// Both apply the authored five-field settings...
|
||||
assert_eq!(
|
||||
live_out.score.tuning_context.default_pitch_space,
|
||||
settings.default_pitch_space
|
||||
);
|
||||
assert_eq!(live_out.score.tuning_context.reference, settings.reference);
|
||||
// ...and both preserve the base's pre-existing accidental_extensions,
|
||||
// untouched — the field the payload cannot carry.
|
||||
assert_eq!(
|
||||
live_out.score.tuning_context.accidental_extensions, pre_existing,
|
||||
"live path: accidental_extensions preserved"
|
||||
);
|
||||
assert_eq!(
|
||||
reloaded_out.score.tuning_context.accidental_extensions, pre_existing,
|
||||
"reloaded path: accidental_extensions preserved"
|
||||
);
|
||||
}
|
||||
|
||||
/// (t6) Undo restores the previous (chain-predecessor) five-field value —
|
||||
/// not the seeded base, and not the type default. Two `SetTuningContext`
|
||||
/// writes in two separate transactions; undoing the *second* must restore
|
||||
/// the *first*'s authored value.
|
||||
///
|
||||
/// **Mutation:** in the `TuningContext` restoration-apply arm, restore
|
||||
/// the default `TuningContextSettings` instead of the chain predecessor
|
||||
/// (or drop the field writes entirely); must fail.
|
||||
#[test]
|
||||
fn t6_undo_restores_the_chain_predecessor_tuning_settings() {
|
||||
let identity = IdentityContext::new(ReplicaId(1));
|
||||
let first = crate::valuegen::tuning_context_settings(1);
|
||||
let second = crate::valuegen::tuning_context_settings(2);
|
||||
assert_ne!(first, second);
|
||||
|
||||
let tx2 = TransactionId::from_raw(2);
|
||||
let envelopes = vec![
|
||||
prim_env(
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
CausalContext::new(),
|
||||
OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: first.clone(),
|
||||
}),
|
||||
),
|
||||
declare_transaction(1, 1, 20, seen_r1(0), tx2),
|
||||
tx_member(
|
||||
1,
|
||||
2,
|
||||
21,
|
||||
seen_r1(1),
|
||||
tx2,
|
||||
OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: second,
|
||||
}),
|
||||
),
|
||||
undo_env(1, 3, 22, seen_r1(2), tx2, UndoPolicy::StrictInverse),
|
||||
];
|
||||
let mut set = OperationSet::new();
|
||||
set.accept_all(envelopes);
|
||||
let out = reduce_operation_set_onto(&set, &Score::empty(identity));
|
||||
|
||||
assert_eq!(
|
||||
out.score.tuning_context.default_pitch_space,
|
||||
first.default_pitch_space
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.default_tuning_system,
|
||||
first.default_tuning_system
|
||||
);
|
||||
assert_eq!(out.score.tuning_context.reference, first.reference);
|
||||
assert_eq!(out.score.tuning_context.smufl, first.smufl);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.overrides, first.overrides,
|
||||
"undo of the second write restores the first write's value"
|
||||
);
|
||||
}
|
||||
|
||||
/// (t7) Undo of the **first** authoring restores the **seeded base**
|
||||
/// settings, identically whether the seed was the type default or a
|
||||
/// non-default value — the never-authored / authored-to-default
|
||||
/// distinction stays unobservable (contract pin 5, and plan trap 5's
|
||||
/// withdrawal). `accidental_extensions` is untouched throughout: it is
|
||||
/// not part of the carried subset type, so undo cannot move it, and this
|
||||
/// test proves the base's own extensions survive both the write and the
|
||||
/// undo unchanged.
|
||||
///
|
||||
/// **Mutation:** skip `tuning_context_chain.seed(...)` in
|
||||
/// `seed_from_graph` -> the first undo sees `NotWritten` (no predecessor
|
||||
/// recorded) instead of `Restore(Base(seeded))`, and the field is left at
|
||||
/// the authored value instead of reverting; must fail.
|
||||
#[test]
|
||||
fn t7_undo_of_first_authoring_restores_the_seeded_base_default_or_not() {
|
||||
// Both cases run through the exact same assertion shape below — no
|
||||
// branch distinguishes "the seed happened to be the type default"
|
||||
// from "the seed was a genuine non-default value". That uniformity is
|
||||
// itself the proof the distinction is unobservable, per pin 5's
|
||||
// explicit instruction not to write code that tells them apart.
|
||||
// A pre-existing, non-empty `accidental_extensions` fixture, built
|
||||
// directly (its constructor is `pub(crate)` in `epiphany-core`, so
|
||||
// this crate cannot reuse the core fixture helper).
|
||||
let default_extensions = || {
|
||||
vec![epiphany_core::ScoreAccidentalExtensions {
|
||||
base: epiphany_core::AccidentalRegistryId::new("heji"),
|
||||
additions: Vec::new(),
|
||||
overrides: Vec::new(),
|
||||
}]
|
||||
};
|
||||
|
||||
for (case, base) in [
|
||||
("case A: from-empty, default seed", {
|
||||
let mut b = Score::empty(IdentityContext::new(ReplicaId(1)));
|
||||
b.tuning_context.accidental_extensions = default_extensions();
|
||||
b
|
||||
}),
|
||||
("case B: loaded base, non-default seed", {
|
||||
let mut b = Score::empty(IdentityContext::new(ReplicaId(1)));
|
||||
let non_default = crate::valuegen::tuning_context_settings(9);
|
||||
b.tuning_context.default_pitch_space = non_default.default_pitch_space;
|
||||
b.tuning_context.default_tuning_system = non_default.default_tuning_system;
|
||||
b.tuning_context.reference = non_default.reference;
|
||||
b.tuning_context.smufl = non_default.smufl;
|
||||
b.tuning_context.overrides = non_default.overrides;
|
||||
b.tuning_context.accidental_extensions = default_extensions();
|
||||
b
|
||||
}),
|
||||
] {
|
||||
let seeded = base.tuning_context.clone();
|
||||
let tx = TransactionId::from_raw(3);
|
||||
let authored = crate::valuegen::tuning_context_settings(20);
|
||||
let envelopes = vec![
|
||||
declare_transaction(1, 0, 10, CausalContext::new(), tx),
|
||||
tx_member(
|
||||
1,
|
||||
1,
|
||||
11,
|
||||
seen_r1(0),
|
||||
tx,
|
||||
OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: authored,
|
||||
}),
|
||||
),
|
||||
undo_env(1, 2, 12, seen_r1(1), tx, UndoPolicy::StrictInverse),
|
||||
];
|
||||
let mut set = OperationSet::new();
|
||||
set.accept_all(envelopes);
|
||||
let out = reduce_operation_set_onto(&set, &base);
|
||||
|
||||
assert_eq!(
|
||||
out.score.tuning_context.default_pitch_space, seeded.default_pitch_space,
|
||||
"{case}: undo restores the seeded default_pitch_space"
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.default_tuning_system, seeded.default_tuning_system,
|
||||
"{case}: undo restores the seeded default_tuning_system"
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.reference, seeded.reference,
|
||||
"{case}: undo restores the seeded reference"
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.smufl, seeded.smufl,
|
||||
"{case}: undo restores the seeded smufl"
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.overrides, seeded.overrides,
|
||||
"{case}: undo restores the seeded overrides"
|
||||
);
|
||||
assert_eq!(
|
||||
out.score.tuning_context.accidental_extensions, seeded.accidental_extensions,
|
||||
"{case}: accidental_extensions is untouched throughout — it is not part \
|
||||
of the carried subset type, so undo cannot move it"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// (s7) Transaction rollback discards the write — and the assertion must
|
||||
/// not be on the field. `WorkingSnapshot` (`:7480`) is the transaction
|
||||
/// **rollback** mechanism: snapshot before, restore on failure. But
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use crate::payload::{
|
|||
InsertIdentifiedPitchOp, ModifyCrossCuttingOp, ModifyEventOp, ModifyIdentifiedPitchOp,
|
||||
OperationKind, OperationKindTag, RespellPitchOp, SetCanvasLayoutDefaultsOp, SetMetadataOp,
|
||||
SetMetricGridOp, SetSpellingPrecedenceOp, SetStaffLayoutOp, SetTempoSegmentOp,
|
||||
SetTimeSignatureOp, SetUserPageBreakOp, SetUserSystemBreakOp, TransactionDescriptor,
|
||||
TransposeIntervalOp, TransposeOp,
|
||||
SetTimeSignatureOp, SetTuningContextOp, SetUserPageBreakOp, SetUserSystemBreakOp,
|
||||
TransactionDescriptor, TransposeIntervalOp, TransposeOp,
|
||||
};
|
||||
use crate::support::OperationKindRegistryId;
|
||||
|
||||
|
|
@ -229,6 +229,9 @@ impl TextValue for OperationKind {
|
|||
OperationKind::SetSpellingPrecedence(op) => {
|
||||
production(self.tag(), vec![op.precedence.project()])
|
||||
}
|
||||
OperationKind::SetTuningContext(op) => {
|
||||
production(self.tag(), vec![op.settings.project()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -566,6 +569,14 @@ impl TextValue for OperationKind {
|
|||
precedence: TextValue::parse(precedence)?,
|
||||
})
|
||||
}
|
||||
OperationKindTag::SetTuningContext => {
|
||||
let [settings] = fields(s, tag, 1)? else {
|
||||
unreachable!("the arity-1 check returned one field")
|
||||
};
|
||||
OperationKind::SetTuningContext(SetTuningContextOp {
|
||||
settings: TextValue::parse(settings)?,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -619,7 +630,7 @@ mod tests {
|
|||
#[test]
|
||||
fn every_operation_kind_round_trips_with_canonical_text() {
|
||||
let tags: Vec<_> = all_tags().collect();
|
||||
assert_eq!(tags.len(), 34, "the grammar has 34 kind productions");
|
||||
assert_eq!(tags.len(), 35, "the grammar has 35 kind productions");
|
||||
for tag in tags {
|
||||
round_trip(&sample_kind(tag));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,10 @@ pub enum V0OperationKind {
|
|||
// every other v1-native kind above.
|
||||
SetCanvasLayoutDefaults(crate::payload::SetCanvasLayoutDefaultsOp),
|
||||
SetSpellingPrecedence(crate::payload::SetSpellingPrecedenceOp),
|
||||
// Genesis tranche G2b — born at wire-disc 34; no lossy v0 form (v0
|
||||
// predates the catalog entirely), so it round-trips by identity like
|
||||
// every other v1-native kind above.
|
||||
SetTuningContext(crate::payload::SetTuningContextOp),
|
||||
}
|
||||
|
||||
/// v0 `InsertEvent`: the event was a bare [`EventId`] plus the reduction-relevant
|
||||
|
|
|
|||
|
|
@ -409,6 +409,30 @@ pub fn spelling_precedence(nth: u8) -> epiphany_core::SpellingPrecedence {
|
|||
.expect("both listed orders are total over the five source kinds")
|
||||
}
|
||||
|
||||
/// Tuning context settings with an `nth`-distinct reference frequency
|
||||
/// (genesis tranche G2b) — the carried [`epiphany_core::TuningContextSettings`]
|
||||
/// of `SetTuningContext`. Distinct `nth` give distinct values so a harness can
|
||||
/// drive concurrent `SetTuningContext`s, an advisory LWW field that resolves
|
||||
/// by canonical order with no conflict.
|
||||
pub fn tuning_context_settings(nth: u8) -> epiphany_core::TuningContextSettings {
|
||||
use epiphany_core::{CmnNominal, PitchSpacePosition, ReferencePitch};
|
||||
epiphany_core::TuningContextSettings {
|
||||
default_pitch_space: epiphany_core::PitchSpaceId::new("cmn-12"),
|
||||
default_tuning_system: epiphany_core::TuningSystemId::new("tet-12"),
|
||||
reference: ReferencePitch::new(
|
||||
PitchSpacePosition::Cmn {
|
||||
nominal: CmnNominal::A,
|
||||
alteration: 0,
|
||||
octave: 4,
|
||||
},
|
||||
440.0 + f64::from(nth),
|
||||
)
|
||||
.expect("a small positive offset from A440 stays finite and positive"),
|
||||
smufl: epiphany_core::SmuflVersionRequirement::default(),
|
||||
overrides: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A well-formed `numerator`/4 [`TimeSignature`](epiphany_core::TimeSignature)
|
||||
/// (Phase-3 tranche): `numerator` quarter-note beat groups summing exactly to
|
||||
/// the measure duration, so [`epiphany_core::TimeSignature::new`]'s beat-group
|
||||
|
|
|
|||
|
|
@ -373,6 +373,44 @@ pub fn decode_vectors() -> Vec<DecodeVector> {
|
|||
precedence_trailing,
|
||||
));
|
||||
|
||||
// --- OperationEnvelope carrying SetTuningContext (genesis tranche G2b) —
|
||||
// the sole genesis payload born at schema major 3. Same rationale as the
|
||||
// siblings above: nothing else in this corpus exercises this payload's
|
||||
// decode path, and a round-trip check alone cannot see a self-consistent
|
||||
// encoder/decoder reorder (the 3b-i lesson).
|
||||
let tuning_envelope = OperationEnvelope {
|
||||
id: OperationId::new(ReplicaId(1), 4),
|
||||
author: crate::support::AuthorId(0),
|
||||
stamp: crate::stamp::OperationStamp::new(
|
||||
crate::stamp::HybridLogicalClock::new(epiphany_core::WallClockTime(1), 1),
|
||||
OperationId::new(ReplicaId(1), 4),
|
||||
),
|
||||
causal_context: crate::causal::CausalContext::new(),
|
||||
transaction: None,
|
||||
payload: crate::payload::OperationPayload::Primitive(
|
||||
crate::payload::OperationKind::SetTuningContext(crate::payload::SetTuningContextOp {
|
||||
settings: crate::valuegen::tuning_context_settings(1),
|
||||
}),
|
||||
),
|
||||
};
|
||||
let tuning_envelope_bytes = tuning_envelope.to_canonical_bytes();
|
||||
v.push(row(
|
||||
OE,
|
||||
"accept",
|
||||
"-",
|
||||
"set_tuning_context",
|
||||
tuning_envelope_bytes.clone(),
|
||||
));
|
||||
let mut tuning_trailing = tuning_envelope_bytes;
|
||||
tuning_trailing.push(0);
|
||||
v.push(row(
|
||||
OE,
|
||||
"reject",
|
||||
"trailing-bytes",
|
||||
"set_tuning_context_trailing",
|
||||
tuning_trailing,
|
||||
));
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
|
|
@ -555,4 +593,33 @@ mod tests {
|
|||
"the committed literal bytes must decode and re-encode injectively"
|
||||
);
|
||||
}
|
||||
|
||||
/// (s8 analogue) Genesis tranche G2b (`spec/CONTRACT_GENESIS_G2B_TUNING.md`
|
||||
/// touch row 11): the `SetTuningContext` envelope decode vector, pinned to
|
||||
/// a literal byte array copied from the committed corpus — not derived by
|
||||
/// calling `.to_canonical_bytes()` here, for the same reason as the
|
||||
/// sibling tests above (the 3b-i lesson: round-trip locking alone cannot
|
||||
/// see a self-consistent encoder/decoder reorder). The mutation this
|
||||
/// guards against is a swap of discriminant 34 with any neighboring
|
||||
/// discriminant in both the encoder and the decoder — self-consistent, so
|
||||
/// every round-trip test stays green, while this correctly-named literal
|
||||
/// vector dies.
|
||||
#[test]
|
||||
fn set_tuning_context_envelope_decode_vector_is_pinned_to_literal_bytes() {
|
||||
#[rustfmt::skip]
|
||||
let bytes: Vec<u8> = vec![
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 48, 0, 0, 0, 6, 0, 0, 0, 99, 109, 110,
|
||||
45, 49, 50, 6, 0, 0, 0, 116, 101, 116, 45, 49, 50, 0, 5, 0, 4, 8, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 144, 123, 64, 1, 0, 40, 0, 1, 0, 40, 0, 0, 0, 0, 0,
|
||||
];
|
||||
let result = check("ops.operation_envelope", &bytes)
|
||||
.expect("ops.operation_envelope is owned by this crate");
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(true),
|
||||
"the committed literal bytes must decode and re-encode injectively"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ use epiphany_ops::{
|
|||
ReplicaAnomalyReason, ReplicaAnomalyRegistryId, ResolutionAction, ResolutionRegistryId,
|
||||
ResolveConflictPayload, RespellPitchOp, SerializedCanonicalInputs, SetCanvasLayoutDefaultsOp,
|
||||
SetMetadataOp, SetMetricGridOp, SetSpellingPrecedenceOp, SetStaffLayoutOp, SetTempoSegmentOp,
|
||||
SetTimeSignatureOp, SetUserPageBreakOp, SetUserSystemBreakOp, TransactionCategory,
|
||||
TransactionDescriptor, TransposeIntervalOp, TransposeOp, TupletCompensation,
|
||||
TupletCompensationKind, UndoPolicy, UndoTransactionPayload,
|
||||
SetTimeSignatureOp, SetTuningContextOp, SetUserPageBreakOp, SetUserSystemBreakOp,
|
||||
TransactionCategory, TransactionDescriptor, TransposeIntervalOp, TransposeOp,
|
||||
TupletCompensation, TupletCompensationKind, UndoPolicy, UndoTransactionPayload,
|
||||
};
|
||||
|
||||
use crate::rng::Rng;
|
||||
|
|
@ -646,7 +646,7 @@ pub fn operation_payload(rng: &mut Rng, events: u64, pitches: u64) -> OperationP
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
let kind = match rng.below(34) {
|
||||
let kind = match rng.below(35) {
|
||||
0 => {
|
||||
let pitches = if rng.boolean() {
|
||||
vec![obj_pitch(rng.below(pitches))]
|
||||
|
|
@ -871,6 +871,10 @@ pub fn operation_payload(rng: &mut Rng, events: u64, pitches: u64) -> OperationP
|
|||
32 => OperationKind::SetSpellingPrecedence(SetSpellingPrecedenceOp {
|
||||
precedence: valuegen::spelling_precedence(rng.below(3) as u8),
|
||||
}),
|
||||
// Genesis tranche G2b: the sole genesis payload born at schema major 3.
|
||||
33 => OperationKind::SetTuningContext(SetTuningContextOp {
|
||||
settings: valuegen::tuning_context_settings(rng.below(3) as u8),
|
||||
}),
|
||||
_ => OperationKind::Registered(
|
||||
OperationKindRegistryId(rng.next_u64() as u128),
|
||||
rng.byte_vec(0, 16),
|
||||
|
|
|
|||
|
|
@ -686,12 +686,18 @@ mod tests {
|
|||
#[test]
|
||||
fn op_block_beyond_the_accept_set_opens_read_only() {
|
||||
use epiphany_bundle::IntegrityAnomaly;
|
||||
// A newer writer's op block, stamped schema major 3 — beyond the reader's
|
||||
// op-block accept-set [0,2]. The bundle opens read-only preservation (the
|
||||
// A newer writer's op block, stamped schema major 4 — beyond the reader's
|
||||
// op-block accept-set [0,3]. The bundle opens read-only preservation (the
|
||||
// canonical base and manifest still read) rather than hard-rejecting.
|
||||
//
|
||||
// Major 4, not 3: genesis tranche G2b raised the op-block accept-set
|
||||
// to [0, 3] (`SetTuningContext` is born at major 3), so major 3 is
|
||||
// now admitted and this test's "beyond the accept-set" major must
|
||||
// move past it to stay an actual test of the read-only-on-overflow
|
||||
// path.
|
||||
let block = StagedChunk::operation_block_versioned(
|
||||
encode_block(&[vec![1u8, 2, 3, 4]]),
|
||||
SchemaVersion::new(3, 0),
|
||||
SchemaVersion::new(4, 0),
|
||||
);
|
||||
let reopened = reopen_with_op_block(0xD2_0002, block);
|
||||
assert!(
|
||||
|
|
@ -700,7 +706,7 @@ mod tests {
|
|||
);
|
||||
assert!(reopened.anomalies().iter().any(|a| matches!(
|
||||
a,
|
||||
IntegrityAnomaly::UnsupportedCanonicalChunkMajor { schema_major: 3 }
|
||||
IntegrityAnomaly::UnsupportedCanonicalChunkMajor { schema_major: 4 }
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -304,14 +304,15 @@ fn the_kind_productions_are_the_operation_vocabulary() {
|
|||
// The count is still hand-maintained, which is the same shape the comment
|
||||
// above warns about — derived list, literal total. Every tranche that
|
||||
// appends a tag must bump it (genesis G1 took it from 31 to 32 by adding
|
||||
// `CreateInstrument`; genesis G2a takes it from 32 to 34 by adding
|
||||
// `SetCanvasLayoutDefaults` and `SetSpellingPrecedence`). It stays a
|
||||
// literal on purpose: deriving it from `PAYLOAD_FREE.len()` would make the
|
||||
// assertion vacuous, since that is the very list it exists to pin.
|
||||
// `CreateInstrument`; genesis G2a took it from 32 to 34 by adding
|
||||
// `SetCanvasLayoutDefaults` and `SetSpellingPrecedence`; genesis G2b takes
|
||||
// it from 34 to 35 by adding `SetTuningContext`). It stays a literal on
|
||||
// purpose: deriving it from `PAYLOAD_FREE.len()` would make the assertion
|
||||
// vacuous, since that is the very list it exists to pin.
|
||||
assert_eq!(
|
||||
expected.len(),
|
||||
34,
|
||||
"33 payload-free kinds plus `Registered`"
|
||||
35,
|
||||
"34 payload-free kinds plus `Registered`"
|
||||
);
|
||||
|
||||
let actual = alternatives("kind");
|
||||
|
|
|
|||
|
|
@ -43,7 +43,13 @@ use epiphany_ops::OperationEnvelope;
|
|||
/// `epiphany-layout-ir` dependency and structurally cannot decode edit-barrier
|
||||
/// bytes). Holding the version while changing the grammar would leave two
|
||||
/// incompatible grammars both claiming `(0 9 0)`.
|
||||
pub const COMPANION_VERSION: (u32, u32, u32) = (0, 10, 0);
|
||||
///
|
||||
/// Bumped again 0.10.0 → 0.11.0 by the genesis tranche G2b
|
||||
/// (`spec/CONTRACT_GENESIS_G2B_TUNING.md`), which appended
|
||||
/// `set-tuning-context` to the `kind` production — the same reasoning as G1
|
||||
/// and G2a: extending the grammar without moving this constant would leave
|
||||
/// two incompatible grammars both claiming `(0 10 0)`.
|
||||
pub const COMPANION_VERSION: (u32, u32, u32) = (0, 11, 0);
|
||||
|
||||
/// A parsed canonical Text Projection document.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -649,11 +649,12 @@ mod tests {
|
|||
}
|
||||
|
||||
// Bumped with `COMPANION_VERSION` (0.7.0 → 0.8.0, genesis G1; 0.8.0 →
|
||||
// 0.9.0, genesis G2a; 0.9.0 → 0.10.0, G-minor). Kept a literal because
|
||||
// `projection` takes `&[&str]` and a formatted String would ripple
|
||||
// through every call site; `the_test_header_tracks_the_implemented_
|
||||
// version` below fails loudly if the two ever drift.
|
||||
const HEADER: &str = "(text-projection (0 10 0))";
|
||||
// 0.9.0, genesis G2a; 0.9.0 → 0.10.0, G-minor; 0.10.0 → 0.11.0, genesis
|
||||
// G2b). Kept a literal because `projection` takes `&[&str]` and a
|
||||
// formatted String would ripple through every call site;
|
||||
// `the_test_header_tracks_the_implemented_version` below fails loudly if
|
||||
// the two ever drift.
|
||||
const HEADER: &str = "(text-projection (0 11 0))";
|
||||
const DOCUMENT: &str = "(document #x00000000000000000000000000000001 (schema 0 1))";
|
||||
|
||||
/// A minimal but complete valid projection: just the two mandatory lines.
|
||||
|
|
|
|||
|
|
@ -329,17 +329,17 @@ pub fn document_vectors() -> Vec<TextVector> {
|
|||
.map(|(name, text)| (SURFACE, "accept", "-", *name, text.as_bytes().to_vec()))
|
||||
.collect();
|
||||
|
||||
// The rejected version must be one this crate does NOT implement. The
|
||||
// G-minor rung moved `COMPANION_VERSION` to 0.10.0, which had been this
|
||||
// The rejected version must be one this crate does NOT implement. Genesis
|
||||
// tranche G2b moved `COMPANION_VERSION` to 0.11.0, which had been this
|
||||
// vector's "future" version — leaving it would have made the negative
|
||||
// vector assert that the *correct* header is rejected. It now names
|
||||
// 0.9.0, the immediately superseded companion, which is the better test
|
||||
// 0.10.0, the immediately superseded companion, which is the better test
|
||||
// anyway: rejecting the version right behind you is exactly the deferred
|
||||
// migrate-on-read posture (`req:textproj:header-version`).
|
||||
let wrong_version = replace_once(
|
||||
minimal,
|
||||
"(text-projection (0 11 0))",
|
||||
"(text-projection (0 10 0))",
|
||||
"(text-projection (0 9 0))",
|
||||
);
|
||||
vectors.push((
|
||||
SURFACE,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -6,10 +6,28 @@ reverses Pass-12 K8 and makes every mutable field of `Score` operation-authored.
|
|||
This plan is the execution scope: what the tranche touches, in what order, and
|
||||
which questions must be answered before a dispatch contract can be written.
|
||||
|
||||
**Status:** **G1 landed** (3b09595, CI green) via
|
||||
`spec/CONTRACT_GENESIS_G1_INSTRUMENT.md`. **G2a contracted**
|
||||
(`spec/CONTRACT_GENESIS_G2A_SETTINGS.md`); **G-minor**, G2b, and G3 scoped, not
|
||||
contracted. §6 lists what still needs ratification.
|
||||
**Status:** the ladder **G1 → G2a → G-minor → G2b** is **complete**; only **G3**
|
||||
remains.
|
||||
|
||||
* **G1 landed** (3b09595) — `CreateInstrument`, kind/tag 31.
|
||||
* **G2a landed** (7df5ca1 + 55eff00) — `SetCanvasLayoutDefaults` and
|
||||
`SetSpellingPrecedence`, kinds/tags 32/33, both major 0.
|
||||
* **G-minor landed** (ff9bd0f) — the chunk schema minor became a derived
|
||||
record; epoch ladder ratified at minors 2–9 (`spec/PLAN_GMINOR_SCHEMA_MINOR.md`
|
||||
§4), governed by `spec/AUDIT_GMINOR_VOCABULARIES.md`. Closed **P13-S14**.
|
||||
* **G2b landed** — `SetTuningContext`, kind/tag **34**, schema major **3**,
|
||||
minor epoch **10**. Carried the sole accept-set raise
|
||||
(`OperationEnvelopeBlock` 2→3) and rewrote the `bundle.rs` rationale it
|
||||
falsified. Payload is the five-field subset
|
||||
`epiphany_core::TuningContextSettings`, **not** the full graph type — §5
|
||||
trap 7's holdout, resolved in the contract as *subset over normalization*.
|
||||
Closed **P13-S13**.
|
||||
* **G3** — the five remaining mint families, plus the delete/modify coverage
|
||||
design. Scoped, not contracted.
|
||||
|
||||
§6 lists what still needs ratification. **Standing constraint, now with real
|
||||
teeth:** pruning MUST NOT be implemented until disposition C lands — after G2b
|
||||
it would discard *authored* genesis state, not merely re-derivable state.
|
||||
|
||||
**G1 shipped documentation debt** — five normative falsehoods across
|
||||
`binary_format.tex`, `core_spec.tex`, and `operation_catalog.tex`, because its
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1455,6 +1455,13 @@ and the reader disagree about the field's type.
|
|||
\tablenums{33} & \texttt{SetSpellingPrecedence} &
|
||||
$\mathrm{lp}$(\texttt{SpellingPrecedence}) &
|
||||
\sectionsc{SetSpellingPrecedence} \\
|
||||
\tablenums{34} & \texttt{SetTuningContext} &
|
||||
$\mathrm{lp}$(\texttt{TuningContextSettings}) --- the \textbf{authored
|
||||
subset} of \texttt{ScoreTuningContext}: exactly its five wire-bearing
|
||||
fields, in the same order, byte-identical to the schema-major-3
|
||||
\texttt{ScoreTuningContext} layout (Section~\ref{sec:evolution:major3});
|
||||
born at v3 unconditionally &
|
||||
\sectionsc{SetTuningContext} \\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
\endgroup
|
||||
|
|
@ -1525,6 +1532,7 @@ discriminants and wrong lengths are decode errors. Append-only past
|
|||
\tablenums{28} & \texttt{CreateRepeatStructure} & \tablenums{29} & \texttt{DeleteRepeatStructure} \\
|
||||
\tablenums{30} & \texttt{TransposeInterval} & \tablenums{31} & \texttt{CreateInstrument} \\
|
||||
\tablenums{32} & \texttt{SetCanvasLayoutDefaults} & \tablenums{33} & \texttt{SetSpellingPrecedence} \\
|
||||
\tablenums{34} & \texttt{SetTuningContext} & & \\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
\endgroup
|
||||
|
|
@ -2332,9 +2340,13 @@ Every chunk declares a \texttt{SchemaVersion} (major, minor) in its
|
|||
role}, not one shared bound (Section~\ref{sec:evolution:major1}'s ``raised
|
||||
per chunk role'' rule, sharpened by major~3, the first bump under which the
|
||||
roles' maxima genuinely diverge): the \texttt{Snapshot} role's max is
|
||||
\tablenums{3}; the \texttt{OperationEnvelopeBlock} role's max stays
|
||||
\tablenums{2} (schema major~3 embeds no operation payload, so this role
|
||||
does not rise); every other role's max is \tablenums{0}. The reference
|
||||
\tablenums{3}; the \texttt{OperationEnvelopeBlock} role's max is
|
||||
\tablenums{3} as of genesis tranche G2b
|
||||
(\texttt{spec/CONTRACT\_GENESIS\_G2B\_TUNING.md} ---
|
||||
\texttt{SetTuningContext}, \tablenums{34}, is born at v3
|
||||
unconditionally; the role stayed at \tablenums{2} from Push~4b tranche
|
||||
3b-i, when schema major~3 embedded no operation payload, until this
|
||||
rung); every other role's max is \tablenums{0}. The reference
|
||||
implementation's overall envelope is $\{0, 1, 2, 3\}$ ($\textsc{min} = 0$,
|
||||
$\textsc{max} = 3$), realized as these differing per-role maxima rather
|
||||
than a single shared bound.
|
||||
|
|
@ -2370,22 +2382,26 @@ The \emph{only} minor-additive mechanism in schema major~0 is
|
|||
\textbf{appending discriminants to open vocabularies}:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{OperationKind}: append at ${\geq}\,\tablenums{34}$
|
||||
\item \texttt{OperationKind}: append at ${\geq}\,\tablenums{35}$
|
||||
(Requirement~\ref{req:binfmt:kind-discriminants}; the Phase-3
|
||||
tranche took \tablenums{24}--\tablenums{27} under this mechanism,
|
||||
the repeat pair \tablenums{28}/\tablenums{29} in the
|
||||
schema-major-2 revision, Push~4a took \tablenums{30}
|
||||
(\texttt{TransposeInterval}), genesis tranche G1 took \tablenums{31}
|
||||
(\texttt{CreateInstrument}), and genesis tranche G2a took
|
||||
(\texttt{CreateInstrument}), genesis tranche G2a took
|
||||
\tablenums{32}/\tablenums{33} (\texttt{SetCanvasLayoutDefaults},
|
||||
\texttt{SetSpellingPrecedence}) --- the discriminant space is one
|
||||
append-only table across majors, but a kind's \emph{block stamp} follows
|
||||
minimal stamping over its payload: \tablenums{28}'s payload embeds a v2
|
||||
layout, so its blocks stamp major~2, Section~\ref{sec:evolution:major2});
|
||||
\item \texttt{OperationKindTag}: append at ${\geq}\,\tablenums{34}$
|
||||
(Requirement~\ref{req:binfmt:kind-tag}; the same ten variants as
|
||||
\texttt{OperationKind}, at the same ten discriminants, appended by the
|
||||
same five tranches);
|
||||
\texttt{SetSpellingPrecedence}), and genesis tranche G2b took
|
||||
\tablenums{34} (\texttt{SetTuningContext}) --- the discriminant space is
|
||||
one append-only table across majors, but a kind's \emph{block stamp}
|
||||
follows minimal stamping over its payload: \tablenums{28}'s payload
|
||||
embeds a v2 layout, so its blocks stamp major~2,
|
||||
Section~\ref{sec:evolution:major2}; \tablenums{34}'s payload embeds a v3
|
||||
layout unconditionally, so its blocks stamp major~3,
|
||||
Section~\ref{sec:evolution:major3} --- the sole appended kind to do so);
|
||||
\item \texttt{OperationKindTag}: append at ${\geq}\,\tablenums{35}$
|
||||
(Requirement~\ref{req:binfmt:kind-tag}; the same eleven variants as
|
||||
\texttt{OperationKind}, at the same eleven discriminants, appended by
|
||||
the same six tranches);
|
||||
\item \texttt{OperationPayload}: append at ${\geq}\,\tablenums{4}$
|
||||
(Section~\ref{sec:ops:payload}; \texttt{ResolveEquivocation} = 3, Push~3,
|
||||
is the one and only append this vocabulary has had);
|
||||
|
|
@ -2977,24 +2993,39 @@ layout ahead of a real consumer is exactly what the frozen-layout rule
|
|||
major~1.
|
||||
|
||||
\subsection{Where the changed fields reach}
|
||||
Only one payload type changes, and it changes at exactly one site:
|
||||
|
||||
\textbf{As first defined (Push~4b tranche 3b-i), no operation payload
|
||||
embedded the tuning context} --- \texttt{ScoreTuningContext} reached the
|
||||
wire only through the acceleration full-\texttt{Score} snapshot, and the
|
||||
\texttt{OperationEnvelopeBlock} role's accept-set stayed at major~2. This
|
||||
changed at genesis tranche G2b
|
||||
(\texttt{spec/CONTRACT\_GENESIS\_G2B\_TUNING.md}), which corrects the claim
|
||||
below:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Snapshot-only:} \texttt{ScoreTuningContext} reaches the wire
|
||||
\emph{only} through the acceleration full-\texttt{Score} snapshot. No
|
||||
operation payload embeds the tuning context (core Chapter~6's operation
|
||||
vocabulary carries no \texttt{ScoreTuningContext} / \texttt{TuningOverride}
|
||||
/ \texttt{SmuflVersionRequirement} field anywhere), so the
|
||||
\textbf{canonical operation layer is untouched}: no op payload is ``born
|
||||
at v3'', and no frozen v2 op-payload decoder is needed. The
|
||||
\texttt{OperationEnvelopeBlock} role's accept-set therefore stays at
|
||||
major~2 (Section~\ref{sec:evolution:major2}) --- schema major~3 is the
|
||||
first data-model bump under which a chunk role's max does \emph{not}
|
||||
move in lockstep with the others.
|
||||
\item \textbf{Reaches two sites, as of genesis tranche G2b.}
|
||||
\texttt{ScoreTuningContext} still reaches the wire through the
|
||||
acceleration full-\texttt{Score} snapshot, \emph{and} its authored
|
||||
subset --- \texttt{TuningContextSettings}, exactly the five
|
||||
wire-bearing fields, byte-identical to this section's layout
|
||||
(Section~\ref{sec:ops:kinds}, \tablenums{34}
|
||||
\texttt{SetTuningContext}) --- now reaches the wire through the
|
||||
canonical operation layer: \texttt{SetTuningContext} is \emph{born at
|
||||
v3 unconditionally} (the same shape as \texttt{CreateInstrument}'s v2
|
||||
birth, Section~\ref{sec:evolution:major2}), since \texttt{smufl} and
|
||||
\texttt{overrides} are mandatory appends with no lower-major layout for
|
||||
this payload to fall back to. The \texttt{OperationEnvelopeBlock}
|
||||
role's accept-set therefore \textbf{widens to major~3}
|
||||
(Section~\ref{sec:evolution:gate}) --- schema major~3 stopped being the
|
||||
data-model bump under which a chunk role's max does not move in
|
||||
lockstep with the others; that distinction now belongs to no major.
|
||||
\item \textbf{The canonical base is unchanged.} The
|
||||
\texttt{MaterializedState} embeds no tuning context, so it remains
|
||||
major~0, byte-identical across the bump --- the same keystone as
|
||||
majors~1 and~2, and a conformance test \SHOULD{} assert it again.
|
||||
\texttt{MaterializedState} embeds no tuning context (nor any other
|
||||
\texttt{Score} field value), so it remains major~0, byte-identical
|
||||
across the bump --- the same keystone as majors~1 and~2, and a
|
||||
conformance test \SHOULD{} assert it again. This is unaffected by G2b:
|
||||
the canonical carrier of the tuning context is the operation log, not
|
||||
the base, so the base's own stamping never moves.
|
||||
\item The manifest stays major~0 (nothing here touches it).
|
||||
\end{itemize}
|
||||
|
||||
|
|
@ -3005,10 +3036,14 @@ or migrate it on read (the reference implementation migrates). Canonical
|
|||
chunks parse or the bundle opens read-only. Migration \emph{composes}: a
|
||||
major-3 reader migrates a major-0 snapshot
|
||||
v0${\to}$v1${\to}$v2${\to}$v3 in one read, each step total and
|
||||
default-filling. Because no operation payload changes at this major, a
|
||||
major-2-only reader's op-block admission is exactly as before this bump ---
|
||||
only the \texttt{Snapshot} role's accept-set widens, to \tablenums{3}
|
||||
(Section~\ref{sec:evolution:gate}).
|
||||
default-filling. As originally defined (Push~4b tranche 3b-i), no operation
|
||||
payload changed at this major, so a major-2-only reader's op-block
|
||||
admission was exactly as before the bump and only the \texttt{Snapshot}
|
||||
role's accept-set widened, to \tablenums{3}
|
||||
(Section~\ref{sec:evolution:gate}). Genesis tranche G2b (above) changes
|
||||
this: a major-2-only reader now meets a possible major-3 op block
|
||||
(\texttt{SetTuningContext}) and opens read-only, exactly the one-way door
|
||||
Section~\ref{sec:evolution:gate} describes for every other accept-set raise.
|
||||
|
||||
\subsection{Changed value layout}
|
||||
As in majors~1 and~2, the wire form appends new fields \emph{after} the
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -6963,6 +6963,11 @@ pub enum OperationKind {
|
|||
// SetMetadata advisory-LWW pattern.
|
||||
SetCanvasLayoutDefaults(SetCanvasLayoutDefaultsOp),
|
||||
SetSpellingPrecedence(SetSpellingPrecedenceOp),
|
||||
// Genesis tranche G2b: the sole genesis payload born at schema major 3,
|
||||
// on the same advisory-LWW pattern. Carries TuningContextSettings, the
|
||||
// authored subset of ScoreTuningContext (accidental_extensions is not
|
||||
// on the wire and is left untouched by reduction).
|
||||
SetTuningContext(SetTuningContextOp),
|
||||
|
||||
// Layout-semantic operations
|
||||
SetUserSystemBreak(SetUserSystemBreakOp),
|
||||
|
|
@ -11931,6 +11936,8 @@ pub enum OperationKindTag {
|
|||
// Genesis tranche G2a: the two major-0 settings setters.
|
||||
SetCanvasLayoutDefaults,
|
||||
SetSpellingPrecedence,
|
||||
// Genesis tranche G2b: name-verbatim, as the two prior additions are.
|
||||
SetTuningContext,
|
||||
}
|
||||
|
||||
pub enum BarrierScope {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -231,7 +231,7 @@
|
|||
{\Large\scshape\color{epiphanyslate}Operation Catalog}\\[6pt]
|
||||
{\large\itshape\color{epiphanyslate}A companion to the Core Specification}\\[14pt]
|
||||
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[24pt]
|
||||
{\normalsize\color{epiphanyink}Version 0.10.0 --- The genesis operation tranche (G1 + G2a)}\\[4pt]
|
||||
{\normalsize\color{epiphanyink}Version 0.11.0 --- The genesis operation tranche reaches schema major 3 (G2b)}\\[4pt]
|
||||
{\small\color{epiphanyslate}Normative for the operation kinds it defines}
|
||||
\vfill
|
||||
\end{titlepage}
|
||||
|
|
@ -382,6 +382,41 @@ clause is narrowed to the canvas object itself, per
|
|||
are no longer outside the operation set; the canvas is still never
|
||||
op-minted.
|
||||
|
||||
\medskip
|
||||
|
||||
\noindent\textbf{Version 0.11.0 (genesis tranche G2b,
|
||||
\texttt{spec/CONTRACT\_GENESIS\_G2B\_TUNING.md}).} Adds one operation kind,
|
||||
\texttt{SetTuningContext} (Section~\ref{sec:k0:set-tuning-context}), the
|
||||
third and final genesis-ladder setter deferred at 0.10.0. Carries
|
||||
\texttt{TuningContextSettings}, the authored subset of
|
||||
\texttt{ScoreTuningContext} --- exactly its five wire-bearing fields, byte-
|
||||
identical to the existing schema-major-3 wire walk (Binary Format companion
|
||||
\sectionsc{Schema Major 3}) --- rather than the full graph type, so that
|
||||
\texttt{accidental\_extensions} (staged off the wire) cannot diverge between
|
||||
a live-authored session and the same document reloaded from bytes.
|
||||
|
||||
\textbf{The sole accept-set-raising surface among the nine genesis
|
||||
settings/creates.} \texttt{SetTuningContext} is schema major~3
|
||||
\emph{unconditionally} (\texttt{ScoreTuningContext}'s \texttt{smufl}/
|
||||
\texttt{overrides} appends are mandatory, so no lower-major layout for this
|
||||
payload exists at any value), which is why it alone --- not
|
||||
\texttt{CreateInstrument} at major~2, and not either G2a setter at major~0 ---
|
||||
drags the \texttt{OperationEnvelopeBlock} accept-set from \texttt{[0, 2]} to
|
||||
\texttt{[0, 3]} (Binary Format companion \sectionsc{The Chunk-Level Gate}).
|
||||
This is the reason the genesis ladder split \texttt{SetTuningContext} into
|
||||
its own rung rather than landing it alongside \texttt{SetCanvasLayoutDefaults}
|
||||
/ \texttt{SetSpellingPrecedence}: a one-way accept-set door is not something
|
||||
to bury inside two unrelated major-0 setters.
|
||||
|
||||
Undo restores the \textbf{seeded base} settings --- default or non-default
|
||||
alike --- exactly as \texttt{SetMetadata} does; the never-authored /
|
||||
authored-to-default distinction stays unobservable
|
||||
(Section~\ref{sec:k0:set-tuning-context}). This closes P13-S13: the
|
||||
canonical operation log is now the sole canonical carrier of the tuning
|
||||
context (the canonical base embeds no \texttt{Score} field value for any
|
||||
setting, \texttt{metadata} included), on the same reasoning that already
|
||||
applied to \texttt{SetMetadata}.
|
||||
|
||||
% ===========================================================================
|
||||
\chapter{The Catalog Framework}
|
||||
\label{ch:framework}
|
||||
|
|
@ -1247,6 +1282,79 @@ state rather than absence.
|
|||
\textbf{Re-anchoring.} Not applicable: the field is a score singleton with no
|
||||
tombstonable anchor.
|
||||
|
||||
\section{SetTuningContext}
|
||||
\label{sec:k0:set-tuning-context}
|
||||
|
||||
Ratified with the genesis operation tranche's G2b rung (\texttt{spec/
|
||||
CONTRACT\_GENESIS\_G2B\_TUNING.md}, executing \texttt{spec/
|
||||
RULING\_GENESIS\_PERSISTENCE.md}): the score's tuning context settings
|
||||
(\texttt{Score.tuning\_context}) join the operation-authored surfaces, on
|
||||
the same \texttt{SetMetadata} pattern as \texttt{SetCanvasLayoutDefaults}
|
||||
and \texttt{SetSpellingPrecedence} above. Unlike either sibling,
|
||||
\texttt{SetTuningContext} is the \textbf{sole genesis payload born at
|
||||
schema major~3} and is the surface that raises the
|
||||
\texttt{OperationEnvelopeBlock} accept-set from \texttt{[0, 2]} to
|
||||
\texttt{[0, 3]} (Binary Format companion \sectionsc{The Chunk-Level Gate}).
|
||||
|
||||
\textbf{Payload schema.} \texttt{SetTuningContextOp \{ settings:
|
||||
TuningContextSettings \}}. \texttt{TuningContextSettings} is the
|
||||
\textbf{authored subset} of \texttt{ScoreTuningContext}: exactly its five
|
||||
wire-bearing fields (\texttt{default\_pitch\_space},
|
||||
\texttt{default\_tuning\_system}, \texttt{reference}, \texttt{smufl},
|
||||
\texttt{overrides}), in the codec's existing order --- not the full
|
||||
six-field graph type. \texttt{accidental\_extensions} is deliberately
|
||||
\textbf{not} a field of the carried payload: it is staged out of schema
|
||||
major~3 (Binary Format companion \sectionsc{Schema Major 3}) and
|
||||
\texttt{OperationSet::accept} stores the authored envelope as a
|
||||
\emph{value}, so a payload carrying the full \texttt{ScoreTuningContext}
|
||||
would let \texttt{accidental\_extensions} reach the graph on the authoring
|
||||
replica while decoding to empty on every replica that received the
|
||||
document through serialization --- the same document in two graph states,
|
||||
depending only on whether it was just authored or just reloaded. The
|
||||
subset type makes this divergence unrepresentable: a field the payload
|
||||
does not carry cannot be set inconsistently.
|
||||
|
||||
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
||||
\texttt{settings}. \texttt{TuningContextSettings}'s encoding is
|
||||
\textbf{byte-identical} to \texttt{ScoreTuningContext}'s existing
|
||||
five-field wire walk --- a type-level narrowing, not a new wire form. Since
|
||||
\texttt{ScoreTuningContext}'s \texttt{smufl} and \texttt{overrides} appends
|
||||
are mandatory (not \texttt{Option}-hidden), no lower-major layout for this
|
||||
payload exists at any value: \texttt{SetTuningContext} is \textbf{born at
|
||||
schema major~3 unconditionally}, the same shape as \texttt{CreateInstrument}'s
|
||||
unconditional birth at major~2 (Section~\ref{sec:k0:create-instrument}), not
|
||||
the value-dependent shape \texttt{CreateRegion}/\texttt{SetStaffLayout} use.
|
||||
|
||||
\textbf{Reduction rule.} An advisory last-writer-wins field overwrite,
|
||||
identical in discipline to \texttt{SetMetadata}: no working state, no
|
||||
conflict, and no idempotence short-circuit. Graph-aware reduction writes
|
||||
exactly the five subset fields onto \texttt{Score.tuning\_context} and
|
||||
\textbf{leaves \texttt{accidental\_extensions} untouched} --- preserving
|
||||
whatever the graph already held, since the operation carries no
|
||||
information about that field and normalizing it away (clearing it at
|
||||
construction, or rejecting a populated in-memory value) would either make
|
||||
correctness depend on a clearing discipline the compiler cannot see, or
|
||||
turn an in-memory-only field into an authoring error for callers who never
|
||||
opted into its persistence.
|
||||
|
||||
\textbf{Conflict cases.} None (advisory LWW).
|
||||
|
||||
\textbf{Undo semantics.} A value-restoring field overwrite
|
||||
(Section~\ref{sec:k0:undo}), with the chain's value type the \textbf{subset}
|
||||
\texttt{TuningContextSettings}, not \texttt{ScoreTuningContext}: the write
|
||||
chain is seeded from the base's five wire-bearing fields at ingest, so
|
||||
undoing the first operational write restores the pre-operational settings
|
||||
rather than absence, identically whether the base was seeded with the type
|
||||
default or a genuine non-default value --- the never-authored /
|
||||
authored-to-default distinction is unobservable and stays so, since
|
||||
\texttt{ScoreTuningContext} is an always-valued \texttt{Score} field, like
|
||||
\texttt{metadata}, with no absent state to return to. \texttt{accidental\_
|
||||
extensions} never participates in undo: no operation ever writes it, so
|
||||
undo leaves it exactly as it found it.
|
||||
|
||||
\textbf{Re-anchoring.} Not applicable: the field is a score singleton with no
|
||||
tombstonable anchor.
|
||||
|
||||
\section{Repeat Structures}
|
||||
\label{sec:k0:repeat-structures}
|
||||
|
||||
|
|
@ -1604,8 +1712,8 @@ transaction minted is tombstoned. \texttt{StrictInverse} conflicts
|
|||
\emph{Value restoration} (this revision): for every last-writer-wins overwrite
|
||||
the target transaction performed --- event and identified-pitch modification,
|
||||
respelling, cross-cutting modification, metadata, canvas layout defaults,
|
||||
spelling precedence, metric grid, meter change, tempo segment, staff layout,
|
||||
and the user break advisories --- the reducer
|
||||
spelling precedence, tuning context settings, metric grid, meter change,
|
||||
tempo segment, staff layout, and the user break advisories --- the reducer
|
||||
maintains, per overwritten key, the \emph{canonical-order write chain} of
|
||||
(writer, value) pairs. Undoing the transaction restores each written key to
|
||||
its chain-predecessor value (or its absence, where the transaction introduced
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -234,7 +234,7 @@
|
|||
{\Large\scshape\color{epiphanyslate}Text Projection}\\[6pt]
|
||||
{\large\itshape\color{epiphanyslate}A companion to the Core Specification}\\[14pt]
|
||||
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[24pt]
|
||||
{\normalsize\color{epiphanyink}Version 0.10.0 --- The carried manifest schema version reaches the document line}\\[4pt]
|
||||
{\normalsize\color{epiphanyink}Version 0.11.0 --- The genesis tuning-context setter reaches the grammar}\\[4pt]
|
||||
{\small\color{epiphanyslate}Normative for the text form it defines}
|
||||
\vfill
|
||||
\end{titlepage}
|
||||
|
|
@ -483,7 +483,7 @@ A projection is, in order:
|
|||
\begin{requirement}
|
||||
\label{req:textproj:header-version}
|
||||
A parser implementing this companion \MUST{} accept exactly one header
|
||||
version: \texttt{(0 10 0)}, the version of the companion it implements. It
|
||||
version: \texttt{(0 11 0)}, the version of the companion it implements. It
|
||||
\MUST{} reject any other version at line one.
|
||||
|
||||
Multi-version acceptance and text migrate-on-read are deferred in the same
|
||||
|
|
@ -534,7 +534,7 @@ projection introduces no ordering of its own.
|
|||
A parser \MUST{} reject every \texttt{(blob ...)} line whose blob is
|
||||
unreferenced by canonical state
|
||||
(Requirement~\ref{req:textproj:canonical-blobs}). At companion
|
||||
version~0.10.0, neither a canonical operation nor canonical reduced state can
|
||||
version~0.11.0, neither a canonical operation nor canonical reduced state can
|
||||
carry a \texttt{BlobId}; canonical state therefore cannot reference a blob,
|
||||
and a parser \MUST{} reject every \texttt{(blob ...)} line.
|
||||
\end{requirement}
|
||||
|
|
@ -1061,6 +1061,7 @@ kind ::= "(insert-event " bytes " " value ")"
|
|||
| "(create-instrument " value ")"
|
||||
| "(set-canvas-layout-defaults " value ")"
|
||||
| "(set-spelling-precedence " value ")"
|
||||
| "(set-tuning-context " value ")"
|
||||
|
||||
tuplet-comp ::= "not-in-tuplet" | "(replace-with-rest " value ")"
|
||||
| "(rewrite-tuplets (" bytes* "))"
|
||||
|
|
@ -1137,7 +1138,7 @@ A document of one operation --- a transposition of two pitches up a perfect
|
|||
fifth, over a compacted base --- projects to five lines:
|
||||
|
||||
\begin{lstlisting}
|
||||
(text-projection (0 10 0))
|
||||
(text-projection (0 11 0))
|
||||
(document #x05050505050505050505050505050505 (schema 0 1))
|
||||
(profile full (0 1 0) (constraints 67108864 (retention 1 () true)))
|
||||
(canonical-base #x1f8b0000000000000000000000000000 #x 1 full (schema 0 1) #x0000)
|
||||
|
|
@ -1372,6 +1373,18 @@ absorb it, exactly as the binary decoder does.
|
|||
both claiming \texttt{(0 9 0)}. Cached projections at \texttt{(0 9 0)} do not
|
||||
migrate; a stale \texttt{TextProjection} chunk is regenerated, not
|
||||
converted. \\
|
||||
\today & Chapter 5 & 0.11.0 --- The genesis tuning-context setter reaches
|
||||
the grammar (genesis tranche G2b,
|
||||
\texttt{spec/CONTRACT\_GENESIS\_G2B\_TUNING.md}). The \texttt{kind}
|
||||
production gains \texttt{"(set-tuning-context " value ")"}
|
||||
(\texttt{req:textproj:operation-vocabulary}), the third appended kind since
|
||||
the header was gated to a single version at 0.7.0.
|
||||
|
||||
Same forcing reason as every prior grammar-extending bump: holding the
|
||||
version while extending the grammar would leave two mutually incompatible
|
||||
grammars both claiming \texttt{(0 10 0)}. Cached projections at
|
||||
\texttt{(0 10 0)} do not migrate; a stale \texttt{TextProjection} chunk is
|
||||
regenerated, not converted. \\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,8 +71,9 @@ ops.operation_kind_tag accept - tag_30 1e
|
|||
ops.operation_kind_tag accept - tag_31 1f
|
||||
ops.operation_kind_tag accept - tag_32 20
|
||||
ops.operation_kind_tag accept - tag_33 21
|
||||
ops.operation_kind_tag accept - tag_34 22
|
||||
ops.operation_kind_tag accept - registered 1000000000000000000123456789abcdef
|
||||
ops.operation_kind_tag reject unknown-discriminant tag_34_one_past_the_vocabulary 22
|
||||
ops.operation_kind_tag reject unknown-discriminant tag_35_one_past_the_vocabulary 23
|
||||
ops.operation_kind_tag reject unknown-discriminant tag_200 c8
|
||||
ops.operation_kind_tag reject truncated tag_empty -
|
||||
ops.operation_kind_tag reject trailing-bytes insert_event_trailing 0000
|
||||
|
|
@ -85,6 +86,8 @@ ops.operation_envelope accept - set_canvas_layout_defaults 000000000000000100000
|
|||
ops.operation_envelope reject trailing-bytes set_canvas_layout_defaults_trailing 000000000000000100000000000000020000000000000000000000000000000001000000000000000100000000000000000000010000000000000002000000000000000000002048000000080000000000000000805a40080000000000000000906240080000000000000000001e40080000000000000000001e40080000000000000000001e40080000000000000000001e4000
|
||||
ops.operation_envelope accept - set_spelling_precedence 000000000000000100000000000000030000000000000000000000000000000001000000000000000100000000000000000000010000000000000003000000000000000000002109000000050000000403020100
|
||||
ops.operation_envelope reject trailing-bytes set_spelling_precedence_trailing 00000000000000010000000000000003000000000000000000000000000000000100000000000000010000000000000000000001000000000000000300000000000000000000210900000005000000040302010000
|
||||
ops.operation_envelope accept - set_tuning_context 00000000000000010000000000000004000000000000000000000000000000000100000000000000010000000000000000000001000000000000000400000000000000000000223000000006000000636d6e2d3132060000007465742d313200050004080000000000000000907b40010028000100280000000000
|
||||
ops.operation_envelope reject trailing-bytes set_tuning_context_trailing 00000000000000010000000000000004000000000000000000000000000000000100000000000000010000000000000000000001000000000000000400000000000000000000223000000006000000636d6e2d3132060000007465742d313200050004080000000000000000907b4001002800010028000000000000
|
||||
|
||||
# bundle.manifest
|
||||
bundle.manifest accept - empty_manifest 6f9e7d11689ab113c4a1f05faf60fe60050505050505050505050505050505050000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000001000000000000000000000400000000010000000001000000000000
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@
|
|||
# document bytes are normative. `<utf8-hex>` is lowercase with no separators.
|
||||
|
||||
# textproj.document
|
||||
textproj.document accept - minimal 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a
|
||||
textproj.document accept - lineage_custom_profile 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document accept - extension_base_two_envelopes 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document accept - rich_document 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830343034303430343034303430343034303430343034303430343034303430342028736368656d612030203129290a286c696e656167652023783134313431343134313431343134313431343134313431343134313431343134290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a28657874656e73696f6e202378303230323032303230323032303230323032303230323032303230323032303220283120302032292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830332929202378303220237830323033290a2863616e6f6e6963616c2d62617365202378303430343034303430343034303430343034303430343034303430343034303420237830313032303420312066756c6c2028736368656d61203020312920237861303034290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303034202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203430302030202378303030303030303030303030303030313030303030303030303030303030303429202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030342929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303035202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203530302030202378303030303030303030303030303030313030303030303030303030303030303529202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030352929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303036202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203630302030202378303030303030303030303030303030313030303030303030303030303030303629202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030362929290a
|
||||
textproj.document reject wrong-header-version superseded_companion_version 28746578742d70726f6a656374696f6e2028302039203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a
|
||||
textproj.document reject blob-line unreferenced_blob 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28626c6f6220226170706c69636174696f6e2f6f637465742d73747265616d222028292023783031290a
|
||||
textproj.document reject out-of-order-sections canonical_base_before_extension 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document reject repeated-singular-section lineage_repeated 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document reject operation-envelope-order envelopes_reversed 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a
|
||||
textproj.document reject profile-declaration-order profiles_reversed 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document reject extension-declaration-order extensions_reversed 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830343034303430343034303430343034303430343034303430343034303430342028736368656d612030203129290a286c696e656167652023783134313431343134313431343134313431343134313431343134313431343134290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303230323032303230323032303230323032303230323032303230323032303220283120302032292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830332929202378303220237830323033290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303430343034303430343034303430343034303430343034303430343034303420237830313032303420312066756c6c2028736368656d61203020312920237861303034290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303034202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203430302030202378303030303030303030303030303030313030303030303030303030303030303429202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030342929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303035202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203530302030202378303030303030303030303030303030313030303030303030303030303030303529202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030352929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303036202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203630302030202378303030303030303030303030303030313030303030303030303030303030303629202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030362929290a
|
||||
textproj.document reject extension-chunk-order extension_chunks_reversed 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document reject missing-trailing-lf final_lf_missing 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e20312028292074727565292929
|
||||
textproj.document accept - minimal 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a
|
||||
textproj.document accept - lineage_custom_profile 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document accept - extension_base_two_envelopes 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document accept - rich_document 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830343034303430343034303430343034303430343034303430343034303430342028736368656d612030203129290a286c696e656167652023783134313431343134313431343134313431343134313431343134313431343134290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a28657874656e73696f6e202378303230323032303230323032303230323032303230323032303230323032303220283120302032292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830332929202378303220237830323033290a2863616e6f6e6963616c2d62617365202378303430343034303430343034303430343034303430343034303430343034303420237830313032303420312066756c6c2028736368656d61203020312920237861303034290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303034202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203430302030202378303030303030303030303030303030313030303030303030303030303030303429202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030342929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303035202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203530302030202378303030303030303030303030303030313030303030303030303030303030303529202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030352929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303036202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203630302030202378303030303030303030303030303030313030303030303030303030303030303629202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030362929290a
|
||||
textproj.document reject wrong-header-version superseded_companion_version 28746578742d70726f6a656374696f6e202830203130203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a
|
||||
textproj.document reject blob-line unreferenced_blob 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28626c6f6220226170706c69636174696f6e2f6f637465742d73747265616d222028292023783031290a
|
||||
textproj.document reject out-of-order-sections canonical_base_before_extension 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document reject repeated-singular-section lineage_repeated 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document reject operation-envelope-order envelopes_reversed 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a
|
||||
textproj.document reject profile-declaration-order profiles_reversed 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document reject extension-declaration-order extensions_reversed 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830343034303430343034303430343034303430343034303430343034303430342028736368656d612030203129290a286c696e656167652023783134313431343134313431343134313431343134313431343134313431343134290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303230323032303230323032303230323032303230323032303230323032303220283120302032292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830332929202378303220237830323033290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303430343034303430343034303430343034303430343034303430343034303420237830313032303420312066756c6c2028736368656d61203020312920237861303034290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303034202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203430302030202378303030303030303030303030303030313030303030303030303030303030303429202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030342929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303035202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203530302030202378303030303030303030303030303030313030303030303030303030303030303529202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030352929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303036202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203630302030202378303030303030303030303030303030313030303030303030303030303030303629202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030362929290a
|
||||
textproj.document reject extension-chunk-order extension_chunks_reversed 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document reject missing-trailing-lf final_lf_missing 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e20312028292074727565292929
|
||||
|
|
|
|||
Loading…
Reference in New Issue