diff --git a/backend/cmd/tmmutate/main.go b/backend/cmd/tmmutate/main.go index 92fb28b7..d9ef86ca 100644 --- a/backend/cmd/tmmutate/main.go +++ b/backend/cmd/tmmutate/main.go @@ -374,7 +374,14 @@ func one(root string, m Mutation, timeout time.Duration, logs string) (v verdict path := filepath.Join(root, file) body, rerr := os.ReadFile(path) if rerr != nil { - return survivedTests, "", fmt.Errorf("%s: %w", m.ID, rerr) + // ROT, not a run failure. ⚠ AND IT DOES NOT CLOSE THE MOVED-FILE CASE, which is what a first + // draft of this comment claimed: the fingerprint pass above reads every file the catalogue names + // BEFORE any entry runs, so a renamed or deleted file still aborts the whole run there, ahead of + // this line. Measured: a catalogue with one entry naming a missing file printed + // «fingerprint …: no such file or directory» and ran nothing, the healthy entry behind it + // included. What this branch does cover is the narrow race — a file that disappears BETWEEN the + // fingerprint pass and this read. The moved-file abort is a live gap with a backlog row. + return rotted, fmt.Sprintf("%s: %v (the file moved or was deleted under the catalogue)", m.ID, rerr), nil } mu.Lock() saved = append(saved, original{path: path, body: body, sum: hash(body)}) diff --git a/backend/cmd/tmmutate/mutations.json b/backend/cmd/tmmutate/mutations.json index 7eda823c..b6271b29 100644 --- a/backend/cmd/tmmutate/mutations.json +++ b/backend/cmd/tmmutate/mutations.json @@ -1576,9 +1576,14 @@ }, { "id": "WB36-langscreen-version-literal", - "why": "the screen's version must be DERIVED from its calibrated constants: a literal lets the floor slide with no snapshot move, and a resumed chunk is re-verdicted by a rule nobody re-billed for", + "why": "the screen's version must be DERIVED from its constants. ⚠ TWO edits, and both are the entry: the literal ALONE is harmless (it prints what the derivation prints) and the slide alone moves the derived string honestly. Only together do they reproduce the finding — the floor slides 0.50→0.45 while the version stays byte-identical, so a resumed chunk is re-verdicted under a threshold nobody re-billed for. An earlier version of this entry carried the SLIDE ONLY under this name, and passed for a pin it was not: it reddened the boundary fixture, never the derivation", "package": "./internal/langscreen/", "edits": [ + { + "file": "internal/langscreen/langscreen.go", + "find": "var Version = fmt.Sprintf(\"langscreen-v1-target-script-share+floor%.2f+min%d\", OnTargetFloor, MinLetters)", + "replace": "var Version = fmt.Sprint(\"langscreen-v1-target-script-share+floor0.50+min200\")" + }, { "file": "internal/langscreen/langscreen.go", "find": "const OnTargetFloor = 0.50",