Pin the two restored rules with mutations, and match anchors to the file
M9 and M10 are the mutation half of the previous commit: a rule with no mutation is a claim, not a pinned rule. M9 replaces the `ForeignStore` refusal in `decode` with a payload format error. The envelope is still refused, so a test asserting only "this fails" would pass either way; the test asserts the refusal itself, and dies. M10 registers the pipeline document types into `runtime_spine_schema_cache`, which is exactly the mechanism the spine refuses a pipeline store with — the registry, not the epoch check. With the types registered the store opens and the test dies. Both aim at the rule rather than at an arbitrary neighbouring line, which is what makes the kill mean something. The suite now spans two files, so every entry names its own, and the first run caught a rake worth keeping: the anchors here are written with plain newlines and the working tree is CRLF, so a multi-line anchor matched nothing. M1 to M9 are single-line and never spanned a newline, which is why the flaw surfaced only when M10 arrived. Normalizing towards LF was backwards; the anchor is now converted to whatever endings the file has. The stale-anchor throw did its job: it refused to report a pass it had not earned. Ten mutations, ten killed, each by its own named test alone. Sources are restored byte-identical to HEAD afterwards. Builds: no code change; the script is run by hand and is wired into no target. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
43a08baa3c
commit
8a598ebbed
1 changed files with 55 additions and 6 deletions
|
|
@ -3,22 +3,27 @@
|
|||
# Each entry below is one rule that a surviving test claims to pin, together
|
||||
# with the exact source change that removes the rule. A mutation counts only if
|
||||
# the named test fails while the mutation is applied. The script applies one
|
||||
# mutation at a time, runs that test alone, and restores the file from HEAD
|
||||
# mutation at a time, runs that test alone, and restores the file from disk
|
||||
# before the next one, so a failed run never leaves the tree mutated.
|
||||
#
|
||||
# pwsh -File tools/eureka-cut4-mutations.ps1
|
||||
#
|
||||
# Requires CARGO_TARGET_DIR to be set the way the cut ran it, or it will build
|
||||
# into the repo-local target/.
|
||||
#
|
||||
# M9 and M10 arrived with Soul's Cut 4 findings F2 and F4: the two rules that
|
||||
# survived the store deletion with no test left standing over them. They pin
|
||||
# refusals in two different files, so every entry names its own file.
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = Split-Path -Parent $PSScriptRoot
|
||||
$file = 'epiphany-core/src/pipeline_documents.rs'
|
||||
$path = Join-Path $repo $file
|
||||
$documents = 'epiphany-core/src/pipeline_documents.rs'
|
||||
$spine = 'epiphany-core/src/runtime_spine.rs'
|
||||
|
||||
$mutations = @(
|
||||
@{
|
||||
Id = 'M1'
|
||||
File = $documents
|
||||
Rule = 'A document decodes back only from an envelope of its own type id.'
|
||||
Test = 'pipeline_documents::tests::every_pipeline_kind_round_trips_through_named_slot_zero'
|
||||
Old = '$(if envelope.r#type == <$document as DatabaseEntry>::TYPE {'
|
||||
|
|
@ -26,6 +31,7 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M2'
|
||||
File = $documents
|
||||
Rule = 'Text bounds are measured in UTF-8 bytes, not characters.'
|
||||
Test = 'pipeline_documents::tests::bounds_refuse_in_utf8_bytes'
|
||||
Old = ' move |field: &str, value: &str| bound(field, limit, value.len())'
|
||||
|
|
@ -33,6 +39,7 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M3'
|
||||
File = $documents
|
||||
Rule = 'An OrgRepo carries exactly one slash.'
|
||||
Test = 'pipeline_documents::tests::repo_fields_must_be_org_slash_repo'
|
||||
Old = " if !org.is_empty() && !repo.is_empty() && !repo.contains('/') && value.len() <= 200 =>"
|
||||
|
|
@ -40,6 +47,7 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M4'
|
||||
File = $documents
|
||||
Rule = 'A write envelope''s key is recomputed from the value and a mismatch refuses.'
|
||||
Test = 'pipeline_documents::tests::keys_are_derived_and_mismatch_refuses'
|
||||
Old = ' if envelope.key != expected {'
|
||||
|
|
@ -47,6 +55,7 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M5'
|
||||
File = $documents
|
||||
Rule = 'A finding label carries no dot, so a composed key segments one way.'
|
||||
Test = 'pipeline_documents::tests::composed_keys_cannot_collide'
|
||||
Old = ' label_text("finding.label", &value.label.0)?;'
|
||||
|
|
@ -54,6 +63,7 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M6'
|
||||
File = $documents
|
||||
Rule = 'A parent local must carry its own kind''s marker character.'
|
||||
Test = 'pipeline_documents::tests::parent_ids_are_parsed_strictly'
|
||||
Old = ' let digits = suffix.strip_prefix(marker).ok_or_else(invalid)?;'
|
||||
|
|
@ -61,6 +71,7 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M7'
|
||||
File = $documents
|
||||
Rule = 'A PipelineRef''s id is parsed as a full id of its declared kind.'
|
||||
Test = 'pipeline_documents::tests::resolution_subject_is_a_full_id_of_its_kind'
|
||||
Old = ' pipeline_id(&format!("{at}.id"), &self.id.0, self.kind).map(|_| ())'
|
||||
|
|
@ -68,20 +79,58 @@ $mutations = @(
|
|||
},
|
||||
@{
|
||||
Id = 'M8'
|
||||
File = $documents
|
||||
Rule = 'Published schemas are the Rust derivation, byte for byte.'
|
||||
Test = 'pipeline_documents::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 at all.
|
||||
@{
|
||||
Id = 'M9'
|
||||
File = $documents
|
||||
Rule = 'An envelope whose type id belongs to another kind is refused as ForeignStore.'
|
||||
Test = 'pipeline_documents::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 the pipeline types into it. The registration function is
|
||||
# `cfg(test)`, which is exactly the build this mutation compiles under.
|
||||
@{
|
||||
Id = 'M10'
|
||||
File = $spine
|
||||
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();
|
||||
crate::pipeline_documents::register_pipeline_document_types(&mut cache)?;
|
||||
'@
|
||||
}
|
||||
)
|
||||
|
||||
$results = @()
|
||||
foreach ($mutation in $mutations) {
|
||||
$path = Join-Path $repo $mutation.File
|
||||
$original = [IO.File]::ReadAllText($path)
|
||||
if (-not $original.Contains($mutation.Old)) {
|
||||
throw "$($mutation.Id): anchor not found in $file. The mutation is stale."
|
||||
# The anchors here are written with plain newlines and the working tree is
|
||||
# CRLF, so a multi-line anchor matches nothing unless it is converted to the
|
||||
# line endings the file actually has. Convert towards the file.
|
||||
$eol = if ($original.Contains("`r`n")) { "`r`n" } else { "`n" }
|
||||
$old = ($mutation.Old -replace "`r`n", "`n") -replace "`n", $eol
|
||||
$new = ($mutation.New -replace "`r`n", "`n") -replace "`n", $eol
|
||||
if (-not $original.Contains($old)) {
|
||||
throw "$($mutation.Id): anchor not found in $($mutation.File). The mutation is stale."
|
||||
}
|
||||
$mutated = $original.Replace($mutation.Old, $mutation.New)
|
||||
$mutated = $original.Replace($old, $new)
|
||||
if ($mutated -eq $original) {
|
||||
throw "$($mutation.Id): replacement changed nothing."
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue