G2b coverage repairs: the second stale rationale, and three guards that could not fail
Closes four coverage gaps found in review. No behavioral change to the reducer,
codec, or wire.
ids.rs carried the same falsehood bundle.rs did - major 3 is snapshot-only, no
operation payload embeds the tuning context, the op-block accept-set is
untouched. t10 grepped only bundle.rs, so it could not see it. Rewritten, with
a pointer to its sibling so the pair stays together.
The text-projection corpus covered no set-tuning-context document: every
accepted document built DeleteRegion envelopes, and G2b only regenerated
headers to 0.11.0. Adds a real one, carried at minor 10. The typed all-kind
round-trip proves the production parses; only a committed vector proves the
emitted text is stable across implementations, which is what that corpus is
for.
Fixing it exposed a latent fragility worth more than the fix: the negative
vectors bound their source documents by positional index into the accept list,
so inserting a document silently repointed every reject builder at the wrong
text. Converted to by-name binding, which is what made the insertion safe.
The generator guard stopped at kind 33 - the very test written because
TransposeInterval and CreateInstrument had gone missing from every corpus while
downstream suites stayed green. It now records and asserts SetTuningContext, so
removing or rerouting the new arm cannot leave it green.
t3 and t4 did not test block behavior. t3 computed max(schema_major()) over
bare OperationKinds without building a block; t4 asserted only the ceiling
constant. Neither would notice a mis-derived stamp or a gate that rejected the
block it now admits. Adds one integration test that stages a real envelope
through stage_operation_block and reopens the bundle, asserting {3, 10} and
read-write. Verified by mutation: reverting the accept-set to 2 fails it.
Also three stale counts, each the hand-maintained-parallel-list shape: the
textproj production count said 31 and had been stale since the original
text-projection commit (now 35), the G-minor epoch table omitted kind 34, and
the corpus count was pinned at 13.
Gate: 1410 tests, clippy 0, fmt clean, conformance 8/8, 14 textproj vectors,
105 decode vectors, git diff --check clean.
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
13c3d2fd46
commit
d685b641cd
|
|
@ -191,8 +191,16 @@ impl SchemaVersion {
|
|||
/// Schema major 3 — the third data-model expansion major (Binary Format
|
||||
/// companion §"Schema Major 3", Push 4b tranche 3b-i): `ScoreTuningContext`
|
||||
/// gains `smufl` and `overrides` on the wire. Stamped on the acceleration
|
||||
/// full-`Score` snapshot only — no operation payload embeds the tuning
|
||||
/// context, so `OperationEnvelopeBlock`'s accept-set is untouched.
|
||||
/// full-`Score` snapshot **and** on any operation-envelope block carrying a
|
||||
/// `SetTuningContext` payload.
|
||||
///
|
||||
/// **Genesis tranche G2b raised `OperationEnvelopeBlock`'s accept-set to
|
||||
/// `[0, 3]`** (`spec/CONTRACT_GENESIS_G2B_TUNING.md`). This comment
|
||||
/// previously said major 3 was snapshot-only because no operation payload
|
||||
/// embedded the tuning context; `SetTuningContext` (kind/tag 34) is exactly
|
||||
/// that payload, carrying `epiphany_core::TuningContextSettings`, so the
|
||||
/// claim no longer holds. See `max_supported_major` in `bundle.rs`, which
|
||||
/// carried the same falsehood.
|
||||
pub const V3: SchemaVersion = SchemaVersion { major: 3, minor: 0 };
|
||||
|
||||
/// Constructs a schema version.
|
||||
|
|
|
|||
|
|
@ -2470,6 +2470,9 @@ mod tests {
|
|||
(OperationKindTag::CreateInstrument, 8),
|
||||
(OperationKindTag::SetCanvasLayoutDefaults, 9),
|
||||
(OperationKindTag::SetSpellingPrecedence, 9),
|
||||
// Genesis G2b. Kept in step with pin 1's table; an epoch omitted
|
||||
// here is an epoch this test cannot see go wrong.
|
||||
(OperationKindTag::SetTuningContext, 10),
|
||||
] {
|
||||
assert_eq!(
|
||||
tag.introduced_minor(),
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ fn parse_bytes(s: &Sexp) -> Result<Vec<u8>, TextError> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Projects and strictly parses all 31 `kind` productions from
|
||||
/// Projects and strictly parses all 35 `kind` productions from
|
||||
/// `spec/text_projection.tex`.
|
||||
impl TextValue for OperationKind {
|
||||
fn project(&self) -> Sexp {
|
||||
|
|
|
|||
|
|
@ -1837,7 +1837,9 @@ mod tests {
|
|||
/// where that set's ceiling sits.
|
||||
///
|
||||
/// The accept-set ceiling itself (`max_supported_major(
|
||||
/// OperationEnvelopeBlock) == 2`) is **already** asserted in a compiled
|
||||
/// OperationEnvelopeBlock)`, raised from 2 to **3** by genesis tranche
|
||||
/// G2b, which introduced the first operation payload born at major 3)
|
||||
/// is **already** asserted in a compiled
|
||||
/// test inside `epiphany-bundle` (`bundle.rs:1322`), which predates this
|
||||
/// packet and which this packet leaves untouched. An earlier draft of
|
||||
/// this comment claimed that half of s6 was unassertable because
|
||||
|
|
@ -1910,6 +1912,7 @@ mod tests {
|
|||
let mut rng = Rng::new(17);
|
||||
let (mut saw_transpose_interval, mut saw_create_instrument) = (false, false);
|
||||
let (mut saw_canvas_layout_defaults, mut saw_spelling_precedence) = (false, false);
|
||||
let mut saw_tuning_context = false;
|
||||
for _ in 0..2000 {
|
||||
let OperationPayload::Primitive(kind) = operation_payload(&mut rng, 8, 8) else {
|
||||
continue;
|
||||
|
|
@ -1919,6 +1922,7 @@ mod tests {
|
|||
OperationKind::CreateInstrument(_) => saw_create_instrument = true,
|
||||
OperationKind::SetCanvasLayoutDefaults(_) => saw_canvas_layout_defaults = true,
|
||||
OperationKind::SetSpellingPrecedence(_) => saw_spelling_precedence = true,
|
||||
OperationKind::SetTuningContext(_) => saw_tuning_context = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1938,6 +1942,10 @@ mod tests {
|
|||
saw_spelling_precedence,
|
||||
"SetSpellingPrecedence (kind 33) never drawn in 2000 samples"
|
||||
);
|
||||
assert!(
|
||||
saw_tuning_context,
|
||||
"SetTuningContext (kind 34, G2b debt) never drawn in 2000 samples"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -683,6 +683,52 @@ mod tests {
|
|||
assert_eq!(blocks, vec![env.to_canonical_bytes()]);
|
||||
}
|
||||
|
||||
/// (G2b, t3+t4 as an integration) A **real** `SetTuningContext` envelope,
|
||||
/// staged through the production writer path and reopened, must stamp
|
||||
/// `{3, 10}` and be admitted **read-write**.
|
||||
///
|
||||
/// The unit-level t3/t4 did not prove this. t3 computed
|
||||
/// `max(schema_major())` over bare `OperationKind`s — never building a
|
||||
/// block — and t4 only asserted the ceiling constant. Both would stay
|
||||
/// green if `stage_operation_block` mis-derived the stamp or if the
|
||||
/// accept-set gate rejected the block it now admits. This test is the one
|
||||
/// that actually crosses ops → testkit → bundle.
|
||||
#[test]
|
||||
fn a_set_tuning_context_block_stamps_3_10_and_reopens_read_write() {
|
||||
use epiphany_core::{OperationId, ReplicaId, WallClockTime};
|
||||
use epiphany_ops::{
|
||||
valuegen, AuthorId, CausalContext, HybridLogicalClock, OperationEnvelope,
|
||||
OperationKind, OperationPayload, OperationStamp, SetTuningContextOp,
|
||||
};
|
||||
|
||||
let id = OperationId::new(ReplicaId(1), 1);
|
||||
let env = OperationEnvelope {
|
||||
id,
|
||||
author: AuthorId(0xAB),
|
||||
stamp: OperationStamp::new(HybridLogicalClock::new(WallClockTime(100), 0), id),
|
||||
causal_context: CausalContext::new(),
|
||||
transaction: None,
|
||||
payload: OperationPayload::Primitive(OperationKind::SetTuningContext(
|
||||
SetTuningContextOp {
|
||||
settings: valuegen::tuning_context_settings(3),
|
||||
},
|
||||
)),
|
||||
};
|
||||
|
||||
let staged = crate::bundle_harness::stage_operation_block(&[env]);
|
||||
assert_eq!(
|
||||
staged.schema_version,
|
||||
SchemaVersion::new(3, 10),
|
||||
"a block carrying SetTuningContext stamps major 3 (the payload is born at v3) and minor 10 (its ratified G2b epoch)"
|
||||
);
|
||||
|
||||
let reopened = reopen_with_op_block(0xD2_0003, staged);
|
||||
assert!(
|
||||
!reopened.is_read_only(),
|
||||
"major 3 is inside the raised op-block accept-set [0, 3], so the bundle must open read-write"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op_block_beyond_the_accept_set_opens_read_only() {
|
||||
use epiphany_bundle::IntegrityAnomaly;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use epiphany_bundle::{
|
|||
use epiphany_core::{OperationId, RegionId, ReplicaId, WallClockTime};
|
||||
use epiphany_ops::{
|
||||
AuthorId, CausalContext, DeleteRegionOp, HybridLogicalClock, OperationEnvelope, OperationKind,
|
||||
OperationPayload, OperationStamp,
|
||||
OperationPayload, OperationStamp, SetTuningContextOp,
|
||||
};
|
||||
|
||||
use crate::parse::parse_document;
|
||||
|
|
@ -158,6 +158,25 @@ fn sample_envelope(counter: u64, physical_time: i64) -> OperationEnvelope {
|
|||
}
|
||||
}
|
||||
|
||||
/// A `SetTuningContext` envelope (kind 34, genesis G2b). The committed corpus
|
||||
/// must contain a real `set-tuning-context` document: the typed all-kind
|
||||
/// round-trip test proves the *production* parses, but only a committed vector
|
||||
/// proves the emitted document text is stable across implementations, which is
|
||||
/// what this corpus exists for.
|
||||
fn tuning_context_envelope(counter: u64, physical_time: i64) -> OperationEnvelope {
|
||||
let id = OperationId::new(ReplicaId(1), counter);
|
||||
OperationEnvelope {
|
||||
id,
|
||||
author: AuthorId(0xAB),
|
||||
stamp: OperationStamp::new(HybridLogicalClock::new(WallClockTime(physical_time), 0), id),
|
||||
causal_context: CausalContext::new(),
|
||||
transaction: None,
|
||||
payload: OperationPayload::Primitive(OperationKind::SetTuningContext(SetTuningContextOp {
|
||||
settings: epiphany_ops::valuegen::tuning_context_settings(7),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn profiles(custom: bool) -> Vec<ProfileDeclaration> {
|
||||
let mut profiles = vec![ProfileDeclaration::full()];
|
||||
if custom {
|
||||
|
|
@ -248,8 +267,22 @@ fn accept_documents() -> Vec<(&'static str, String)> {
|
|||
],
|
||||
};
|
||||
|
||||
// Genesis G2b: kind 34 in the committed corpus, carried at minor 10 —
|
||||
// the epoch a block containing this operation actually requires.
|
||||
let tuning_context = TextDocument {
|
||||
document_id: DocumentId([5; 16]),
|
||||
manifest_schema_version: SchemaVersion::new(0, 10),
|
||||
lineage_id: None,
|
||||
profiles: profiles(false),
|
||||
extensions: Vec::new(),
|
||||
canonical_base: None,
|
||||
blobs: Vec::new(),
|
||||
envelopes: vec![tuning_context_envelope(7, 700)],
|
||||
};
|
||||
|
||||
vec![
|
||||
("minimal", project_text_document(&minimal)),
|
||||
("set_tuning_context", project_text_document(&tuning_context)),
|
||||
(
|
||||
"lineage_custom_profile",
|
||||
project_text_document(&lineage_custom),
|
||||
|
|
@ -319,10 +352,23 @@ fn duplicate_first_line(text: &str, head: &str) -> String {
|
|||
/// Every whole-document vector in stable committed order.
|
||||
pub fn document_vectors() -> Vec<TextVector> {
|
||||
let accepts = accept_documents();
|
||||
let minimal = &accepts[0].1;
|
||||
let lineage_custom = &accepts[1].1;
|
||||
let extension_base_multi = &accepts[2].1;
|
||||
let rich = &accepts[3].1;
|
||||
// Bound **by name, not by index**. These were positional (`accepts[0]`..
|
||||
// `accepts[3]`), which silently coupled every negative vector to the
|
||||
// insertion order of the accept list: adding the G2b
|
||||
// `set_tuning_context` document shifted all four and made the reject
|
||||
// builders operate on the wrong source text. By-name binding makes the
|
||||
// accept list reorderable and extendable without touching this block.
|
||||
let by_name = |name: &str| -> &String {
|
||||
&accepts
|
||||
.iter()
|
||||
.find(|(n, _)| *n == name)
|
||||
.unwrap_or_else(|| panic!("accept document is absent: {name}"))
|
||||
.1
|
||||
};
|
||||
let minimal = by_name("minimal");
|
||||
let lineage_custom = by_name("lineage_custom_profile");
|
||||
let extension_base_multi = by_name("extension_base_two_envelopes");
|
||||
let rich = by_name("rich_document");
|
||||
|
||||
let mut vectors: Vec<TextVector> = accepts
|
||||
.iter()
|
||||
|
|
@ -659,7 +705,7 @@ mod tests {
|
|||
#[test]
|
||||
fn the_reference_implementation_agrees_with_every_vector() {
|
||||
match verify(COMMITTED) {
|
||||
Ok(count) => assert_eq!(count, 13, "the corpus has unexpectedly thinned"),
|
||||
Ok(count) => assert_eq!(count, 14, "the corpus has unexpectedly thinned"),
|
||||
Err(failures) => panic!(
|
||||
"{} disagreement(s):\n{}",
|
||||
failures.len(),
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
# textproj.document
|
||||
textproj.document accept - minimal 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830313031303130313031303130313031303130313031303130313031303130312028736368656d612030203129290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a
|
||||
textproj.document accept - set_tuning_context 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830353035303530353035303530353035303530353035303530353035303530352028736368656d61203020313029290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303037202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203730302030202378303030303030303030303030303030313030303030303030303030303030303729202863617573616c2028292028292920282920287072696d697469766520287365742d74756e696e672d636f6e74657874202874756e696e672d636f6e746578742d73657474696e67732022636d6e2d31322220227465742d31322220287265666572656e63652d70697463682028636d6e2061203020342920237830303030303030303030663037623430292028736d75666c2d76657273696f6e2d726571756972656d656e742028736d75666c2d76657273696f6e2031203430292028736d75666c2d76657273696f6e20312034302929202829292929290a
|
||||
textproj.document accept - lineage_custom_profile 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830323032303230323032303230323032303230323032303230323032303230322028736368656d612030203129290a286c696e656167652023783132313231323132313231323132313231323132313231323132313231323132290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303031202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203130302030202378303030303030303030303030303030313030303030303030303030303030303129202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030312929290a
|
||||
textproj.document accept - extension_base_two_envelopes 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830333033303330333033303330333033303330333033303330333033303330332028736368656d612030203829290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a2863616e6f6e6963616c2d62617365202378303330333033303330333033303330333033303330333033303330333033303320237830313032303320312066756c6c2028736368656d61203020312920237861303033290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303032202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203230302030202378303030303030303030303030303030313030303030303030303030303030303229202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030322929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303033202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203330302030202378303030303030303030303030303030313030303030303030303030303030303329202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030332929290a
|
||||
textproj.document accept - rich_document 28746578742d70726f6a656374696f6e202830203131203029290a28646f63756d656e7420237830343034303430343034303430343034303430343034303430343034303430342028736368656d612030203129290a286c696e656167652023783134313431343134313431343134313431343134313431343134313431343134290a2870726f66696c652066756c6c20283020312030292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a2870726f66696c652028637573746f6d20237863636363636363636363636363636363636363636363636363636363636363632920283120322033292028636f6e73747261696e74732036373130383836342028726574656e74696f6e203120282920747275652929290a28657874656e73696f6e202378303130313031303130313031303130313031303130313031303130313031303120283120302031292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830312920286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830322929202378303120237830313032290a28657874656e73696f6e202378303230323032303230323032303230323032303230323032303230323032303220283120302032292066616c73652028286368756e6b20657874656e73696f6e2d646174612028736368656d61203020312920237830332929202378303220237830323033290a2863616e6f6e6963616c2d62617365202378303430343034303430343034303430343034303430343034303430343034303420237830313032303420312066756c6c2028736368656d61203020312920237861303034290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303034202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203430302030202378303030303030303030303030303030313030303030303030303030303030303429202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030342929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303035202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203530302030202378303030303030303030303030303030313030303030303030303030303030303529202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030352929290a28656e76656c6f70652023783030303030303030303030303030303130303030303030303030303030303036202378303030303030303030303030303030303030303030303030303030303030616220287374616d70203630302030202378303030303030303030303030303030313030303030303030303030303030303629202863617573616c2028292028292920282920287072696d6974697665202864656c6574652d726567696f6e20237830303030303030303030303030303031303030303030303030303030303030362929290a
|
||||
|
|
|
|||
Loading…
Reference in New Issue