Delete every committed mutation suite
Better to have nothing than a harness that punishes refactoring (operator ruling, 2026-09-22). Remove all eight tools/*mutations*.psd1 files (921 lines total): cut5, cut6, cut6b, cut6c, cut6d, cut8-epiphany, cut10-epiphany, readside-leaf. The harness that ran them, eureka-mutations.ps1, was already deleted from the Eureka skill (37c80e4). notes/eureka-body-plane-cut.md, notes/eureka-pipeline-state-cut.md and notes/eureka-read-side-cut.md still name these suites; they are Self's campaign records and are left untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
5f916b6974
commit
703fdd0bf7
8 changed files with 0 additions and 1021 deletions
|
|
@ -1,69 +0,0 @@
|
|||
# Eureka Cut 10 mutation suite, the Epiphany half: the public slug door,
|
||||
# `Slug::validate_slug`, filled into its owner by Self's ruling on the F6
|
||||
# fork. Entries only; the harness moved out of this repo, 2026-09-17, to
|
||||
# `C:\Users\Meta\.claude\skills\eureka\tools\eureka-mutations.ps1`
|
||||
# (`GameCult/Eureka`), so one copy serves every campaign. It takes `-Repo`
|
||||
# when run from outside that checkout.
|
||||
#
|
||||
# powershell -File C:\Users\Meta\.claude\skills\eureka\tools\eureka-mutations.ps1 `
|
||||
# -Repo F:\Projects\Epiphany `
|
||||
# -Entries tools/eureka-cut10-epiphany-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# G1 is the revert: the door answers `Ok` to everything, which is exactly the
|
||||
# gap Huginn's throwaway `PipelineInstance` wrapper used to paper over. G1L is
|
||||
# a loosening reachable through the same door as the leaf's other text
|
||||
# fields: admitting a lone backslash inside one label. The fixture
|
||||
# `outer\inner` has no empty label for the loosening to hide behind, so it
|
||||
# dies only on the backslash byte itself.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'G1'
|
||||
Rule = 'The public slug door applies the crate''s own grammar: a declared name outside the crate is held to the same check every Slug field is (Self''s ruling on the F6 fork).'
|
||||
Test = 'tests::slug_validate_applies_the_dotted_grammar'
|
||||
Old = ' pub fn validate_slug(&self) -> Result<(), PipelineRefusal> {
|
||||
Bounded::validate(self, "slug")
|
||||
}'
|
||||
New = ' pub fn validate_slug(&self) -> Result<(), PipelineRefusal> {
|
||||
let _ = self;
|
||||
Ok(())
|
||||
}'
|
||||
},
|
||||
@{
|
||||
Id = 'G1L'
|
||||
Rule = 'A label byte is exactly `[A-Za-z0-9_-]`: a lone backslash inside one label is refused, not just a backslash beside an empty label.'
|
||||
Test = 'tests::slug_validate_applies_the_dotted_grammar'
|
||||
Old = ' .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b''_'' | b''-''));'
|
||||
New = ' .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b''_'' | b''-'' | b''\\''));'
|
||||
},
|
||||
@{
|
||||
Id = 'S5whole'
|
||||
Rule = 'The whole-name bound is 64 bytes: `dotted_text` refuses a value one byte past it, even when every individual label sits far under 64.'
|
||||
Test = 'tests::slug_validate_applies_the_dotted_grammar'
|
||||
Old = 'fn dotted_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
if value.is_empty() || value.len() > 64 {
|
||||
return Err(format_error(field, value));
|
||||
}'
|
||||
New = 'fn dotted_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
if value.is_empty() {
|
||||
return Err(format_error(field, value));
|
||||
}'
|
||||
},
|
||||
@{
|
||||
Id = 'S5label'
|
||||
Rule = 'The per-label bound is 64 bytes too, the same check `Label` is held to on its own: a lone label carrying no dot is refused one byte past it.'
|
||||
Test = 'tests::slug_validate_applies_the_dotted_grammar'
|
||||
Old = ' if !valid || value.is_empty() || value.len() > 64 {'
|
||||
New = ' if !valid || value.is_empty() {'
|
||||
},
|
||||
@{
|
||||
Id = 'S5nul'
|
||||
Rule = 'NUL is refused as a label byte, not merely as one more character `is_ascii_alphanumeric` happens to reject.'
|
||||
Test = 'tests::slug_validate_applies_the_dotted_grammar'
|
||||
Old = ' .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b''_'' | b''-''));'
|
||||
New = ' .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b''_'' | b''-'' | 0u8));'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
# Eureka Cut 5 mutation suite. Entries only; the harness is
|
||||
# tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-cut5-mutations.psd1 `
|
||||
# -Target epiphany-core/src/reasoning_context.rs,epiphany-core/src/runtime_spine.rs `
|
||||
# -Test 'cargo test -p epiphany-core --lib'
|
||||
#
|
||||
# Cut 5 collapsed `TypedCommitStore` into `commit_authorized_mind_mutation`.
|
||||
# The collapse is only safe if the rules the profile's tests used to pin are
|
||||
# still pinned against the concrete owner. M1-M5 are the collapse's own rules.
|
||||
# MS1 and MS2 close Soul's findings S7 and S8: each is a mutation the suite
|
||||
# used to survive, kept here so the gap cannot reopen.
|
||||
#
|
||||
# Two of the rules are about the *order* of two statements, and removing a
|
||||
# statement and reinserting it elsewhere is two edits, so those entries carry
|
||||
# an `Edits` list. The validation loop they move is
|
||||
#
|
||||
# for write in &writes {
|
||||
# crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
# }
|
||||
#
|
||||
# written out in each entry, since a data file cannot share it.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'M1'
|
||||
Rule = 'The store opener refuses a store at a foreign schema epoch, before the commit can write to it.'
|
||||
Test = 'reasoning_context::tests::mind_commit_refuses_a_foreign_epoch_store'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/runtime_spine.rs'
|
||||
Old = ' validate_runtime_store_epoch(&backing_store.pull_all()?)?;'
|
||||
New = ' let _ = &backing_store;'
|
||||
}
|
||||
)
|
||||
},
|
||||
@{
|
||||
Id = 'M2'
|
||||
Rule = 'The batch is validated against the Mind document rules at all. MS2 pins that every write is.'
|
||||
Test = 'reasoning_context::tests::mind_commit_keeps_validation_receipts_replay_and_conflicts'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = @'
|
||||
for write in &writes {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
for write in &writes {
|
||||
let _ = write;
|
||||
}
|
||||
'@
|
||||
}
|
||||
)
|
||||
},
|
||||
@{
|
||||
Id = 'M3'
|
||||
Rule = 'The owner resolves one backing store and both reads and writes that one.'
|
||||
Test = 'reasoning_context::tests::mind_commit_reads_and_writes_one_store'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = ' if backing_store.compare_and_swap_batch(&expected, replacements)? {'
|
||||
New = ' if runtime_spine_backing_store(&store_path.with_file_name("other.cc"))?.compare_and_swap_batch(&expected, replacements)? {'
|
||||
}
|
||||
)
|
||||
},
|
||||
# Ruling 12 order, first half. Hoisting validation above the uniqueness
|
||||
# checks changes which refusal a caller is told about for a batch that
|
||||
# is both duplicated and invalid.
|
||||
@{
|
||||
Id = 'M4'
|
||||
Rule = 'Identity uniqueness is checked before the writes are validated.'
|
||||
Test = 'reasoning_context::tests::mind_commit_refuses_repeated_write_identities_before_validating'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = @'
|
||||
for write in &writes {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
},
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = ' validate_unique_envelope_identities(&strong_reads, "strong read")?;'
|
||||
New = @'
|
||||
for write in &writes {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
validate_unique_envelope_identities(&strong_reads, "strong read")?;
|
||||
'@
|
||||
}
|
||||
)
|
||||
},
|
||||
# Ruling 12 order, second half, and Soul's finding S6. Moving validation
|
||||
# below the replay answer lets a stored receipt answer a batch the
|
||||
# current validator would refuse.
|
||||
@{
|
||||
Id = 'M5'
|
||||
Rule = 'Writes are validated before a stored receipt may answer a replay.'
|
||||
Test = 'reasoning_context::tests::mind_commit_validates_before_answering_a_replay'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = @'
|
||||
for write in &writes {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
},
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = @'
|
||||
return Ok(EpiphanyMindCommitOutcome::Committed(existing));
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
return Ok(EpiphanyMindCommitOutcome::Committed(existing));
|
||||
}
|
||||
for write in &writes {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
'@
|
||||
}
|
||||
)
|
||||
},
|
||||
# Soul's finding S7. M3 sends the CAS to a *different* path, which
|
||||
# leaves a second file the one-store test can see. This sends it to the
|
||||
# same path, which leaves no trace in the directory at all and survived
|
||||
# M3's test. It is still a second handle, and the map's redb plan makes
|
||||
# that fatal: redb permits one writable handle per path.
|
||||
@{
|
||||
Id = 'MS1'
|
||||
Rule = 'The owner resolves the backing store once and reuses that handle, rather than re-deriving it.'
|
||||
Test = 'reasoning_context::tests::mind_commit_reads_and_writes_one_store'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = ' if backing_store.compare_and_swap_batch(&expected, replacements)? {'
|
||||
New = ' if runtime_spine_backing_store(store_path)?.compare_and_swap_batch(&expected, replacements)? {'
|
||||
}
|
||||
)
|
||||
},
|
||||
# Soul's finding S8. M2 neuters validation for every write, which any
|
||||
# single-write batch catches. This keeps validation and narrows it to
|
||||
# the first write, which nothing caught until a batch of two carried an
|
||||
# invalid second write.
|
||||
@{
|
||||
Id = 'MS2'
|
||||
Rule = 'Every write in the batch is validated, not just the first.'
|
||||
Test = 'reasoning_context::tests::mind_commit_validates_every_write_in_the_batch'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'epiphany-core/src/reasoning_context.rs'
|
||||
Old = @'
|
||||
for write in &writes {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
for write in writes.iter().take(1) {
|
||||
crate::mind_documents::validate_mind_write_envelope(write)?;
|
||||
}
|
||||
'@
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
# Eureka Cut 6 mutation suite. Entries only; the harness is
|
||||
# tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-cut6-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs,epiphany-core/src/runtime_spine.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# M1-M9 are Cut 4's suite re-anchored: the code they mutate moved from
|
||||
# `epiphany-core/src/pipeline_documents.rs` to `epiphany-pipeline/src/lib.rs`.
|
||||
# M10 still mutates the spine in epiphany-core, so it carries its own command.
|
||||
# M11 is the first of the two the cut named for the kinds it added.
|
||||
#
|
||||
# Five entries Cut 6b's grammar made stale are gone rather than re-anchored;
|
||||
# each note below names where the rule is pinned now.
|
||||
#
|
||||
# - M5, "a finding label carries no dot": the finding arm no longer validates
|
||||
# the label itself; the label goes through `local` as its last part, which
|
||||
# refuses a dotted part as `finding.key`. Cut 6b's F5 accepted that
|
||||
# refusal; the rule is pinned on the composer by Cut 6b's N3 (the last part
|
||||
# skips the label check) and M6 (the head does), and through the documents
|
||||
# by `composed_keys_cannot_collide`.
|
||||
# - M12, "a repo inside a key is escaped", and M13, "the repo escape is
|
||||
# injective": `key_segment` replaced `repo_segment`; each is re-anchored on
|
||||
# it as Cut 6b's N7 (the escape is the identity) and N8 (`/` to a bare `_`,
|
||||
# `_` unescaped), both killed by `stewardship_key_escapes_the_repo_slash`.
|
||||
# - M14, "an instance's slug is validated where its key is composed": the
|
||||
# instance arm composes through the shared root check; pinned by Cut 6b's
|
||||
# M5 and M1.
|
||||
# - M15, "the id reader knows the same roots the key writer does": every kind
|
||||
# reads back with no root excused; pinned by Cut 6b's N6 (the reader's kind
|
||||
# check excuses the Instance kind), killed by
|
||||
# `keys_read_back_as_ids_of_their_kind` reading a campaign key as an
|
||||
# instance, and by Cut 6b's M1.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'M1'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'A document decodes back only from an envelope of its own type id.'
|
||||
Test = 'tests::every_pipeline_kind_round_trips_through_named_slot_zero'
|
||||
Old = '$(if envelope.r#type == <$document as DatabaseEntry>::TYPE {'
|
||||
New = '$(if envelope.r#type != <$document as DatabaseEntry>::TYPE {'
|
||||
},
|
||||
@{
|
||||
Id = 'M2'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'Text bounds are measured in UTF-8 bytes, not characters.'
|
||||
Test = 'tests::bounds_refuse_in_utf8_bytes'
|
||||
Old = ' move |field: &str, value: &str| bound(field, limit, value.len())'
|
||||
New = ' move |field: &str, value: &str| bound(field, limit, value.chars().count())'
|
||||
},
|
||||
@{
|
||||
Id = 'M3'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'An OrgRepo carries exactly one slash.'
|
||||
Test = 'tests::repo_fields_must_be_org_slash_repo'
|
||||
Old = " if !org.is_empty() && !repo.is_empty() && !repo.contains('/') && value.len() <= 200 =>"
|
||||
New = ' if !org.is_empty() && !repo.is_empty() && value.len() <= 200 =>'
|
||||
},
|
||||
@{
|
||||
Id = 'M4'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'A write envelope''s key is recomputed from the value and a mismatch refuses.'
|
||||
Test = 'tests::keys_are_derived_and_mismatch_refuses'
|
||||
Old = ' if envelope.key != expected {'
|
||||
New = ' if false {'
|
||||
},
|
||||
@{
|
||||
Id = 'M6'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'A parent local must carry its own kind''s marker character.'
|
||||
Test = 'tests::parent_ids_are_parsed_strictly'
|
||||
Old = ' let digits = suffix.strip_prefix(marker).ok_or_else(invalid)?;'
|
||||
New = ' let digits = suffix.strip_prefix(marker).unwrap_or(suffix);'
|
||||
},
|
||||
@{
|
||||
Id = 'M7'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'A PipelineRef''s id is parsed as a full id of its declared kind.'
|
||||
Test = 'tests::resolution_subject_is_a_full_id_of_its_kind'
|
||||
Old = ' pipeline_id(&format!("{at}.id"), &self.id.0, self.kind).map(|_| ())'
|
||||
New = ' Ok(())'
|
||||
},
|
||||
@{
|
||||
Id = 'M8'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'Published schemas are the Rust derivation, byte for byte.'
|
||||
Test = 'tests::pipeline_published_schemas_match_derivation'
|
||||
Old = ' Short = 200, |field, value| within(200)(field, value);'
|
||||
New = ' Short = 201, |field, value| within(200)(field, value);'
|
||||
},
|
||||
# Soul F2. M1 removes the positive match; this removes the refusal that
|
||||
# catches everything the positive match misses, leaving a decode that
|
||||
# says "malformed payload" about an envelope that was never a document.
|
||||
@{
|
||||
Id = 'M9'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'An envelope whose type id belongs to another kind is refused as ForeignStore.'
|
||||
Test = 'tests::decode_refuses_an_envelope_of_a_foreign_type'
|
||||
Old = 'Err(PipelineRefusal::ForeignStore { r#type: envelope.r#type.clone() })'
|
||||
New = 'Err(format_error("payload", &envelope.r#type))'
|
||||
},
|
||||
# Soul F4. The spine cache refuses a pipeline store by registering its
|
||||
# own types and nothing else, so the mutation that removes the rule is
|
||||
# the one that registers a pipeline type into it. epiphany-core no
|
||||
# longer has the documents, so the mutation declares its own type
|
||||
# carrying the same type id, exactly as the test does.
|
||||
@{
|
||||
Id = 'M10'
|
||||
File = 'epiphany-core/src/runtime_spine.rs'
|
||||
Command = 'cargo test -p epiphany-core --lib'
|
||||
Rule = 'The runtime spine cache refuses an epiphany.pipeline.* envelope.'
|
||||
Test = 'runtime_spine::tests::runtime_spine_cache_refuses_a_pipeline_store'
|
||||
Old = @'
|
||||
fn runtime_spine_schema_cache() -> Result<CultCache> {
|
||||
let mut cache = CultCache::new();
|
||||
'@
|
||||
New = @'
|
||||
fn runtime_spine_schema_cache() -> Result<CultCache> {
|
||||
let mut cache = CultCache::new();
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.pipeline.campaign.v1",
|
||||
schema = "EpiphanyPipelineCampaignDocument"
|
||||
)]
|
||||
struct RegisteredPipelineDocument {
|
||||
#[cultcache(key = 0)]
|
||||
slug: String,
|
||||
}
|
||||
cache.register_entry_type::<RegisteredPipelineDocument>()?;
|
||||
'@
|
||||
},
|
||||
# The cut's first named mutation: change a value type's shape and leave
|
||||
# the published schema alone. A field cannot be added without breaking
|
||||
# every struct literal that builds the value, which would fail the whole
|
||||
# target and prove nothing about this test in particular, so the shape
|
||||
# change is a new enum variant: it compiles, and it changes exactly one
|
||||
# derived schema.
|
||||
@{
|
||||
Id = 'M11'
|
||||
File = 'epiphany-pipeline/src/lib.rs'
|
||||
Rule = 'A change to a value type''s shape is caught unless its schema is regenerated.'
|
||||
Test = 'tests::pipeline_published_schemas_match_derivation'
|
||||
Old = ' FindingSeverity { Blocker, High, Medium, Low }'
|
||||
New = ' FindingSeverity { Blocker, High, Medium, Low, Cosmetic }'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
# Eureka Cut 6b mutation suite: the key grammar. Entries only; the harness is
|
||||
# tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-cut6b-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# M1-M6 are the cut's own rules. S2, S4 and S5 are the mutations Soul's pass
|
||||
# found surviving; each is kept exactly as Soul wrote it, against the test the
|
||||
# fix batch extended to kill it. N3, N6, N7, N8, N9 and N10 are Soul's second
|
||||
# pass: N7 and N8 re-anchor Cut 6's M12 and M13 on `key_segment`, N3 is the
|
||||
# analogue of Cut 6's M5 on `local`, N6 of Cut 6's M15 on the reader, and N9
|
||||
# and N10 are the dotted-root mutations that survived until
|
||||
# `dotted_roots_key_and_read_back`. X1 and X11 are Soul's third pass, the
|
||||
# reader trimming a trailing dot from the root or the local, killed by the
|
||||
# malformed fixtures in `keys_read_back_as_ids_of_their_kind`. The file holds
|
||||
# an `é` literal in `bounds_refuse_in_utf8_bytes`, which is why the harness
|
||||
# I/O is byte-exact.
|
||||
#
|
||||
# Soul's N4 moved the bound in `local` and both 64s in the depth test to 60
|
||||
# together and survived: the test restated the literal. `LOCAL_MAX` now names
|
||||
# the bound, `local` and the depth test both read it, and the mutation is no
|
||||
# longer expressible as a single-site edit, so it has no entry here.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'M1'
|
||||
Rule = 'Campaign and instance roots do not share one namespace (defect 1).'
|
||||
Test = 'tests::roots_of_different_kinds_do_not_share_a_key'
|
||||
Old = ' D::Campaign(value) => ("campaign.slug", value.slug.0.as_str(), local(&key_field, &[ROOT_LOCAL])?),'
|
||||
New = ' D::Campaign(value) => return Ok(value.slug.0.clone()),'
|
||||
},
|
||||
@{
|
||||
Id = 'M2'
|
||||
Rule = 'A resolution''s key carries its subject''s kind (defect 2).'
|
||||
Test = 'tests::a_resolution_names_its_subjects_kind'
|
||||
Old = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
New = ' let parts = subject_local.split(''.'').chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
},
|
||||
@{
|
||||
Id = 'M3'
|
||||
Rule = 'A resolution of a resolution composes a key the reader accepts (defect 3).'
|
||||
Test = 'tests::a_resolution_of_a_resolution_reads_back'
|
||||
Old = ' (field, subject_root, local(&key_field, &parts)?)'
|
||||
New = ' return Ok(format!("resolution:{}", value.subject.id.0))'
|
||||
},
|
||||
@{
|
||||
Id = 'M4'
|
||||
Rule = 'A composed local is bounded whole, not only per part.'
|
||||
Test = 'tests::a_composed_local_is_bounded_whole'
|
||||
Old = @'
|
||||
if joined.len() > LOCAL_MAX {
|
||||
return Err(format_error(field, &joined));
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
},
|
||||
@{
|
||||
Id = 'M5'
|
||||
Rule = 'The root segment is validated where the key is composed.'
|
||||
Test = 'tests::every_key_has_exactly_three_segments'
|
||||
Old = ' dotted_text(root_field, root)?;'
|
||||
New = ' let _ = root_field;'
|
||||
},
|
||||
@{
|
||||
Id = 'M6'
|
||||
Rule = 'No local part carries the separator, the head included (R2).'
|
||||
Test = 'tests::no_local_part_carries_the_separator'
|
||||
Old = @'
|
||||
for part in parts {
|
||||
label_text(field, part)?;
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
for (index, part) in parts.iter().enumerate() {
|
||||
if index == 0 { dotted_text(field, part)?; } else { label_text(field, part)?; }
|
||||
}
|
||||
'@
|
||||
},
|
||||
# Soul S2. The escape's `_` half was pinned only through an OrgRepo, so
|
||||
# an escape that skipped it for a value with no slash survived: `a_db`
|
||||
# and `a.b` both keyed to `a_db`. The receiver pair kills it.
|
||||
@{
|
||||
Id = 'S2'
|
||||
Rule = 'key_segment escapes `_` for a slug as well as a repo, so `a_db` and `a.b` are two receivers.'
|
||||
Test = 'tests::hand_off_names_both_instances'
|
||||
Old = ' value.replace(''_'', "__").replace(''/'', "_-").replace(''.'', "_d")'
|
||||
New = @'
|
||||
if !value.contains('/') {
|
||||
return value.replace('.', "_d");
|
||||
}
|
||||
value.replace('_', "__").replace('/', "_-").replace('.', "_d")
|
||||
'@
|
||||
},
|
||||
# Soul S4. The composer's root check was pinned by `a:b` alone, so a
|
||||
# check that trimmed a trailing dot first survived. The root fixtures
|
||||
# kill it.
|
||||
@{
|
||||
Id = 'S4'
|
||||
Rule = 'The composer''s root check refuses a trailing dot, not only a colon.'
|
||||
Test = 'tests::every_key_has_exactly_three_segments'
|
||||
Old = ' dotted_text(root_field, root)?;'
|
||||
New = ' dotted_text(root_field, root.trim_end_matches(''.''))?;'
|
||||
},
|
||||
# Soul S5. The whole-local bound was unpinned on both sides, so an
|
||||
# off-by-one survived. The 64- and 65-byte fixtures kill it.
|
||||
@{
|
||||
Id = 'S5'
|
||||
Rule = 'The whole-local bound is 64 exactly: 65 refuses.'
|
||||
Test = 'tests::a_composed_local_is_bounded_whole'
|
||||
Old = ' if joined.len() > LOCAL_MAX {'
|
||||
New = ' if joined.len() > LOCAL_MAX + 1 {'
|
||||
},
|
||||
# Soul N3, the analogue of Cut 6's M5 ("a finding label carries no
|
||||
# dot") on the composer: the join bound stays and the last part skips
|
||||
# `label_text`. A finding's label is the last part of its local.
|
||||
@{
|
||||
Id = 'N3'
|
||||
Rule = 'Every local part is a label, the last included (Cut 6 M5 on the composer).'
|
||||
Test = 'tests::no_local_part_carries_the_separator'
|
||||
Old = @'
|
||||
for part in parts {
|
||||
label_text(field, part)?;
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
for part in &parts[..parts.len() - 1] {
|
||||
label_text(field, part)?;
|
||||
}
|
||||
'@
|
||||
},
|
||||
# Soul N6, the analogue of Cut 6's M15 on the reader: the kind check
|
||||
# excuses the Instance kind. A campaign key read as an instance kills it.
|
||||
@{
|
||||
Id = 'N6'
|
||||
Rule = 'The reader checks the kind segment for a root kind as for every other (Cut 6 M15).'
|
||||
Test = 'tests::keys_read_back_as_ids_of_their_kind'
|
||||
Old = ' if name != kind.name() {'
|
||||
New = ' if name != kind.name() && kind != PipelineKind::Instance {'
|
||||
},
|
||||
# Soul N7, Cut 6's M12 re-anchored on `key_segment`: the escape is
|
||||
# the identity, so a repo's slash reaches the local and is refused.
|
||||
@{
|
||||
Id = 'N7'
|
||||
Rule = 'A repo inside a key is escaped (Cut 6 M12).'
|
||||
Test = 'tests::stewardship_key_escapes_the_repo_slash'
|
||||
Old = ' value.replace(''_'', "__").replace(''/'', "_-").replace(''.'', "_d")'
|
||||
New = ' value.to_string()'
|
||||
},
|
||||
# Soul N8, Cut 6's M13 re-anchored on `key_segment`: `/` becomes a
|
||||
# bare `_` and `_` is not escaped, so two repos claim one key.
|
||||
@{
|
||||
Id = 'N8'
|
||||
Rule = 'The repo escape is injective (Cut 6 M13).'
|
||||
Test = 'tests::stewardship_key_escapes_the_repo_slash'
|
||||
Old = ' value.replace(''_'', "__").replace(''/'', "_-").replace(''.'', "_d")'
|
||||
New = ' value.replace(''/'', "_").replace(''.'', "_d")'
|
||||
},
|
||||
# Soul N9 and N10: the root is a `Slug`, on the writer and the reader.
|
||||
# Both survived while no test keyed or read back a dotted root.
|
||||
@{
|
||||
Id = 'N9'
|
||||
Rule = 'The composer''s root check is a slug check: a dotted root keys.'
|
||||
Test = 'tests::dotted_roots_key_and_read_back'
|
||||
Old = ' dotted_text(root_field, root)?;'
|
||||
New = ' label_text(root_field, root)?;'
|
||||
},
|
||||
@{
|
||||
Id = 'N10'
|
||||
Rule = 'The reader''s root check is a slug check: a dotted root reads back.'
|
||||
Test = 'tests::dotted_roots_key_and_read_back'
|
||||
Old = ' dotted_text(field, root)?;'
|
||||
New = ' label_text(field, root)?;'
|
||||
},
|
||||
# Soul X1 and X11, the reader's halves of S4: a trailing dot on the
|
||||
# root or on the local was refused only by the writer, which never
|
||||
# composes one, so a reader that trimmed it survived. The malformed
|
||||
# fixtures read through `pipeline_id` kill both.
|
||||
@{
|
||||
Id = 'X1'
|
||||
Rule = 'The reader''s root check refuses a trailing dot; it does not trim one.'
|
||||
Test = 'tests::keys_read_back_as_ids_of_their_kind'
|
||||
Old = ' dotted_text(field, root)?;'
|
||||
New = ' dotted_text(field, root.trim_end_matches(''.''))?;'
|
||||
},
|
||||
@{
|
||||
Id = 'X11'
|
||||
Rule = 'The reader''s local check refuses a trailing dot; it does not trim one.'
|
||||
Test = 'tests::keys_read_back_as_ids_of_their_kind'
|
||||
Old = ' dotted_text(field, local)?;'
|
||||
New = ' dotted_text(field, local.trim_end_matches(''.''))?;'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
# Eureka Cut 6c mutation suite: the Ghostlight shapes. Entries only; the
|
||||
# harness is tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-cut6c-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# A retyping is the mutation-hostile case: the compiler catches the type
|
||||
# change, so the tempting test constructs a valid value and asserts `Ok(())`,
|
||||
# which stays green under every mutation below. Where the cut's content is a
|
||||
# type, the mutation is a type-level mutation (M19, M20, M22) and its killer
|
||||
# is a forgery the wider type admits. The key suite is blind to all of these
|
||||
# because a resolution's key is outcome-invariant.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'M16'
|
||||
Rule = 'A Superseded list is validated item by item, not only bounded: a wrong-kind referent inside it is refused.'
|
||||
Test = 'tests::resolution_outcome_referents_are_parsed_ids_of_their_kind'
|
||||
Old = ' Self::Superseded { by } => list(&format!("{field}.by"), by, 8),'
|
||||
New = ' Self::Superseded { by } => bound(&format!("{field}.by"), 8, by.len()),'
|
||||
},
|
||||
@{
|
||||
Id = 'M17'
|
||||
Rule = 'Every resolution referent is validated as a full id of the kind it declares.'
|
||||
Test = 'tests::resolution_outcome_referents_are_parsed_ids_of_their_kind'
|
||||
Old = @'
|
||||
match self {
|
||||
Self::Superseded { by } => list(&format!("{field}.by"), by, 8),
|
||||
Self::Answered { by } => by.validate(&format!("{field}.by")),
|
||||
Self::Fixed { commit, by } => {
|
||||
commit.validate(&format!("{field}.commit"))?;
|
||||
by.validate(&format!("{field}.by"))
|
||||
}
|
||||
Self::Deferred { to } => to.validate(&format!("{field}.to")),
|
||||
Self::Recorded { reason } | Self::Withdrawn { reason } => reason.validate(&format!("{field}.reason")),
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
let _ = field;
|
||||
Ok(())
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'M18'
|
||||
Rule = 'A fix''s commit is validated as a sha (ruling B): uppercase hex of legal length is refused.'
|
||||
Test = 'tests::fixed_resolution_requires_a_commit_sha'
|
||||
Old = ' commit.validate(&format!("{field}.commit"))?;'
|
||||
New = ' let _ = commit;'
|
||||
},
|
||||
@{
|
||||
Id = 'M19'
|
||||
Rule = 'A mutation record''s label is a Label, so it is key-safe: a dotted label is refused.'
|
||||
Test = 'tests::mutation_records_carry_a_dot_free_label_and_a_commit'
|
||||
Old = 'pub struct MutationRecord { label: Label,'
|
||||
New = 'pub struct MutationRecord { label: Short,'
|
||||
},
|
||||
@{
|
||||
Id = 'M20'
|
||||
Rule = 'A mutation record is pinned to a tree: its commit is a Sha, not free text.'
|
||||
Test = 'tests::mutation_records_carry_a_dot_free_label_and_a_commit'
|
||||
Old = 'commit: Sha, failed_as_expected: bool }'
|
||||
New = 'commit: Short, failed_as_expected: bool }'
|
||||
},
|
||||
# The two-line anchor carries the opener: the numbers alone also match
|
||||
# the report sample's StructuralDelta.
|
||||
@{
|
||||
Id = 'M21'
|
||||
Rule = 'The estimate''s two u32 fields are not interchangeable: the sample spec estimates a net addition.'
|
||||
Test = 'tests::the_sample_cut_spec_estimates_a_net_addition'
|
||||
Old = @'
|
||||
estimate: StructuralDelta {
|
||||
lines_added: 900, lines_removed: 0,
|
||||
'@
|
||||
New = @'
|
||||
estimate: StructuralDelta {
|
||||
lines_added: 0, lines_removed: 900,
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'M22'
|
||||
Rule = 'A verdict claim names at most eight mutations.'
|
||||
Test = 'tests::a_verdict_claim_names_the_promise_and_the_mutation_it_measured'
|
||||
Old = 'mutations: Vec<Label>[8]'
|
||||
New = 'mutations: Vec<Label>[16]'
|
||||
},
|
||||
# Soul's Cut 6c pass: three rules that survived, each now pinned by a
|
||||
# forgery the fix batch added. The ids are Soul's.
|
||||
@{
|
||||
Id = 'S2'
|
||||
Rule = 'A fix''s commit is validated whether or not a record is named: Fixed { by: Some(..) } does not excuse the sha.'
|
||||
Test = 'tests::fixed_resolution_requires_a_commit_sha'
|
||||
Old = ' commit.validate(&format!("{field}.commit"))?;'
|
||||
New = ' if by.is_none() { commit.validate(&format!("{field}.commit"))?; }'
|
||||
},
|
||||
@{
|
||||
Id = 'S14'
|
||||
Rule = 'The validator bounds a supersession at eight itself; the schema''s maxItems is not the only bound.'
|
||||
Test = 'tests::resolution_outcome_referents_are_parsed_ids_of_their_kind'
|
||||
Old = ' Self::Superseded { by } => list(&format!("{field}.by"), by, 8),'
|
||||
New = ' Self::Superseded { by } => list(&format!("{field}.by"), by, 9),'
|
||||
},
|
||||
@{
|
||||
Id = 'S10'
|
||||
Rule = 'A Recorded or Withdrawn reason is bounded as a Line.'
|
||||
Test = 'tests::resolution_outcome_referents_are_parsed_ids_of_their_kind'
|
||||
Old = ' Self::Recorded { reason } | Self::Withdrawn { reason } => reason.validate(&format!("{field}.reason")),'
|
||||
New = ' Self::Recorded { reason } | Self::Withdrawn { reason } => { let _ = reason; Ok(()) }'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
# Eureka Cut 6d mutation suite: resolution history and stewardship by
|
||||
# sequence. Entries only; the harness is tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-cut6d-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# Each entry restores the permissiveness the shape it pins removed: M23 and
|
||||
# M24 restore the one-per-subject and one-per-repo keys this cut retired, M25
|
||||
# and M26 put the sequence anywhere but last, and M27 drops a nested subject's
|
||||
# own sequence on the way into the key. S6 and S11 are Soul's, from the pass
|
||||
# over this suite: S6 copies a nested subject's sequence instead of using the
|
||||
# document's own, which every all-`1` fixture hides, and S11 is M27 for the
|
||||
# other sequenced kind, a stewardship subject. Cut 6b's M2 already covers the
|
||||
# subject's kind on the same line and is re-anchored there, not repeated here.
|
||||
#
|
||||
# Stated limits, so a later reader does not mistake silence for coverage. The
|
||||
# `n` marker itself has no entry: it is pinned only by the sample-key
|
||||
# equalities in `keys_are_derived_and_mismatch_refuses` and the two prefix
|
||||
# literals, a fixture pin like 6c's M21, and changing a marker restores no
|
||||
# permissiveness. The `u32` type has no runtime mutation. Date-invariance has
|
||||
# none of its own either: putting `assigned_on` into the stewardship key is
|
||||
# not a permissiveness the old code had, and M24's test asserts the local's
|
||||
# part count is exactly two, so a mutant that appends the date as a third part
|
||||
# dies there.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'M23'
|
||||
Rule = 'A subject''s resolutions are distinct records: the sequence is in the key (Q17 B).'
|
||||
Test = 'tests::a_subject_keeps_every_resolution_it_had'
|
||||
Old = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
New = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.split(''.'')).collect::<Vec<_>>();'
|
||||
},
|
||||
@{
|
||||
Id = 'M24'
|
||||
Rule = 'A repo''s stewardships on a mind are distinct records: the sequence is in the key (Q18 A, Q20 A).'
|
||||
Test = 'tests::a_repo_keeps_every_stewardship_it_had'
|
||||
Old = 'local(&key_field, &[&key_segment(&value.repo.0), &sequence])?)'
|
||||
New = 'local(&key_field, &[&key_segment(&value.repo.0)])?)'
|
||||
},
|
||||
@{
|
||||
Id = 'M25'
|
||||
Rule = 'A stewardship''s sequence is the last part, so a repo''s assignments share a prefix (D2).'
|
||||
Test = 'tests::a_repo_keeps_every_stewardship_it_had'
|
||||
Old = 'local(&key_field, &[&key_segment(&value.repo.0), &sequence])?)'
|
||||
New = 'local(&key_field, &[&sequence, &key_segment(&value.repo.0)])?)'
|
||||
},
|
||||
@{
|
||||
Id = 'M26'
|
||||
Rule = 'A resolution''s sequence is the last part, so a subject''s resolutions share a prefix (D2).'
|
||||
Test = 'tests::a_subjects_resolutions_share_a_prefix_no_other_key_has'
|
||||
Old = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
New = ' let parts = std::iter::once(value.subject.kind.name()).chain(std::iter::once(sequence.as_str())).chain(subject_local.split(''.'')).collect::<Vec<_>>();'
|
||||
},
|
||||
@{
|
||||
Id = 'M27'
|
||||
Rule = 'A nested subject is recovered whole: a resolution of a resolution keys the inner key, sequence included (defect 3, under the new shape).'
|
||||
Test = 'tests::a_resolution_of_a_resolution_reads_back'
|
||||
Old = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
New = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.rsplit_once(''.'').map_or(subject_local, |(head, _)| head).split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
},
|
||||
@{
|
||||
Id = 'S6'
|
||||
Rule = 'A nesting''s own sequence is its own field: a resolution of a resolution copies the subject''s sequence instead (Q17 B, defect 3).'
|
||||
Test = 'tests::every_level_of_a_nesting_keeps_its_own_sequence'
|
||||
Old = ' let sequence = format!("n{}", value.sequence);
|
||||
let parts = std::iter::once(value.subject.kind.name())'
|
||||
New = ' let sequence = if matches!(value.subject.kind, PipelineKind::Resolution) { subject_local.rsplit(''.'').next().unwrap_or("").to_string() } else { format!("n{}", value.sequence) };
|
||||
let parts = std::iter::once(value.subject.kind.name())'
|
||||
},
|
||||
@{
|
||||
Id = 'S11'
|
||||
Rule = 'A stewardship subject is recovered whole: its own sequence survives into the resolution key (M27 for the other sequenced kind).'
|
||||
Test = 'tests::a_resolution_of_a_stewardship_reads_back'
|
||||
Old = ' let parts = std::iter::once(value.subject.kind.name()).chain(subject_local.split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
New = ' let parts = std::iter::once(value.subject.kind.name()).chain(if matches!(value.subject.kind, PipelineKind::Stewardship) { subject_local.rsplit_once(''.'').map_or(subject_local, |(head, _)| head) } else { subject_local }.split(''.'')).chain(std::iter::once(sequence.as_str())).collect::<Vec<_>>();'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
# Eureka Cut 8 mutation suite, the Epiphany half: the epoch constant and the
|
||||
# live registrar. Entries only; the harness is tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-cut8-epiphany-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# E1-E3 are killed by `every_kind_is_at_the_epochs_version`: it reads the
|
||||
# version segment off the constant and holds every type id to it, so a wrong
|
||||
# epoch dies whether it is `.v2` or `.v1x`; and it counts what the live
|
||||
# registrar registers against `PipelineKind::ALL`, so one type fewer and one
|
||||
# foreign type more both die on the count.
|
||||
#
|
||||
# D1 and D2 are the derives the wire carries a batch through, and both are
|
||||
# killed by `every_document_and_refusal_serialises_and_reads_back`: the tag a
|
||||
# reader dispatches on is asserted against `PipelineKind::name()` for every
|
||||
# kind, and every refusal variant is round-tripped whole, so a field that
|
||||
# leaves the wire comes back empty and is not the refusal that went out.
|
||||
#
|
||||
# R1 and R2 are the public reference door, and both are killed by
|
||||
# `a_ref_validates_as_an_id_of_the_kind_it_declares`: a kind that is not the
|
||||
# id's kills the door that answers Ok to everything, and a trailing dot kills
|
||||
# the door that reads the kind segment and nothing else.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'E1'
|
||||
Rule = 'The epoch names the version every kind is published at; a bumped epoch fails until the kinds move with it.'
|
||||
Test = 'tests::every_kind_is_at_the_epochs_version'
|
||||
Old = 'pub const PIPELINE_SCHEMA_EPOCH: &str = "epiphany.pipeline.epoch.v1";'
|
||||
New = 'pub const PIPELINE_SCHEMA_EPOCH: &str = "epiphany.pipeline.epoch.v2";'
|
||||
},
|
||||
@{
|
||||
Id = 'E2'
|
||||
Rule = 'The live registrar registers every kind: one fewer is refused on the count.'
|
||||
Test = 'tests::every_kind_is_at_the_epochs_version'
|
||||
Old = ' $(cache.register_entry_type::<$document>()?;)*'
|
||||
New = ' let mut first = true; $(if !first { cache.register_entry_type::<$document>()?; } first = false;)*'
|
||||
},
|
||||
@{
|
||||
Id = 'E3'
|
||||
Rule = 'The live registrar registers nothing but the kinds: the foreign stand-in registered again under cfg(test) is refused on the count.'
|
||||
Test = 'tests::every_kind_is_at_the_epochs_version'
|
||||
Old = @'
|
||||
$(cache.register_entry_type::<$document>()?;)*
|
||||
Ok(())
|
||||
'@
|
||||
New = @'
|
||||
$(cache.register_entry_type::<$document>()?;)*
|
||||
#[cfg(test)]
|
||||
cache.register_entry_type::<ForeignDocument>()?;
|
||||
Ok(())
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'D1'
|
||||
Rule = 'The document tag is the kind name: a wire reader dispatches on the same string the key segment carries, so the variant spelling is renamed to it and not left as Rust case.'
|
||||
Test = 'tests::every_document_and_refusal_serialises_and_reads_back'
|
||||
Old = ' #[serde(tag = "kind", content = "value", rename_all = "snake_case")]'
|
||||
New = ' #[serde(tag = "kind", content = "value")]'
|
||||
},
|
||||
@{
|
||||
Id = 'D2'
|
||||
Rule = 'A refusal carries its named parts over the wire: the field it refused is on the wire, not dropped and defaulted back on the reader.'
|
||||
Test = 'tests::every_document_and_refusal_serialises_and_reads_back'
|
||||
Old = ' FieldBound { field: String, limit: u32, actual: u32 },'
|
||||
New = ' FieldBound { #[serde(skip)] field: String, limit: u32, actual: u32 },'
|
||||
},
|
||||
@{
|
||||
Id = 'R1'
|
||||
Rule = 'The public door is the grammar, not a formality: a door that answers Ok to every reference leaves the read side unable to tell a malformed reference from a missing document.'
|
||||
Test = 'tests::a_ref_validates_as_an_id_of_the_kind_it_declares'
|
||||
Old = @'
|
||||
pub fn validate_ref(&self) -> Result<(), PipelineRefusal> {
|
||||
self.validate("ref")
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
pub fn validate_ref(&self) -> Result<(), PipelineRefusal> {
|
||||
Ok(())
|
||||
}
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'R2'
|
||||
Rule = 'The door delegates to the one grammar: a door that checks the kind segment alone admits a local no writer composes, and the reference grammar has two owners.'
|
||||
Test = 'tests::a_ref_validates_as_an_id_of_the_kind_it_declares'
|
||||
Old = @'
|
||||
pub fn validate_ref(&self) -> Result<(), PipelineRefusal> {
|
||||
self.validate("ref")
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
pub fn validate_ref(&self) -> Result<(), PipelineRefusal> {
|
||||
if self.id.0.split(':').nth(1) != Some(self.kind.name()) {
|
||||
return Err(format_error("ref.id", &self.id.0));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
'@
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
# Eureka read-side cut, Cut RS-L: the leaf's title, the epoch, and the two new
|
||||
# grammar doors. Entries only; the harness is tools/eureka-mutations.ps1.
|
||||
#
|
||||
# powershell -File tools/eureka-mutations.ps1 `
|
||||
# -Entries tools/eureka-readside-leaf-mutations.psd1 `
|
||||
# -Target epiphany-pipeline/src/lib.rs `
|
||||
# -Test 'cargo test -p epiphany-pipeline --lib'
|
||||
#
|
||||
# M0 is the harness's own built-in control and is not an entry here.
|
||||
#
|
||||
# L1 and L1L are `OrgRepo::validate_org_repo`, on `Slug::validate_slug`'s
|
||||
# pattern, and both are killed by `the_org_repo_and_label_doors_are_the_grammar`:
|
||||
# a door that answers Ok to everything dies on "GameCult" alone, and the
|
||||
# loosened grammar that accepts any string carrying a slash dies on "a/b/c"
|
||||
# and on the 201-byte fixture, both of which carry exactly one slash.
|
||||
#
|
||||
# L2 and L2L are `Label::validate_label`, on the same pattern, and both are
|
||||
# killed by the same test: a door that answers Ok to everything dies on the
|
||||
# empty string, and a door delegating to the slug grammar instead of the
|
||||
# label grammar dies on "a.b", which `dotted_text` accepts as two labels
|
||||
# joined by a dot and `label_text` refuses outright because a label carries
|
||||
# no dot.
|
||||
#
|
||||
# L3 and L3L are the `Title` type's non-empty and 200-byte bounds, both
|
||||
# killed by `question_and_ruling_carry_a_title`: removing the non-empty check
|
||||
# dies on the empty-title case, and widening the bound to 201 bytes dies on
|
||||
# the pinned 201-byte refusal.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'L1'
|
||||
Rule = 'The public door onto the org_repo grammar is the grammar, not a formality: a door that answers Ok to every repo string leaves a caller holding a bare OrgRepo (the read side vocabulary''s repo alias, RS-3) unable to tell a malformed one from a missing document.'
|
||||
Test = 'tests::the_org_repo_and_label_doors_are_the_grammar'
|
||||
Old = @'
|
||||
pub fn validate_org_repo(&self) -> Result<(), PipelineRefusal> {
|
||||
Bounded::validate(self, "org_repo")
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
pub fn validate_org_repo(&self) -> Result<(), PipelineRefusal> {
|
||||
Ok(())
|
||||
}
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'L1L'
|
||||
Rule = 'The door delegates to the one grammar, org_repo_text whole: a loosening that accepts any string carrying a slash still depends on the input, but admits a second slash and an over-long value the real grammar refuses.'
|
||||
Test = 'tests::the_org_repo_and_label_doors_are_the_grammar'
|
||||
Old = @'
|
||||
fn org_repo_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
match value.split_once('/') {
|
||||
Some((org, repo))
|
||||
if !org.is_empty() && !repo.is_empty() && !repo.contains('/') && value.len() <= 200 =>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(format_error(field, value)),
|
||||
}
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
fn org_repo_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
if value.contains('/') { Ok(()) } else { Err(format_error(field, value)) }
|
||||
}
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'L2'
|
||||
Rule = 'The public door onto the label grammar is the grammar, not a formality: a door that answers Ok to every label string leaves a caller holding a bare Label (the read side vocabulary''s cut alias, RS-3) unable to tell a malformed one from a missing document.'
|
||||
Test = 'tests::the_org_repo_and_label_doors_are_the_grammar'
|
||||
Old = @'
|
||||
pub fn validate_label(&self) -> Result<(), PipelineRefusal> {
|
||||
Bounded::validate(self, "label")
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
pub fn validate_label(&self) -> Result<(), PipelineRefusal> {
|
||||
Ok(())
|
||||
}
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'L2L'
|
||||
Rule = 'The door delegates to the label grammar and not to some other one: a door wired to dotted_text instead of label_text admits a dotted name a label never allows, since a label composes no key of its own and carries no dot.'
|
||||
Test = 'tests::the_org_repo_and_label_doors_are_the_grammar'
|
||||
Old = @'
|
||||
pub fn validate_label(&self) -> Result<(), PipelineRefusal> {
|
||||
Bounded::validate(self, "label")
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
pub fn validate_label(&self) -> Result<(), PipelineRefusal> {
|
||||
dotted_text("label", &self.0)
|
||||
}
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'L3'
|
||||
Rule = 'Q-RS1 (ruled B): a title is never empty. Removing the non-empty check leaves only the 200-byte bound, so a bare leaf with an empty campaign, cut spec, question or ruling title would validate.'
|
||||
Test = 'tests::question_and_ruling_carry_a_title'
|
||||
Old = @'
|
||||
fn title_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
if value.is_empty() {
|
||||
return Err(format_error(field, value));
|
||||
}
|
||||
within(200)(field, value)
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
fn title_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
within(200)(field, value)
|
||||
}
|
||||
'@
|
||||
},
|
||||
@{
|
||||
Id = 'L3L'
|
||||
Rule = 'The title bound is exactly 200 bytes, not 201: a loosening that still depends on the input''s length dies on the pinned 201-byte case, which the real bound refuses and the widened one would accept.'
|
||||
Test = 'tests::question_and_ruling_carry_a_title'
|
||||
Old = @'
|
||||
fn title_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
if value.is_empty() {
|
||||
return Err(format_error(field, value));
|
||||
}
|
||||
within(200)(field, value)
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
fn title_text(field: &str, value: &str) -> Result<(), PipelineRefusal> {
|
||||
if value.is_empty() {
|
||||
return Err(format_error(field, value));
|
||||
}
|
||||
within(201)(field, value)
|
||||
}
|
||||
'@
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in a new issue