4750 lines
241 KiB
JSON
4750 lines
241 KiB
JSON
[
|
||
{
|
||
"id": "A-lock-arbiter",
|
||
"why": "the lock bank-apply takes is the same flock a RUN holds; a pin that holds it with LockProject only proves LockProject conflicts with itself",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/store/store.go",
|
||
"find": "\tf, err := acquireLock(dbPath + \".lock\")",
|
||
"replace": "\tf, err := acquireLock(dbPath + \".lock-not-the-run-lock\")"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "B-report-mitigations",
|
||
"why": "canonical_rewrite and preexisting_problems are the two published fields the pack's own named risks are mitigated by",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\trep.CanonicalRewrite = rep.CanonicalRewriteDelta || rep.CanonicalRewriteRejects",
|
||
"replace": "\trep.CanonicalRewrite = false"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\tPreexistingProblems: orEmpty(res.Preexisting),",
|
||
"replace": "\t\tPreexistingProblems: []string{},"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "C-refusal-table-total",
|
||
"why": "a refusal class with no exit number falls into the catch-all 19; the totality gate must enumerate the classes from the SOURCE, constants included",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/refusal.go",
|
||
"find": "\tRefusalDecisionsRejected RefusalClass = \"decisions_rejected\"",
|
||
"replace": "\tRefusalDecisionsRejected RefusalClass = \"decisions_rejected\"\n\n\t// RefusalPlanted is a mutation: a class with no number in refusalExit.\n\tRefusalPlanted RefusalClass = \"planted_class\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "D-decline-normalizes",
|
||
"why": "the REPLACEMENT half of a decline must match the surface the way the matcher does — an approval written in the simplified form has to fall to a decline written in the traditional one",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "func dropTerms(f *seed.File, src string) []seed.Term {\n\tnk := text.NormalizeSourceKey(src)",
|
||
"replace": "func dropTerms(f *seed.File, src string) []seed.Term {\n\tnk := src"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "E1-ruby-deep-copy",
|
||
"why": "withRubyAliases must copy the alias slices deeply; AttachRubyAliasesToManual appends in place and would reach back into the seed rows the before/after comparison reads again",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\te.Aliases = append([]store.GlossaryAlias(nil), e.Aliases...)",
|
||
"replace": "\t\t_ = e.Aliases"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "E2-promotion-note",
|
||
"why": "a promotion carries the owner's note into the delta row",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tif d.Note != \"\" {\n\t\tt.Note = d.Note\n\t}",
|
||
"replace": "\tif false {\n\t\tt.Note = d.Note\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "E3-unreadable-is-not-undecided",
|
||
"why": "an UNREADABLE decision file must stay a loud error; only ABSENT means «nobody has decided anything yet» (the declared-path era is retired, the distinction that survives is absent-vs-unreadable)",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\tdefault:\n\t\treturn false, fmt.Errorf(\"pipeline: %s is not readable: %w\", path, err)\n\t}",
|
||
"replace": "\tdefault:\n\t\treturn false, nil\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "F-artifact-paths-absolute",
|
||
"why": "the published artifact paths are absolute; a consumer runs in another working directory, so a relative one names a different file",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\t\tMinedDelta: absPath(r.Book.MinedDelta),",
|
||
"replace": "\t\tMinedDelta: r.Book.MinedDelta,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "G-byte-gate",
|
||
"why": "the second write gate is the BYTES. UNREACHABLE behind the first: *Touched is set only when a decision changed the document, and `next` is the render of the CHANGED document, so bytes-equal implies nothing-changed implies !*Touched. Kept as depth against a coarser future *Touched and pinned as a function contract (TestChangedDocIsTheSecondFence...), which no source mutation of the CALL can reach.",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tdelta = res.DeltaBytes != nil && res.DeltaTouched &&\n\t\tchangedDoc(st.deltaRaw, res.DeltaBytes, emptyDelta(res.Delta))",
|
||
"replace": "\tdelta = res.DeltaBytes != nil && res.DeltaTouched"
|
||
}
|
||
],
|
||
"expect": "survives"
|
||
},
|
||
{
|
||
"id": "H-decisions-travel-with-the-book",
|
||
"why": "the decision files default beside the BOOK, not beside project_db: they are the user's data and must travel with the book directory in a backup or an export",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/book.go",
|
||
"find": "\tb.MinedDelta = filepath.Join(dir, b.BookID+MinedDeltaSuffix)",
|
||
"replace": "\tb.MinedDelta = filepath.Join(filepath.Dir(b.ProjectDB), b.BookID+MinedDeltaSuffix)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "I-normalize-text",
|
||
"why": "the document the door writes is normalized, which is what keeps a lawful note with a leading newline writable at all",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/seed/normalize.go",
|
||
"find": "func normText(s string) string { return strings.TrimSpace(s) }",
|
||
"replace": "func normText(s string) string { return strings.TrimLeft(s, \"\") }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "J-render-gate",
|
||
"why": "the render PROVES its bytes read back as the same document; without the equality half it only proves they parse",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tif reflect.DeepEqual(back, doc) {\n\t\treturn b, nil\n\t}",
|
||
"replace": "\tif reflect.DeepEqual(back, doc) || true {\n\t\treturn b, nil\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "K-decision-text-trimmed",
|
||
"why": "the door trims the free text it accepts, the same rule it already applies to src/sense/dst",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\td.Kind, d.Note = strings.TrimSpace(d.Kind), strings.TrimSpace(d.Note)",
|
||
"replace": "\t\td.Kind = strings.TrimSpace(d.Kind)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "L-book-id-is-a-path-element",
|
||
"why": "book_id names four files; a separator in it walks the whole set out of the book's directory",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/book.go",
|
||
"find": "\t} else if strings.ContainsAny(b.BookID, `/\\`) || b.BookID == \".\" || b.BookID == \"..\" {",
|
||
"replace": "\t} else if false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "M-owned-paths-distinct",
|
||
"why": "two config keys on one path means one document silently overwrites the other",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/book.go",
|
||
"find": "\t\t\tif owned[i].path == \"\" || owned[i].path != owned[j].path {\n\t\t\t\tcontinue\n\t\t\t}",
|
||
"replace": "\t\t\tif true {\n\t\t\t\tcontinue\n\t\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "N-flag-presence",
|
||
"why": "the flag guards key on the flag being PRESENT, not on its value being non-empty",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/invocation.go",
|
||
"find": "\tif given[\"decisions\"] {",
|
||
"replace": "\tif *decisions != \"\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "O-reversed-window",
|
||
"why": "a chapter window that ends before it begins is written, loads, and fires nowhere",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tif key.Until != 0 && key.Since > key.Until {",
|
||
"replace": "\tif false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "P-alias-judged-on-the-set",
|
||
"why": "an inert decline is judged AFTER the fold, on the result of the whole call: the term that owns the alias may be approved by another decision of the same set (anchor re-pointed when refuseInertDeclines gained its ApplyInput argument; the attacked property — the ORDER of the two phases — is unchanged)",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tfoldAccepted(in, rs, &res)\n\trefuseInertDeclines(in, rs, &res)",
|
||
"replace": "\trefuseInertDeclines(in, rs, &res)\n\tfoldAccepted(in, rs, &res)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "Q-note-not-erased-by-omission",
|
||
"why": "a repeat decision that carries no note keeps the note the owner wrote, in BOTH files",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\tif note == \"\" {\n\t\t\tnext.Note = r.Note\n\t\t}",
|
||
"replace": "\t\tif false {\n\t\t\tnext.Note = r.Note\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "R-decisions-cap",
|
||
"why": "the decision document is the one input a USER sizes; uncapped, the engine dies of memory and the platform reads that as a successful run",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif len(raw) > maxDecisionsBytes {",
|
||
"replace": "\tif false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "S-stop-writes-nothing",
|
||
"why": "a SIGTERM between the decision and the write must leave the files untouched",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif err := ctx.Err(); err != nil {\n\t\treturn finishReport(verdict, outcomeStopped, book, st, res, writtenFiles{}), err\n\t}",
|
||
"replace": "\tif err := ctx.Err(); err != nil && false {\n\t\treturn finishReport(verdict, outcomeStopped, book, st, res, writtenFiles{}), err\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "T-problems-by-subject",
|
||
"why": "a pre-existing fault is recognised per SUBJECT; joined into one string, fixing one term makes every other term's fault look new",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\tif errors.As(err, &sp) {\n\t\t\tproblems = append(problems, sp.Problems...)",
|
||
"replace": "\t\tif errors.As(err, &sp) && false {\n\t\t\tproblems = append(problems, sp.Problems...)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "U-signature-honesty",
|
||
"why": "a call that leaves surfaces of the signature map undecided must not read as «done»",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\tst.Surfaces++\n\t\tif !handled[nk] {\n\t\t\tst.Undecided++\n\t\t}",
|
||
"replace": "\t\tst.Surfaces++\n\t\tif !handled[nk] && false {\n\t\t\tst.Undecided++\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "V-manifest-artifacts",
|
||
"why": "the artifact envelope reaches the surface the consumer actually calls",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\tChunksTotal: len(chunks), Artifacts: r.artifacts(),",
|
||
"replace": "\t\tChunksTotal: len(chunks),"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "W-decision-count-cap",
|
||
"why": "the COUNT of decisions is what holds the call inside a caller's timeout; the byte cap admits ~29 000 minimal declines and five minutes of work",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif n := len(doc.Decisions); n > maxDecisions {",
|
||
"replace": "\tif n := len(doc.Decisions); false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "Y-subject-has-no-rendering",
|
||
"why": "a fault's subject is the term's IDENTITY; embedding the rendering makes a fault look new the moment the door changes that term's dst — and gender has no channel through which to fix it",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "\t\t\t\tSubject: subjectOf(\"gender\", e.Src, e.Sense, e.SinceCh, e.UntilCh),",
|
||
"replace": "\t\t\t\tSubject: fmt.Sprintf(\"%s\\u2192%s: gender %q\", e.Src, e.Dst, e.Gender),"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "Z-signature-unreadable",
|
||
"why": "a signature map that cannot be parsed must not report as «nothing left undecided»",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\t// «everything is decided», which is the one answer this field must never give by accident.\n\t\treturn SignatureState{Map: absPath(path), Unreadable: true}\n\t}",
|
||
"replace": "\t\t// «everything is decided», which is the one answer this field must never give by accident.\n\t\treturn SignatureState{Map: absPath(path)}\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AB-alias-escape",
|
||
"why": "a decline is judged by what the surface DOES after the call; the «it was a term of its own» escape let through a decline that leaves the surface firing as another term's alias",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\tif owner, held := aliasOwner(res.Delta, r.key.Src); held {",
|
||
"replace": "\t\tdroppedARow := false\n\t\tfor _, a := range res.Accepted {\n\t\t\tif a.Index == i {\n\t\t\t\tdroppedARow = len(a.Replaced) > 0\n\t\t\t}\n\t\t}\n\t\tif owner, held := aliasOwner(res.Delta, r.key.Src); held && !droppedARow {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AC-alias-owner-status",
|
||
"why": "only a SIGNED row excludes its surfaces from proposals, so declining the alias of a status:auto row is not inert — and row 199's first format mine is exactly that state",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\tif st := strings.TrimSpace(t.Status); st != \"\" && st != \"approved\" {",
|
||
"replace": "\t\tif st := strings.TrimSpace(t.Status); st != \"\" && st != \"approved\" && false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AD-loader-partial-set",
|
||
"why": "the loader hands back the entries it built even when it refuses; without them the checks below it cannot see a document that does not load, and repairing loadability reads as introducing everything the blindness was hiding",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "\t\t\t\tfmt.Sprintf(\"term %q: a %s term must have a non-empty dst (an empty one is silently inert; only status=auto may lack a dst)\", t.Src, status))",
|
||
"replace": "\t\t\t\tfmt.Sprintf(\"term %q: a %s term must have a non-empty dst (an empty one is silently inert; only status=auto may lack a dst)\", t.Src, status))\n\t\t\tcontinue"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AE-positionless-subject",
|
||
"why": "the three «record N has no identity» messages key on a POSITION, which moves when an earlier record is removed; the explicit positionless subject is what keeps a shifted position from reading as a new fault",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/problems.go",
|
||
"find": "func (p *problemList) addKeyed(subject, text string) {\n\t*p = append(*p, Problem{Subject: subject, Text: text})\n}",
|
||
"replace": "func (p *problemList) addKeyed(subject, text string) {\n\t_ = subject\n\t*p = append(*p, Problem{Subject: text, Text: text})\n}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AF-keys-file-reason-covers-all",
|
||
"why": "the reason a refusal gives must describe the command that receives it; a hand-written list left `bank-apply` — the verb this pack added — undescribed by its own refusal",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/invocation.go",
|
||
"find": "The $0 read commands (report/status/export/build/manifest/seed-lint/bank-apply) must not demand provider keys at all (D20.4)",
|
||
"replace": "The $0 read commands (report/status/export/build/manifest/seed-lint) must not demand provider keys at all (D20.4)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AG-stopped-report",
|
||
"why": "a call stopped by a signal must not PRINT that it applied — not in mode, not in changed, and not in the accepted list, which is the field a consumer iterates",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\trep.Mode, rep.Changed, rep.Accepted = \"stopped\", false, []membank.AcceptedDecision{}",
|
||
"replace": "\t\trep.Mode, rep.Changed = \"apply\", true"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AH-signature-read-error",
|
||
"why": "a signature map that exists but cannot be READ must not report as «nothing left undecided»",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\treturn SignatureState{} // no run has reached the bank boundary: nothing to count against\n\t\t}",
|
||
"replace": "\t\treturn SignatureState{}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AI-refused-warns-nothing",
|
||
"why": "canonical_rewrite warns about a mutation that is about to happen; a refused call performs none",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\trep.Mode, rep.Changed = \"refused\", false\n\t\trep.Signature = signatureState(book, st.seed.Terms, docsFromDisk(st))",
|
||
"replace": "\t\trep.Mode, rep.Changed = \"refused\", false\n\t\twarn()\n\t\trep.Signature = signatureState(book, st.seed.Terms, docsFromDisk(st))"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "AJ-seed-alias-decline",
|
||
"why": "a decline of a seed ALIAS is inert only while the delta has no row of its own for that surface; when it does, the decline drops it and repairs the collision the report is listing (anchor re-pointed when the refusal gained its second carve-out; the attacked property is unchanged)",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "held &&\n\t\t\t!deltaHoldsSurface(in.Delta, r.key.Src) && !rejectsHoldSurface(in.Rejects, r.key.Src) {",
|
||
"replace": "held && !rejectsHoldSurface(in.Rejects, r.key.Src) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX1-stop-memory-not-recorded",
|
||
"why": "a stop that does not RECORD what it presented re-stops on the same map forever — the flag stops paying once per novelty",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\tif merr := r.Store.MarkStopPresented(r.Book.BookID, presentedSurfaces(mined)); merr != nil {",
|
||
"replace": "\tif merr := r.Store.MarkStopPresented(r.Book.BookID, nil); merr != nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX2-old-stop-semantics",
|
||
"why": "the pre-D39.144 formula — stop on ANY non-empty delta — is the exact regression §4.11 says no test guarded; the flag-model chain must redden on it",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\t\tstopping = hasUnpresentedCluster(mined, presented)",
|
||
"replace": "\t\tstopping = len(mined) > 0 && len(presented) >= 0"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX3-memory-by-representative-only",
|
||
"why": "a memory keyed on the representative alone re-stops when a cluster's first-ranked member changes to an alias of the same entity",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\tfor _, a := range t.Aliases {\n\t\tif nk := text.NormalizeSourceKey(a); nk != \"\" {\n\t\t\tout = append(out, nk)\n\t\t}\n\t}\n\treturn out\n}\n\n// hasUnpresentedCluster",
|
||
"replace": "\t_ = t.Aliases\n\treturn out\n}\n\n// hasUnpresentedCluster"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX4-auto-wire-gated-on-the-flag",
|
||
"why": "§4.3: a raised flag on a NON-stopping run must not switch the auto wire off — the unsigned rows must still ride to the editor",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\t\tif err := r.writeAutoBank(ctx, mined, proposals, ownerHandled(unsignedEngineSurfaces(seed), rejects)); err != nil {\n\t\t\treturn false, err\n\t\t}",
|
||
"replace": "\t\tif !r.VerifyBank {\n\t\t\tif err := r.writeAutoBank(ctx, mined, proposals, ownerHandled(unsignedEngineSurfaces(seed), rejects)); err != nil {\n\t\t\t\treturn false, err\n\t\t\t}\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX5-normalize-one-trigger",
|
||
"why": "the canonical form trims the WHOLE whitespace class, not the one trigger (leading newline) the emitter defect made famous",
|
||
"package": "./internal/seed/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/seed/normalize.go",
|
||
"find": "func normText(s string) string { return strings.TrimSpace(s) }",
|
||
"replace": "func normText(s string) string { return strings.TrimLeft(s, \"\\n\") }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX6-subject-collapses-to-class",
|
||
"why": "a fault subject without identity lets any pre-existing fault of a class excuse every INTRODUCED fault of that class",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/problems.go",
|
||
"find": "\treturn fmt.Sprintf(\"%s|%s|%s|%d|%d\", class, src, sense, since, until)",
|
||
"replace": "\treturn class"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX7-subject-loses-the-window",
|
||
"why": "a fault subject without the chapter window masks a new fault on the SAME term in ANOTHER window",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/problems.go",
|
||
"find": "\treturn fmt.Sprintf(\"%s|%s|%s|%d|%d\", class, src, sense, since, until)",
|
||
"replace": "\treturn fmt.Sprintf(\"%s|%s|%s\", class, src, sense)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX8-report-paths-relative",
|
||
"why": "the bank-apply report's file paths must be absolute — the consumer runs in its own working directory (the status report pins the same guarantee separately)",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif abs, err := filepath.Abs(p); err == nil {\n\t\treturn abs\n\t}\n\treturn p",
|
||
"replace": "\treturn p"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX9-byte-cap-boundary",
|
||
"why": "a document of exactly the byte cap is lawful; > tightened to >= refuses it",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif len(raw) > maxDecisionsBytes {",
|
||
"replace": "\tif len(raw) >= maxDecisionsBytes {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX10-count-cap-boundary",
|
||
"why": "a document of exactly the count cap is lawful; > tightened to >= refuses it",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif n := len(doc.Decisions); n > maxDecisions {",
|
||
"replace": "\tif n := len(doc.Decisions); n >= maxDecisions {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX12-absolute-paths-not-cleaned",
|
||
"why": "an uncleaned absolute path is a second spelling of a guarded path; the acceptance walked through exactly this and reproduced the data-loss blocker",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/book.go",
|
||
"find": "\t\t\treturn filepath.Clean(p)",
|
||
"replace": "\t\t\treturn p"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX13-retired-keys-honoured",
|
||
"why": "a declared decision-path key must fail naming the cure, never silently resolve to the convention while the operator believes the declared path is in force",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/book.go",
|
||
"find": "\tif b.RetiredMinedDelta != \"\" || b.RetiredMinedRejects != \"\" {",
|
||
"replace": "\tif false && (b.RetiredMinedDelta != \"\" || b.RetiredMinedRejects != \"\") {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX14-map-truncated-in-place",
|
||
"why": "the signature map is read by the other side of the seam; os.WriteFile truncates first and a live reader gets a half-written map",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\tif err := writeFileAtomic(r.signatureMapPath(), mapBytes); err != nil {",
|
||
"replace": "\tif err := os.WriteFile(r.signatureMapPath(), mapBytes, 0o644); err != nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX15-envelope-dropped",
|
||
"why": "the map's seam envelope (version + content id) is 17-seam-inbound-law п.3; without it two maps are indistinguishable without a diff",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tm := seed.SignatureMap{Version: seed.SignatureMapVersion, ID: id, File: content}",
|
||
"replace": "\tm := seed.SignatureMap{File: content}\n\t_ = id"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX16-interleaved-commit",
|
||
"why": "§4.8: both documents are STAGED before either rename; committing the first before staging the second re-opens «half landed» for failures staging would catch with zero bytes moved",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif rejectsChanged {\n\t\tif rejectsStage, err = stageFileAtomic(book.MinedRejects, res.RejectBytes); err != nil {\n\t\t\tif deltaStage != nil {\n\t\t\t\tdeltaStage.abort()\n\t\t\t}\n\t\t\treturn wrote, err\n\t\t}\n\t}",
|
||
"replace": "\tif deltaStage != nil {\n\t\tif err := deltaStage.commit(); err != nil {\n\t\t\treturn wrote, err\n\t\t}\n\t\twrote.delta = true\n\t\tdeltaStage = nil\n\t}\n\tif rejectsChanged {\n\t\tif rejectsStage, err = stageFileAtomic(book.MinedRejects, res.RejectBytes); err != nil {\n\t\t\treturn wrote, err\n\t\t}\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX17-dir-sync-skipped",
|
||
"why": "the decision files are the user's words: either the directory entries are flushed or the report says durability is unproven — silence is neither",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif err := syncDir(filepath.Dir(book.MinedDelta)); err != nil {\n\t\treturn wrote, fmt.Errorf(\"the files are renamed but their directory entries are not proven durable (a host crash could still lose them): %w\", err)\n\t}",
|
||
"replace": "\t_ = syncDir"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX18-write-incomplete-unmapped",
|
||
"why": "a refusal class without a number falls to exit 19 and the consumer loses the one thing the class buys — «re-send the same document»",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/main.go",
|
||
"find": "\tpipeline.RefusalDecisionsRejected: exitDecisionsRejected,\n\tpipeline.RefusalWriteIncomplete: exitWriteIncomplete,",
|
||
"replace": "\tpipeline.RefusalDecisionsRejected: exitDecisionsRejected,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX19-cap-report-dropped",
|
||
"why": "§4.7: every refusal of the decisions class prints a report, the caps included — the contract's two carriers must not diverge again",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\t\treturn capRefusalReport(book, err), err",
|
||
"replace": "\t\t\treturn BankDecisionsReport{}, err"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX20-write-failed-success-shape",
|
||
"why": "the acceptance's repro: EXIT=1 with a success-shaped report over a half-written pair; the write-failed outcome must never print «apply»",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\trep.Mode, rep.Changed = \"write_incomplete\", files.delta || files.rejects",
|
||
"replace": "\t\trep.Mode, rep.Changed = \"apply\", true"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX11-window-boundary",
|
||
"why": "a single-chapter window (since == until) is a lawful term of one chapter; > tightened to >= refuses it as a reversal",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tif key.Until != 0 && key.Since > key.Until {",
|
||
"replace": "\tif key.Until != 0 && key.Since >= key.Until {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX21-dry-run-silently-paid",
|
||
"why": "`tmctl translate --dry-run` used to parse cleanly and run a PAID translation — the flag's silent version inverts the one promise (--dry-run = spends nothing) the caller relies on",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/invocation.go",
|
||
"find": "\tif given[\"dry-run\"] && cmd != \"bank-apply\" && cmd != \"redrive\" {",
|
||
"replace": "\tif false && given[\"dry-run\"] && cmd != \"bank-apply\" && cmd != \"redrive\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX22-retry-never-reproves-durability",
|
||
"why": "the retry the write-incomplete class prescribes lands on the byte-no-op branch; without a directory sync there, the one thing exit 15 warned about is never re-proven and the retry answers 0",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\tif err := syncDir(filepath.Dir(book.MinedDelta)); err != nil {\n\t\t\treturn finishReport(verdict, outcomeWriteFailed, book, st, res, writtenFiles{}),\n\t\t\t\trefuse(RefusalWriteIncomplete, fmt.Errorf(\"nothing was written by this call, but the directory holding the decision files still cannot be flushed — their durability is unproven: %w\", err))\n\t\t}",
|
||
"replace": "\t\t_ = syncDir"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX23-map-write-failure-downgraded",
|
||
"why": "the map-before-memory ORDER is load-bearing: a run that proceeds past a failed map write stops pointing the owner at a file that is not there and marks the clusters presented — no later flag ever stops on them again (acceptance planting, survived three candidate packages)",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\tif err := writeFileAtomic(r.signatureMapPath(), mapBytes); err != nil {\n\t\treturn false, fmt.Errorf(\"pipeline: the bank-mining stop write signature map %s: %w\", r.signatureMapPath(), err)\n\t}",
|
||
"replace": "\tif err := writeFileAtomic(r.signatureMapPath(), mapBytes); err != nil {\n\t\tr.Log.WarnContext(ctx, \"pipeline: the bank-mining stop write signature map failed\", \"err\", err)\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FX24-write-failed-signature-from-intent",
|
||
"why": "the write-failed report must measure the signature against the DISK: fed the call's own result, a class-15 report whose write landed nothing prints undecided:0 — a success-shaped block on the one outcome the honesty work was done for (panel finding, reproduced)",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\t\t// The POST-state, re-read from disk: for an outcome that touched the world the report describes\n\t\t// the files as they now are, never the bytes the call intended.\n\t\trep.Signature = signatureState(book, st.seed.Terms, docsReRead(book))",
|
||
"replace": "\t\t// The POST-state, re-read from disk: for an outcome that touched the world the report describes\n\t\t// the files as they now are, never the bytes the call intended.\n\t\trep.Signature = signatureState(book, st.seed.Terms, docsFromResult(st, res))"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH1-system-join",
|
||
"why": "an endpoint that carries ONE system message must receive the memory-bank injection INSIDE that message; without the join the glossary is dropped by the provider at HTTP 200 and no gate can see it",
|
||
"package": "./internal/llm/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\tif mode != SystemMessagesSingle {",
|
||
"replace": "\tif true {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH2-system-axis-from-yaml",
|
||
"why": "the declared quirk has to survive the trip from models.yaml to the resolved Capability; dropped there, the join never runs and the loss is silent again",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/models.go",
|
||
"find": "\tdefault:\n\t\tc.SystemMessages = llm.SystemMessagesMode(cfg.SystemMessages)\n\t}",
|
||
"replace": "\tdefault:\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH3-gap-marker",
|
||
"why": "a unit that ships text with a member chunk MISSING must say so in the text a human reads; without the marker the reader gets a seamless concatenation with a hole and no seam",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\tm := fmt.Sprintf(\"[⚠ TEXT MISSING — %d source %s of this unit could not be translated and %s NOT in the text below\",",
|
||
"replace": "\tm := fmt.Sprintf(\"[%d %s %s\","
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH4-dropped-members-counted",
|
||
"why": "the incompleteness fact is carried by DroppedMembers, not inferred from a flag reason; stop counting it and both renderers go back to guessing, which is how the reader was told a member drop was a cosmetic clean-up",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/export.go",
|
||
"find": "\tce.DroppedMembers = len(drops)",
|
||
"replace": "\tce.DroppedMembers = 0"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH5-decline-converges",
|
||
"why": "a decline already on the record is a decision being RE-SENT, not one being made; without this carve-out the door refuses the identical document forever and a worker that retries splits the state",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": " && !rejectsHoldSurface(in.Rejects, r.key.Src) {",
|
||
"replace": " {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH6-rejects-rename-first",
|
||
"why": "the delta is the document the seed-conflict refusal READS, so it must not be the one that lands first: a decline interrupted after a delta-first rename leaves a state no predicate can tell from an inert decline, and the re-send is refused forever",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankdecisions.go",
|
||
"find": "\tif rejectsStage != nil {\n\t\tif err := rejectsStage.commit(); err != nil {\n\t\t\tif deltaStage != nil {\n\t\t\t\tdeltaStage.abort()\n\t\t\t}\n\t\t\treturn wrote, err\n\t\t}\n\t\twrote.rejects = true\n\t}\n\tif deltaStage != nil {\n\t\tif err := deltaStage.commit(); err != nil {\n\t\t\treturn wrote, err\n\t\t}\n\t\twrote.delta = true\n\t}",
|
||
"replace": "\tif deltaStage != nil {\n\t\tif err := deltaStage.commit(); err != nil {\n\t\t\tif rejectsStage != nil {\n\t\t\t\trejectsStage.abort()\n\t\t\t}\n\t\t\treturn wrote, err\n\t\t}\n\t\twrote.delta = true\n\t}\n\tif rejectsStage != nil {\n\t\tif err := rejectsStage.commit(); err != nil {\n\t\t\treturn wrote, err\n\t\t}\n\t\twrote.rejects = true\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH7-drop-reason-is-the-holes-own",
|
||
"why": "the marker must name why the MEMBER dropped, not the unit's flag reason: a unit whose edit flagged for a cosmetic strip AND lost a member would otherwise tell the reader a clean-up ate a chunk of the book — the same false claim the pack removes, moved into the prose",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "gapMarker(ce.DroppedMembers, ce.DroppedReason)",
|
||
"replace": "gapMarker(ce.DroppedMembers, ce.FlagReason)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH8-draft-only-has-no-members",
|
||
"why": "a draft-only pipeline makes every chunk a SINGLETON unit whose own draft row is its final row, so the member-drop rule reads the unit's own flag as a lost member; without the guard a cosmetically stripped chunk that lost NOTHING is announced as incomplete — a marker that misinforms, which the order forbids as firmly as the silence it replaces",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/export.go",
|
||
"find": "\tif r.finalStageWave() == waveEdit {",
|
||
"replace": "\tif true {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH9-shipped-gemini-declaration",
|
||
"why": "the two lines in configs/models.yaml are the ONLY thing that makes the system-message join reach the real endpoint; everything else about the fix is exercised against a synthetic fixture provider, and deleting them used to leave the whole module green",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "configs/models.yaml",
|
||
"find": " capabilities:\n system_messages: single\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH10-translate-empty-text-guard",
|
||
"why": "renderTranslate's non-empty-text guards are what stop the gap marker pointing at text that does not exist; the code calls them load-bearing and nothing pinned them",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\t\tcase ch.DroppedMembers > 0 && ch.FinalText != \"\":",
|
||
"replace": "\t\tcase ch.DroppedMembers > 0:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH11-explicit-multi-costs-nothing",
|
||
"why": "writing the DEFAULT out loud must not put a key in the capability the snapshot carries — otherwise a line that changes no byte on the wire re-buys the book",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/models.go",
|
||
"find": "\tcase \"multi\":\n\t\tc.SystemMessages = llm.SystemMessagesMulti",
|
||
"replace": "\tcase \"multi\":\n\t\tc.SystemMessages = llm.SystemMessagesMode(\"multi\")"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH12-declined-never-enters-the-bank",
|
||
"why": "the rejects-first rename order leaves the delta row on disk so an interrupted decline can converge; without this filter that window is one in which a PAID run injects a term the owner explicitly declined",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\t\t\tif rejects[text.NormalizeSourceKey(e.Src)] {",
|
||
"replace": "\t\t\tif false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SH13-inert-decline-second-door",
|
||
"why": "the ALIAS door refuses an inert decline for its own good reason, but a decline ALREADY on the record is a decision being re-sent; unnarrowed it refused the standing ledger forever and, the layer being all-or-nothing, discarded the lawful decisions sent beside it",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\t\tif rejectsHoldSurface(in.Rejects, r.key.Src) {\n\t\t\tcontinue\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC1-book-filter-store",
|
||
"why": "CheckpointUsageForBook's `WHERE j.book_id = ?` is the only thing keeping another book's checkpoints out of the consent number when two books share a project database; removing it left every store and money test green (backlog row 197, ФЧ-1 — reproduced by the acceptance), because no fixture had two books at one position",
|
||
"package": "./internal/store/",
|
||
"run": "TestCheckpointUsageForBookIsScopedToTheBook",
|
||
"edits": [
|
||
{
|
||
"file": "internal/store/ledger.go",
|
||
"find": "\t\tWHERE j.book_id = ?\n\t\tORDER BY c.rowid",
|
||
"replace": "\t\tWHERE j.book_id = ? OR 1 = 1\n\t\tORDER BY c.rowid"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC1-book-filter-pipeline",
|
||
"why": "the same planting judged where the money is published: another book's calls at the same positions, ten times the price, must not overshoot this book's rows into «quoted from history» or move its projected book cost",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAnotherBooksMoneyNeverEntersTheConsentNumber",
|
||
"edits": [
|
||
{
|
||
"file": "internal/store/ledger.go",
|
||
"find": "\t\tWHERE j.book_id = ?\n\t\tORDER BY c.rowid",
|
||
"replace": "\t\tWHERE j.book_id = ? OR 1 = 1\n\t\tORDER BY c.rowid"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC1-cell-key",
|
||
"why": "the repricer's cell is the POSITION (chapter AND chunk); keyed by chapter alone it pools the neighbours' calls and the newest-first walk eats the neighbour's money — invisible while every money fixture had one chunk per chapter (backlog row 197, ФЧ-1)",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryChunkOfAChapterIsRePricedFromItsOwnCalls",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\t\tkey := chunkKey{cu.Chapter, cu.ChunkIdx}",
|
||
"replace": "\t\tkey := chunkKey{cu.Chapter, 0}"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\tcalls := rp.cells[chunkKey{cs.Chapter, cs.ChunkIdx}][cs.Stage]",
|
||
"replace": "\tcalls := rp.cells[chunkKey{cs.Chapter, 0}][cs.Stage]"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC1-unpriced-not-disclosed",
|
||
"why": "a call carried at its billed figure (no usable usage) must mark the row as quoted from history; without the mark the amount stays right and every disclosure of «part of this sum is old money» — consent text, status --json, CLI — goes silent (backlog row 197, ФЧ-1 / ФЧ-4)",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABilledDecodeRowIsDisclosedAsOldMoneyEndToEnd|TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\t\tif !calls[i].priced {\n\t\t\tfromHistory = true\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC5-carry-unbounded",
|
||
"why": "the volume grant admits units an earlier run started and never shipped OUTSIDE the grant, and that carry must be bounded BY the grant; unbounded, a run that drafted a whole book hands every unit to the next purchase and --max-units stops bounding anything (measured on a fixture: a grant of one paid for five units)",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheCarryIsBoundedByTheGrant",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tif s.stop.Carried >= r.MaxUnits {\n\t\t\ts.stop.LeftFresh++\n\t\t\tcontinue\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC5-model-axis-configured-slot",
|
||
"why": "the re-payment prices a stored call at the model its stage RESOLVES to (label routing applied), not at the configured `model:` key; reading the configured slot prices a labelled book's re-payment at a model the run never calls, and no other test in the tree tells the two slots apart",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheModelAxisReadsTheRESOLVEDSlotNotTheConfiguredOne",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\t\tif escalation && st.ResolvedHop != \"\" {\n\t\t\treturn st.ResolvedHop, true\n\t\t}\n\t\treturn st.ResolvedModel, st.ResolvedModel != \"\"",
|
||
"replace": "\t\tif escalation && st.EscalateTo != \"\" {\n\t\t\treturn st.EscalateTo, true\n\t\t}\n\t\treturn st.Model, st.Model != \"\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC6-reconcile-fresh-branch",
|
||
"why": "reconcile's docstring claims EVERY branch keeps Carried inside Delivered, and granted() = Delivered - Carried + Reworked depends on it; the fresh branch reverted to `Delivered > 0` drives the grant arithmetic negative and prints «0 NEW unit(s) delivered» beside «1 of the new ones had been started by an earlier run» — a sentence that cannot be true",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestReconcileKeepsContainmentOnTheFreshBranch",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tcase class == unitFresh && s.stop.Delivered > s.stop.Carried:",
|
||
"replace": "\t\tcase class == unitFresh && s.stop.Delivered > 0:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC6-capped-refusal-borrows-book-basis",
|
||
"why": "the --accept-rebill=<usd> refusal quotes THIS run's figure, so it must carry THIS run's basis; borrowing the book's tells a caller sizing their cap that more units are an upper estimate than the number beside it covers. Its twin, the threshold refusal's scoped clause, was pinned from the start; this branch was not",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheScopedClauseOfTheRefusalCarriesItsOwnBasis",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/rebill.go",
|
||
"find": "\t\t\tproj.USD, proj.Rows, projectionBasis(proj), r.AcceptRebill.CapUSD, r.AcceptRebill.CapUSD)",
|
||
"replace": "\t\t\tproj.USD, proj.Rows, projectionBasis(book), r.AcceptRebill.CapUSD, r.AcceptRebill.CapUSD)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC6-model-caveat-printed-at-zero",
|
||
"why": "a caveat that prints when its count is zero puts «0 of the N unit(s) were bought from a model their stage no longer resolves to» into EVERY consent text; a reader who learns to skip it skips it when it means something. The old-money twin has had this negative pin since it was written",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestProjectionBasisNamesWhatTheAmountIsMadeOf",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/rebill.go",
|
||
"find": "\tif p.ModelMovedRows > 0 {",
|
||
"replace": "\tif p.ModelMovedRows >= 0 {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC6-escalation-flag-store",
|
||
"why": "the re-pricing reads the escalation flag through CheckpointUsageForBook to know that a hop call's «today» is the stage's current escalate_to; dropping the column from the SELECT survives the store's own battery and only the pipeline's goes red — the same one-sided seam the book filter is pinned on both sides for",
|
||
"package": "./internal/store/",
|
||
"run": "TestCheckpointUsageCarriesTheEscalationFlag",
|
||
"edits": [
|
||
{
|
||
"file": "internal/store/ledger.go",
|
||
"find": "\t\t c.cost_usd, c.escalation\n\t\tFROM checkpoints c JOIN jobs j ON j.id = c.job_id",
|
||
"replace": "\t\t c.cost_usd, 0\n\t\tFROM checkpoints c JOIN jobs j ON j.id = c.job_id"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC7-volume-stop-silent-outside-grant",
|
||
"why": "a run that finished units an earlier run began delivered MORE than its grant, and the stop line is the only channel that says why; judged by the remainder alone the report goes silent in exactly the case with the largest discrepancy",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestARunThatWorkedOutsideItsGrantSaysSo",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "func (s *volumeScope) bound() bool { return s != nil && (s.stop.Left() > 0 || s.stop.Carried > 0) }",
|
||
"replace": "func (s *volumeScope) bound() bool { return s != nil && s.stop.Left() > 0 }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC7-reconcile-free-branch",
|
||
"why": "the second fall-through of reconcile must keep Carried inside Delivered too; the guard removed there drives granted() negative on a book with one carried unit and one free unit that flags on resume",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestReconcileKeepsCarriedInsideDelivered",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tcase s.stop.Delivered > s.stop.Carried:\n\t\t\ts.stop.Delivered--\n\t\tcase s.stop.Carried > 0:\n\t\t\ts.stop.Delivered--\n\t\t\ts.stop.Carried--\n",
|
||
"replace": "\t\tcase s.stop.Delivered > 0:\n\t\t\ts.stop.Delivered--\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC7-zero-usage-row-silent-after-a-move",
|
||
"why": "a $0 row with no usage on file cannot be re-priced once its stage resolves elsewhere — there are no tokens to price at the new model; publishing a bare $0 with no caveat is the silent under-quote D39.150 п.1 forbids",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestRepriceCheckpointFallsBackToTheBilledAmount",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\t\t\ttoday, ok := r.currentModelFor(cu.Stage, cu.Escalation)\n\t\t\treturn 0, !ok || today == cu.ModelRequested, false",
|
||
"replace": "\t\t\treturn 0, true, false"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC7-scoped-clause-without-basis",
|
||
"why": "the threshold refusal prints the BOOK's figure and THIS run's slice; each travels with the basis of the projection it came from, because which of the slice's units are an upper estimate is not something the book's basis states for them",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheScopedClauseOfTheRefusalCarriesItsOwnBasis",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/rebill.go",
|
||
"find": "\t\t\tproj.Rows, proj.USD, projectionBasis(proj), scopedRepin)",
|
||
"replace": "\t\t\tproj.Rows, proj.USD, scopedRepin)"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/rebill.go",
|
||
"find": "would re-pay %d of them, ~$%.6f (%s)%s — but the threshold",
|
||
"replace": "would re-pay %d of them, ~$%.6f%s — but the threshold"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC7-overshoot-claims-a-model-move",
|
||
"why": "the revert/overshoot branch quotes a row at what it was BILLED, which is priced at no model's current table; counting it among the units priced across a model move inflates the caveat with rows the caveat does not describe",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\t\treturn cs.CostUSD, true, false\n\t}\n\tif residue := cs.CostUSD - accounted; residue > residueEpsilonUSD {",
|
||
"replace": "\t\treturn cs.CostUSD, true, moved\n\t}\n\tif residue := cs.CostUSD - accounted; residue > residueEpsilonUSD {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC8-free-call-dropped-from-a-paid-generation",
|
||
"why": "a call that cost NOTHING can neither complete a row's cost nor overshoot it, so a walk that stops on money drops it — and with it the price that call carries today. A $0 primary whose PAID escalation hop alone accounts for the row (a local model with a cloud fallback) is then quoted at half what the re-run buys, with no caveat: the silent under-quote D39.150 п.1 forbids",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\t\tfree := calls[i].then <= residueEpsilonUSD\n\t\tif accounted >= cs.CostUSD-residueEpsilonUSD && !free {",
|
||
"replace": "\t\tfree := calls[i].then <= residueEpsilonUSD\n\t\t_ = free\n\t\tif accounted >= cs.CostUSD-residueEpsilonUSD {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC8-unestablished-zero-row-published-as-free",
|
||
"why": "a $0 row whose newest call on file belongs to an older generation cannot be caught by the overshoot test — no walk overshoots $0 — so without this it is published as a bare $0 with no caveat; a `skipped` row is genuinely free, any other is one whose generation could not be established",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\tif taken == 0 && len(calls) > 0 && cs.Disposition != string(DispSkipped) {\n\t\tfromHistory = true\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC8-log-fork-at-the-end-of-the-book",
|
||
"why": "the run log takes the same fork as the operator line: a run that reached the end of the book did NOT stop short of it, and saying so of every bounded run makes the sentence worthless exactly where the grant behaved unusually",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRunLogSaysWhichKindOfVolumeStopItWas",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\tif scope.stop.Left() == 0 {\n\t\t\tmsg = \"the run reached the end of the book under a VOLUME ceiling that held nothing back; it is reported because units an earlier run began were finished outside this grant\"\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "FC8-empty-cell-disclosed-as-unestablished",
|
||
"why": "the «generation could not be established» mark is for a $0 row whose calls belong to somebody else, NOT for a row with no call at all — that one is free by construction; without the guard every such row inflates the count of units the operator is told could not be re-priced",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reprice.go",
|
||
"find": "\tif taken == 0 && len(calls) > 0 && cs.Disposition != string(DispSkipped) {",
|
||
"replace": "\tif taken == 0 && cs.Disposition != string(DispSkipped) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB1-head-always-end-of-book",
|
||
"why": "the stop line's two openings are a fork, and only the NEW opening was held: a fork that reaches the end-of-book wording unconditionally passed the whole tree, and in a run whose grant really did cut it short it printed «reached the END of the book … Still in the book: 1 unit(s) NEVER delivered» — the book is finished and a unit of it was never delivered, in one sentence",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheStopLineSaysTheGrantHeldWorkBackWhenItDid",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\tif v.Left() == 0 {\n\t\thead = \"reached the END of the book",
|
||
"replace": "\tif v.Left() >= 0 {\n\t\thead = \"reached the END of the book"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB2-head-keyed-on-the-carry",
|
||
"why": "the same fork asked about the wrong quantity. Which opening applies is the REMAINDER — did the grant hold work back — not the CARRY, which is why the report is attached at all. Keyed on the carry the fork is right on every state the tree exercised and wrong on the mixed one the carry itself made reachable: work carried AND work held back",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheStopLineSaysTheGrantHeldWorkBackWhenItDid",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\tif v.Left() == 0 {\n\t\thead = \"reached the END of the book",
|
||
"replace": "\tif v.Carried > 0 {\n\t\thead = \"reached the END of the book"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB3-bound-border-drops-a-single-carry",
|
||
"why": "bound() reports a run that did work OUTSIDE its grant, and the smallest such run is ONE interrupted unit finished at the end of the book. A `> 1` border drops exactly that one: the run delivers a unit it never paid a slot for and returns no report at all — the silent limit D39.165 §1б names. The existing carry test finishes TWO units and survives the border",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestOneCarriedUnitIsStillWorkOutsideTheGrant",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "return s != nil && (s.stop.Left() > 0 || s.stop.Carried > 0) }",
|
||
"replace": "return s != nil && (s.stop.Left() > 0 || s.stop.Carried > 1) }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB4-cut-tag-drops-the-embedded-data",
|
||
"why": "the cut tag rides inside every unit id, and a component dropped from it is invisible by construction — the id still looks like an id while it now points at different text. The embedded language data decides where boundaries fall (CJK numerals, sentence terminators), so without it an injection.txt edit re-cuts the whole book and re-mints nothing. Found by planting: removing it left the ENTIRE battery green, and this pack's point 3 chose the Go render over the data precisely because that component is load-bearing",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheCutTagActuallyCarriesTheEngineData",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\tChunker: chunkerVersion, Segmentation: r.segmentationSnapshot(), ShippingWave: r.shippingWaveTag(),\n\t\tLangpack: r.packVersion(), Embedded: lang.EmbeddedVersion(), Norm: text.NormVersion(),",
|
||
"replace": "\t\tChunker: chunkerVersion, Segmentation: r.segmentationSnapshot(), ShippingWave: r.shippingWaveTag(),\n\t\tLangpack: r.packVersion(), Embedded: \"\", Norm: text.NormVersion(),"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB5-target-screen-never-sees-the-target",
|
||
"why": "the target-language screen must be handed the target's DECLARED scripts; given none it abstains, so the verdict row 46 exists for is never reached. Re-anchored: the assembly of the input moved into one builder (WB33 guards that), so this entry now attacks the screen's own call",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRunnerHandsTheScreenItsData",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "if scr := langscreen.Screen(out, in.TargetScripts); scr.Verdict == langscreen.OffTarget {",
|
||
"replace": "if scr := langscreen.Screen(out, nil); scr.Verdict == langscreen.OffTarget {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB6-off-target-flag-not-escalatable",
|
||
"why": "D39.93 п.2 refused to reuse FlagCJKArtifact precisely so the new flag would need an EXPLICIT entry in escalatable(). Without it a caught off-target chunk is silently LOST instead of re-attacked on another model — the failure the whitelist exists to prevent, and the one the design was warned about before the code was written",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAnOffTargetCompletionIsFlaggedAndEscalated",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\tcase FlagCJKArtifact, FlagOffTargetLang, FlagExcisionSuspect, FlagLoopDegenerate,",
|
||
"replace": "\tcase FlagCJKArtifact, FlagExcisionSuspect, FlagLoopDegenerate,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB7-bank-role-exemption-narrows-to-one-role",
|
||
"why": "the exemption is the role SET: a bank role answers a term table, whose letters are engine identifiers and source terms, so BOTH the echo rule and the target screen fire on every healthy batch. It was `role == roleTerminologist` for as long as no shipping config ran the classifier; row 140 runs it, so the regression goes live with it",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRunnerHandsTheScreenItsData",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\treturn role == roleTerminologist || role == roleClassifier",
|
||
"replace": "\treturn role == roleTerminologist"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB8-latin-residue-runs-on-a-latin-target",
|
||
"why": "the Latin-residue detector reads a run of Latin words as an untranslated leak — true for a Cyrillic target, inverted for a Latin-written one, where it drops every chunk of clean target prose. The guard was declared in a doccomment two layers up for years while standing only on the repair path",
|
||
"package": "./internal/checks/",
|
||
"run": "TestTheLatinResidueClassIsGatedOnTheTargetsOwnScript",
|
||
"edits": [
|
||
{
|
||
"file": "internal/checks/sanitizer.go",
|
||
"find": "\tif c.TargetScriptNonLatin() {\n\t\tif det := detectLatinInsertion(glossMasked); det != \"\" {",
|
||
"replace": "\tif true {\n\t\tif det := detectLatinInsertion(glossMasked); det != \"\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB9-contour-configured-and-inert",
|
||
"why": "both bank roles run INSIDE the bank-mining stop, which returns before building a single candidate without a contrast artifact. The loader refuses the unset key; without that refusal a fully budgeted contour is configured and never executes, the class this loader refuses everywhere else",
|
||
"package": "./internal/config/",
|
||
"run": "TestABankContourThatCanNeverFireIsRefused",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/pipeline.go",
|
||
"find": "\t\tif p.Mining.ContrastPath == \"\" {\n\t\t\tbad(\"gates.terminology is enabled but `mining.contrast_path` is not set",
|
||
"replace": "\t\tif false {\n\t\t\tbad(\"gates.terminology is enabled but `mining.contrast_path` is not set"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB10-missing-contrast-costs-a-wave",
|
||
"why": "the run opens the contrast artifact at the bank-mining stop, AFTER the whole draft wave is bought. The write-path preflight is what turns «a deployment forgot the artifact» from a wave's money into a free refusal naming the path",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheContourRefusesAWriteRunItCannotExecute",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "\t\tif err := pipe.CheckMiningContrast(); err != nil {\n\t\t\treturn nil, refuse(RefusalBadConfig, err)\n\t\t}",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB11-nil-pack-contour-never-runs",
|
||
"why": "the OTHER condition of the same early return, and the one a config loader structurally cannot see: a book with no langpack_root, or one whose pair has no catalog directory, gets a nil pack and the stop returns before any candidate exists. The gate is known to the pipeline and the pack to the book; only the runner holds both",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheContourRefusesAWriteRunItCannotExecute",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "\tif forWrite && r.Pipeline.Gates.Terminology.Enabled && r.pack == nil {",
|
||
"replace": "\tif false && r.Pipeline.Gates.Terminology.Enabled && r.pack == nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB12-two-orders-for-one-term",
|
||
"why": "the law block binds a canon to its SOURCE, and one surface legitimately carries several signed senses (the bank's key is (src, sense, since, until)). Deduping on the bare src drops a signature silently while the translator's block still renders both — the regression this entry used to enshrine before the rule was corrected",
|
||
"package": "./internal/membank/",
|
||
"run": "TestTheLawBlockGivesOneOrderPerSourceTerm",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tkey := src + \"\\x00\" + dst",
|
||
"replace": "\t\tkey := src"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB13-placeholder-refusal-arrives-after-a-paid-wave",
|
||
"why": "Render refuses an unknown placeholder per stage per chunk MID-RUN, so a prompt pack carrying a retired marker fails on the first call of that stage — for an editor prompt, after the whole draft wave is bought (measured on the {{genre}} retirement). The load-time scan makes the same refusal free. 27 eval/ prompt files still carry {{genre}}",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestRenderRejectsUnknownPlaceholder",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/render.go",
|
||
"find": "\tif err := CheckPlaceholders(canon); err != nil {\n\t\treturn nil, fmt.Errorf(\"pipeline: prompt %s: %w\", path, err)\n\t}",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB14-corpus-softened-until-the-number-looked-good",
|
||
"why": "a FIXTURE mutation, and the first this catalogue carries. The 12.87x margin rests on ONE healthy specimen; zeroing its source runes left the whole battery green while REPORTING A BETTER NUMBER than the corpus ever had, because the margin test asserts a floor and a floor is not a measurement. Found by planting into data, which is the half of the method that was missing",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheCorpusStillHoldsItsHardestSpecimens",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/testdata/offtarget-corpus.json",
|
||
"find": "\"source_script_runes\": 109,\n \"total_runes\": 9352,",
|
||
"replace": "\"source_script_runes\": 0,\n \"total_runes\": 9352,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB15-screen-floor-slides-inside-its-interval",
|
||
"why": "a threshold ratified as a CONTRACT (D39.92 п.1а) was pinned only to an INTERVAL — 0.45 is still inside the corpus's empty band — so a slide re-verdicted a resumed chunk under a threshold nobody re-billed for. Version is derived from the constants, so THIS edit moves the snapshot and the golden catches it. ⚠ THAT IS THE WHOLE OF WHAT THIS ENTRY PROVES: the golden sees a slide, and it CANNOT see a literal that stops tracking the constants — under `literal + slide` the string is byte-identical, the golden does not move and this entry survives. The derivation itself is WB36's, in the langscreen package. An earlier `why` here claimed «the snapshot moves by construction» and read as a guarantee against both",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestGoldenDeterminism",
|
||
"edits": [
|
||
{
|
||
"file": "internal/langscreen/langscreen.go",
|
||
"find": "const OnTargetFloor = 0.50",
|
||
"replace": "const OnTargetFloor = 0.45"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB16-validator-blesses-what-the-renderer-refuses",
|
||
"why": "the load-time gate and the renderer must accept the SAME set. The validator's own copy of that set is gone (it now renders), so this entry attacks what replaced it: a gate that swallows the renderer's unknown-name refusal blesses every word and the pack dies mid-run, after the wave",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheValidatorAndTheRendererAcceptTheSameSet",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/render.go",
|
||
"find": "\tif _, err := render(tpl, renderVals(RenderVars{Book: &config.Book{}})); err != nil {",
|
||
"replace": "\tif _, err := render(tpl, renderVals(RenderVars{Book: &config.Book{}})); err != nil && !strings.Contains(err.Error(), \"unknown placeholder\") {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB17-priority-rank-loses-its-disposition-axis",
|
||
"why": "priorityRank orders by DISPOSITION and by STATUS, and every fixture correlated them (an auto row is always ambiguous), so dropping the disposition bit passed everything. The state that separates them is two SIGNED rows where one was matched by a collision-prone key — and since row 134 that state decides WHICH of two lawful orders the editor is given",
|
||
"package": "./internal/membank/",
|
||
"run": "TestTheBudgetKeepsTheTrustworthyMatchFirst",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\tif p.Disp != Confirmed {\n\t\trank |= 1 << 2\n\t}",
|
||
"replace": "\tif false {\n\t\trank |= 1 << 2\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB18-banknote-buys-what-nobody-reads",
|
||
"why": "the banknote channel's only consumer is the bank-mining stop; a book that cannot reach it still pays for the term table the pair's prompt asks for on every draft call. Not a refusal — the run is correct and the slice does its own job — but an operator must be told what is being bought",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheBanknoteChannelSaysWhenNobodyWillReadIt",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "\tif forWrite && r.Pipeline.Gates.Banknote.Enabled && (r.pack == nil || r.Pipeline.Mining.ContrastPath == \"\") {",
|
||
"replace": "\tif false && r.Pipeline.Gates.Banknote.Enabled && (r.pack == nil || r.Pipeline.Mining.ContrastPath == \"\") {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB19-a-prompt-path-routes-around-the-load-gate",
|
||
"why": "the load-time placeholder gate covers FOUR paths — a stage, a repair class, and each bank role — and «the check stands once and covers all of them» rested on the funnel's SHAPE, not on any pin. Every one of the four could be routed around it with the whole battery green; the cost is the refusal returning mid-run, after the wave that stage sits behind is bought",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryPromptLoadPathRefusesAnUnrenderableTemplate",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/render.go",
|
||
"find": "\tif err := CheckPlaceholders(canon); err != nil {\n\t\treturn nil, fmt.Errorf(\"pipeline: prompt %s: %w\", path, err)\n\t}",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB20-law-block-drops-a-second-signature",
|
||
"why": "the law block's dedup is about SIGNATURES, not about the surface. Keying it on the bare src discards a signed canon whenever one source term carries two — 青山 the surname and 青山 the direction, which the bank's (src, sense, …) key exists to permit — silently, while the translator's block still rendered both, so the two wires disagreed. A live regression of the dofix, found by an adversarial probe",
|
||
"package": "./internal/membank/",
|
||
"run": "TestTheLawBlockGivesOneOrderPerSourceTerm",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif p.entry.status != \"approved\" && signedSrc[src] {\n\t\t\tcontinue // an unsigned rendering yields to the signed canon of the same surface\n\t\t}",
|
||
"replace": "\t\tif seen[src] {\n\t\t\tcontinue\n\t\t}\n\t\tseen[src] = true"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB21-bank-models-are-not-reachable-models",
|
||
"why": "buildClients pre-builds ReachableModels and CheckKeys demands their keys, both at OPEN time. With the bank roles missing from that list a terminologist or classifier model without a key passes the whole preflight and kills the run after the draft wave is bought",
|
||
"package": "./internal/config/",
|
||
"run": "TestABankModelIsAReachableModel",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/pipeline.go",
|
||
"find": "\tif p.Gates.Terminology.Enabled {\n\t\tif m := p.Gates.Terminology.Model; m != \"\" {",
|
||
"replace": "\tif false && p.Gates.Terminology.Enabled {\n\t\tif m := p.Gates.Terminology.Model; m != \"\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB22-sticky-carry-recomputes-the-trust",
|
||
"why": "a carry has no firing key of its own, so recomputing the match-trust from the ROW re-trusts a doubtful match — and since row 134 that hands the model a gender directive for an entity that may not be on the page. Invisible where src IS the firing key, which is every fixture that existed; the separating state is a Han src with a short phonetic ALIAS",
|
||
"package": "./internal/membank/",
|
||
"run": "TestAStickyCarryKeepsTheTrustOfTheKeyThatFiredIt",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "Disp: carried.Disp, KeyTrusted: carried.KeyTrusted}",
|
||
"replace": "Disp: carried.Disp, KeyTrusted: keyTrusted(e, text.NormalizeSourceKey(e.src))}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB24-sticky-window-narrows",
|
||
"why": "scene inertia carries the previous TWO chunks; at one, a term named in chunk N is gone by N+2 and the injection silently narrows. Survived the first round's plantings and was carried as a finding before it was closed",
|
||
"package": "./internal/membank/",
|
||
"run": "TestTheStickyWindowIsTwoChunksDeep",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "const StickyDepth = 2",
|
||
"replace": "const StickyDepth = 1"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB25-label-sweep-quietly-stops-covering-a-role",
|
||
"why": "the sweep walked only what it FOUND, never what the ledger RECORDS, so dropping a role from it orphaned that role's entry and unpinned its prompt with every test green. The classifier line was added by the pack that made it ship, and removing it again cost nothing",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestPromptLabelsPinTheirBytes",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/promptlabel_test.go",
|
||
"find": "\t\t\t\t{\"classifier\", g.ClassifyPromptPath},\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB26-sticky-records-vanish-from-the-law-block",
|
||
"why": "the editor's block claims «every injected record with a rendering», and a term carried by scene inertia into a pronominal chunk is exactly where the editor most needs to know what the book calls it. Excluding carries dropped them from the law with the whole tree green, golden included",
|
||
"package": "./internal/membank/",
|
||
"run": "TestAStickyRecordIsStillLawForTheEditor",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\tvar lines []string\n\tseen := map[string]bool{}\n\tfor _, p := range injected {\n\t\tif !p.valid() {\n\t\t\tcontinue\n\t\t}",
|
||
"replace": "\tvar lines []string\n\tseen := map[string]bool{}\n\tfor _, p := range injected {\n\t\tif !p.valid() || p.Sticky {\n\t\t\tcontinue\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB27-the-stop-sentence-undercounts-what-was-billed",
|
||
"why": "the first number an operator reads is «%d paying output unit(s)», and dropping the flagged units out of it under-reports what the run was billed for. The struct's arithmetic was pinned; the SENTENCE, which is what reaches a human, was pinned nowhere",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheStopSentenceCountsEveryUnitTheRunPaidFor",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tv.MaxUnits, v.Paid()+v.Flagged, v.Delivered, v.Reworked)",
|
||
"replace": "\t\tv.MaxUnits, v.Paid(), v.Delivered, v.Reworked)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB28-a-free-unit-that-flagged-is-billed",
|
||
"why": "reconcile's default branch exists so a unit that rode along at $0 and then flagged is not counted as paid; without it the run invents a charge and reports money spent on nothing that was never spent",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestReconcileLeavesUnpaidAndReworkedUnitsAlone",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tdefault:\n\t\t\tcontinue // it was never counted as paid (a free unit that flagged on resume); nothing to move",
|
||
"replace": "\t\tdefault:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB29-a-flagged-rework-is-counted-twice",
|
||
"why": "a re-made unit that flags must LEAVE the rework count as it enters the flagged one; keeping both double-counts the grant and over-reports the paying units by one",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestReconcileLeavesUnpaidAndReworkedUnitsAlone",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tcase s.stop.Reworked > 0:\n\t\t\ts.stop.Reworked--",
|
||
"replace": "\t\tcase s.stop.Reworked > 0:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB30-offtarget-severity-ranked",
|
||
"why": "a flag the engine raises must be RANKED, or it becomes the most benign reason in the chapter passport and a chapter in the wrong language reports `length` as its worst problem",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\tFlagCJKArtifact: 1,\n\tFlagOffTargetLang: 1,\n",
|
||
"replace": "\tFlagCJKArtifact: 1,\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB31-cjk-target-unscreened",
|
||
"why": "a CJK target must be screened for the LANGUAGE of the answer; putting the screen back under the echo gate leaves ja/zh/ko books judged by nothing",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\t\tif !in.NonProseReply {\n",
|
||
"replace": "\t\tif !in.NonProseReply && !isCJKTarget(in.TargetLang) {\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB32-echo-rule-for-cjk-target",
|
||
"why": "the ECHO rule must stay OFF for a CJK target — asking «is the output in the source's script» of a →ja book flags every correct chapter",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\t\t\tif !isCJKTarget(in.TargetLang) {\n\t\t\t\tif share := sourceScriptShare(out, in.SourceScripts); share > cjkEchoThreshold {",
|
||
"replace": "\t\t\tif true {\n\t\t\t\tif share := sourceScriptShare(out, in.SourceScripts); share > cjkEchoThreshold {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB33-classify-input-loses-target-scripts",
|
||
"why": "every live classify() call must carry the target's scripts; the builder exists because the second call site forgot them once already",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/chunkrun.go",
|
||
"find": "\t\tTargetScripts: lang.LangScripts(r.Book.TargetLang),\n",
|
||
"replace": "\t\tTargetScripts: nil,\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB34-unlabelled-preflight-skips-gates",
|
||
"why": "the UNLABELLED preflight — the shipping default — must demand the keys of the models the GATES call, or the run buys the draft wave and dies at the gate",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/models.go",
|
||
"find": "\tfor _, mdl := range pipe.gateModels() {\n\t\tneeded[mdl] = struct{}{}\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB35-classifier-model-demanded-when-off",
|
||
"why": "a gate that is OFF must not make a book hold a key it never uses; the classifier model is reachable only when the classify phase is on",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/pipeline.go",
|
||
"find": "\t\tif p.Gates.Terminology.ClassifyTypes {\n\t\t\tif m := p.Gates.Terminology.ClassifierModel(); m != \"\" {",
|
||
"replace": "\t\tif true {\n\t\t\tif m := p.Gates.Terminology.ClassifierModel(); m != \"\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB36-langscreen-version-literal",
|
||
"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",
|
||
"replace": "const OnTargetFloor = 0.45"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB37-langscreen-floor-subprecision",
|
||
"why": "the version's floor field must be able to EXPRESS the floor: a slide inside the last printed decimal moves no snapshot at all",
|
||
"package": "./internal/langscreen/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/langscreen/langscreen.go",
|
||
"find": "const OnTargetFloor = 0.50",
|
||
"replace": "const OnTargetFloor = 0.504"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB38-langscreen-boundary-flip",
|
||
"why": "«below the floor is off target» is the rule; a comparison that quietly becomes «at or below» changes verdicts and moves no constant",
|
||
"package": "./internal/langscreen/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/langscreen/langscreen.go",
|
||
"find": "\tif share < OnTargetFloor {",
|
||
"replace": "\tif share <= OnTargetFloor {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB39-load-gate-scans-system-only",
|
||
"why": "the load gate must scan the WHOLE prompt file: a retired marker in the user tail or a few-shot block otherwise loads clean and fails inside Render, after the wave",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/render.go",
|
||
"find": "\tif err := CheckPlaceholders(canon); err != nil {",
|
||
"replace": "\tif err := CheckPlaceholders(t.System); err != nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB40-sticky-carry-disp-recomputed",
|
||
"why": "a sticky carry has no firing key of its own, so BOTH halves are inherited; recomputing the disposition from the row's own surface is the D16.2 upgrade in the other half's coat",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "Disp: carried.Disp, KeyTrusted: carried.KeyTrusted}",
|
||
"replace": "Disp: dispositionFor(e, text.NormalizeSourceKey(e.src)), KeyTrusted: carried.KeyTrusted}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB41-signed-set-keyed-on-disposition",
|
||
"why": "signature is a STATUS, not a confidence: a signed row that fired on a doubtful key is still the canon others yield to",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif p.valid() && p.entry.status == \"approved\" && strings.TrimSpace(p.entry.dst) != \"\" {",
|
||
"replace": "\t\tif p.valid() && p.Disp == Confirmed && strings.TrimSpace(p.entry.dst) != \"\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB42-only-draft-yields-to-canon",
|
||
"why": "what yields to a signed canon is every UNSIGNED status, `auto` included — `auto` is what the miner writes, i.e. the row the rule exists to subordinate",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif p.entry.status != \"approved\" && signedSrc[src] {",
|
||
"replace": "\t\tif p.entry.status == \"draft\" && signedSrc[src] {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB46-runner-reachable-stage-only",
|
||
"why": "the runner's eager client map must cover the models a GATE calls; a miss there is not a fallback, it is the run dying after the draft wave is paid for",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "\treturn r.Pipeline.ReachableModels()\n",
|
||
"replace": "\tvar out []string\n\tfor _, st := range r.Pipeline.Stages {\n\t\tout = append(out, st.ResolvedModel)\n\t}\n\treturn out\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB47-flagged-clause-prints-rework",
|
||
"why": "the operator sentence's FLAGGED count must be the flagged count: it printed the rework count with the whole tree green, because both fixtures had them equal",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "re-attacks a flag)\", v.Flagged)",
|
||
"replace": "re-attacks a flag)\", v.Reworked)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB48-mined-rows-admitted-to-base-bank",
|
||
"why": "the DRAFT wave selects over the base bank (Source:mined excluded) — that is what makes «one re-payment» honest at the WIRE level and not only in the version hash",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankmaterialize.go",
|
||
"find": "\t\tif row.Source == \"mined\" {\n\t\t\thasMined = true\n\t\t\tcontinue\n\t\t}",
|
||
"replace": "\t\tif row.Source == \"mined\" {\n\t\t\thasMined = true\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB43-law-block-single-pass",
|
||
"why": "the law block must not depend on the input ORDER: a single pass that records signatures as it goes keeps whichever row arrived first, and passes every fixture because Select sorts the signed row to the front in production",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\tsignedSrc := map[string]bool{}\n\tfor _, p := range injected {\n\t\tif p.valid() && p.entry.status == \"approved\" && strings.TrimSpace(p.entry.dst) != \"\" {\n\t\t\tsignedSrc[strings.TrimSpace(p.entry.src)] = true\n\t\t}\n\t}\n",
|
||
"replace": "\tsignedSrc := map[string]bool{}\n"
|
||
},
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif p.entry.status != \"approved\" && signedSrc[src] {\n\t\t\tcontinue // an unsigned rendering yields to the signed canon of the same surface\n\t\t}",
|
||
"replace": "\t\tif p.entry.status == \"approved\" && strings.TrimSpace(p.entry.dst) != \"\" {\n\t\t\tsignedSrc[src] = true\n\t\t}\n\t\tif p.entry.status != \"approved\" && signedSrc[src] {\n\t\t\tcontinue // an unsigned rendering yields to the signed canon of the same surface\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB44-corpus-second-hardest-softened",
|
||
"why": "the corpus ledger must pin the POPULATION, not one extremum per axis: every hard specimen but the worst could otherwise be softened with every test green",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/testdata/offtarget-corpus.json",
|
||
"find": " \"id\": \"ca9277ac34ae\",\n \"label\": \"healthy\",\n \"source_script_runes\": 42,",
|
||
"replace": " \"id\": \"ca9277ac34ae\",\n \"label\": \"healthy\",\n \"source_script_runes\": 0,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB45-corpus-offtarget-side-drifts",
|
||
"why": "the empty band the 0.50 floor sits in has TWO sides: the off-target extremum was compared constant-to-constant, so the data could drift to 0.49 with every corpus test green and the floor would sit on top of a specimen",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/testdata/offtarget-corpus.json",
|
||
"find": " \"id\": \"33affac80ec5\",\n \"label\": \"off_target\",\n \"source_script_runes\": 0,\n \"total_runes\": 5990,\n \"letters\": 4690,\n \"target_script_letters\": 0,",
|
||
"replace": " \"id\": \"33affac80ec5\",\n \"label\": \"off_target\",\n \"source_script_runes\": 0,\n \"total_runes\": 5990,\n \"letters\": 4690,\n \"target_script_letters\": 2300,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB49-empty-canon-swallows-the-term",
|
||
"why": "a signed row with NO rendering orders nothing, so nothing may yield to it — otherwise the unsigned rendering is dropped in favour of a row that prints no line and the term leaves the law block entirely",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif p.valid() && p.entry.status == \"approved\" && strings.TrimSpace(p.entry.dst) != \"\" {",
|
||
"replace": "\t\tif p.valid() && p.entry.status == \"approved\" {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB50-signed-set-untrimmed-src",
|
||
"why": "the signature set and the lookup must normalize the surface the same way; recorded untrimmed and looked up trimmed, the set silently stops matching and every unsigned row of that surface becomes law again",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\t\tsignedSrc[strings.TrimSpace(p.entry.src)] = true",
|
||
"replace": "\t\t\tsignedSrc[p.entry.src] = true"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB51-flag-reason-declared-elsewhere",
|
||
"why": "every FlagReason the engine declares must be RANKED; the exhaustiveness test used to read one file, so a reason declared anywhere else fell to the unknown bucket while the test still said «every declared reason is ranked»",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "const severityUnknown = 9\n",
|
||
"replace": "const severityUnknown = 9\n\n// planted\nconst FlagPlantedElsewhere FlagReason = \"planted_elsewhere\"\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB52-builder-hardcodes-the-target",
|
||
"why": "the classify input's target must come from the BOOK; a hard-coded target makes every book judged as the shipping pair, and no runner fixture translates into anything but Russian, so nothing else can see it",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/chunkrun.go",
|
||
"find": "\t\tTargetLang: r.Book.TargetLang,\n",
|
||
"replace": "\t\tTargetLang: \"ru\",\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB53-builder-hardcodes-target-scripts",
|
||
"why": "the target's scripts must come from the BOOK's target: hard-coded, a →ja book is screened against Cyrillic and every correct chapter is off-target",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/chunkrun.go",
|
||
"find": "\t\tTargetScripts: lang.LangScripts(r.Book.TargetLang),\n",
|
||
"replace": "\t\tTargetScripts: lang.LangScripts(\"ru\"),\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB54-builder-hardcodes-source-scripts",
|
||
"why": "the source's scripts must come from the RUN's compiled checkers: hard-coded to zh, the echo rule measures the wrong script for every other source language",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/chunkrun.go",
|
||
"find": "\t\tSourceScripts: r.checkers.SourceScripts(),\n",
|
||
"replace": "\t\tSourceScripts: lang.LangScripts(\"zh\"),\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB56-labelled-preflight-skips-gates",
|
||
"why": "the LABELLED branch of CheckKeys must demand the keys of the models the gates call: it was pinned only by the reachable LIST, which is exactly the thing «a pin on the list is not a pin on the preflight» rejects",
|
||
"package": "./internal/config/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/models.go",
|
||
"find": "\t\tfor _, mdl := range pipe.ReachableModels() {\n\t\t\tneeded[mdl] = struct{}{}\n\t\t}\n\t\treturn m.checkKeysFor(needed)",
|
||
"replace": "\t\tfor _, st := range pipe.Stages {\n\t\t\tneeded[st.ResolvedModel] = struct{}{}\n\t\t}\n\t\treturn m.checkKeysFor(needed)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB57-banknote-warning-loses-the-pack-arm",
|
||
"why": "the banknote warning fires when EITHER mining input is missing; keyed on the contrast artifact alone, a book with a pack and no contrast pays for a term table nobody reads and is told nothing",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "(r.pack == nil || r.Pipeline.Mining.ContrastPath == \"\")",
|
||
"replace": "(r.Pipeline.Mining.ContrastPath == \"\")"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB58-banknote-warning-loses-the-contrast-arm",
|
||
"why": "the mirror arm: keyed on the pack alone, a book with a contrast artifact and no langpack pays for the same unread table in silence",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "(r.pack == nil || r.Pipeline.Mining.ContrastPath == \"\")",
|
||
"replace": "(r.pack == nil)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB59-banknote-warning-fires-on-a-read-open",
|
||
"why": "a $0 read path buys nothing, so warning there teaches an operator to ignore the line on the run where it costs money",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "\tif forWrite && r.Pipeline.Gates.Banknote.Enabled && (",
|
||
"replace": "\tif r.Pipeline.Gates.Banknote.Enabled && ("
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB60-volume-report-decided-after-reconcile",
|
||
"why": "the volume report is decided BEFORE reconcile trues the counters up: decided after, a run whose only work outside its grant was a carried unit that came back FLAGGED has Carried==0 and reports nothing at all — the one run that most needs the disclosure goes silent. ⚠ Run-filtered: this planting also strips the report from the DRAFT-ONLY path, which three neighbouring tests catch for a different reason (they lose the report entirely, not out of order). Unfiltered it would go red without proving anything about this pin. WB64 is the faithful both-paths form",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\tif scope.bound() {\n\t\tres.Volume = &scope.stop\n",
|
||
"replace": "\tif scope.bound() {\n"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\tscope.reconcile(res.Chunks)\n\tr.Log.InfoContext(ctx, \"book run finished\", \"book\"",
|
||
"replace": "\tscope.reconcile(res.Chunks)\n\tif scope.bound() {\n\t\tres.Volume = &scope.stop\n\t}\n\tr.Log.InfoContext(ctx, \"book run finished\", \"book\""
|
||
}
|
||
],
|
||
"run": "TestTheVolumeReportIsAskedBeforeTheCountersAreTruedUp"
|
||
},
|
||
{
|
||
"id": "WB55-corpus-provenance-swapped",
|
||
"why": "the corpus digest must cover the ADDRESS a specimen was taken from, not only its counts: a corpus whose numbers are right and whose provenance is wrong is unreproducible in the one way that matters",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/testdata/offtarget-corpus.json",
|
||
"find": "\"provenance\": \"probe-4axes/arms/backups/20260902T075308Z.db#draft\",\n \"head\": \"Рассечение камня",
|
||
"replace": "\"provenance\": \"planted/not-the-address-it-came-from.db#draft\",\n \"head\": \"Рассечение камня"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB61-builder-hardcodes-the-fixtures-own-pair",
|
||
"why": "a hard-code to the FIXTURE'S OWN pair is the hole a value-comparing test cannot see: the earlier pin asserted the builder's output against values chosen for one book, so it caught a hard-code to the shipping pair and would have sailed past a hard-code to the pair the test itself uses. Only «two books must answer differently» is unsatisfiable by any constant. ⚠ Run-filtered on purpose: a hard-code to `ja` also breaks twenty ru fixtures, so an unfiltered entry would go red without proving anything about THIS pin. The dangerous hard-code is the one to the SHIPPING pair (WB52), which nothing else sees",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/chunkrun.go",
|
||
"find": "\t\tTargetLang: r.Book.TargetLang,\n",
|
||
"replace": "\t\tTargetLang: \"ja\",\n"
|
||
}
|
||
],
|
||
"run": "TestClassifyInputForLeavesNoAxisUnset"
|
||
},
|
||
{
|
||
"id": "WB62-innocent-const-must-not-be-accused",
|
||
"expect": "survives",
|
||
"why": "THE BOUNDARY OF TestEveryFlagReasonIsRanked, recorded because the test used to cross it. An ordinary untyped string constant sitting in the FlagReason block is NOT a flag — Go types it as untyped string, inheriting nothing — and the walk must ignore it. An earlier version carried the previous spec's type forward and failed the test with a message accusing a documentation URL of being an unranked flag. A test that accuses the innocent is not enforcing its own sentence, so this planting MUST survive",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryFlagReasonIsRanked",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\tFlagSanitizerStripped FlagReason = \"sanitizer_stripped\"\n",
|
||
"replace": "\tFlagSanitizerStripped FlagReason = \"sanitizer_stripped\"\n\n\t// not a flag: a documentation pointer that happens to live in this block\n\tflagReasonDocURL = \"https://example.invalid/flags\"\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB63-run-compiles-a-constant-source",
|
||
"why": "the run's declared SOURCE scripts must come from the book. This is the WRITER of the axis whose reader was pinned one commit earlier — pinning the reader alone left the whole axis decided by an unguarded line, and a constant here gives a ko→ru or ja→ru book the wrong declared source while the echo detector measures a script the book does not use",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRunCompilesTheBooksOwnSourceScripts",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/runner.go",
|
||
"find": "\t\tr.checkers.SetSourceScripts(lang.LangScripts(r.Book.SourceLang))\n",
|
||
"replace": "\t\tr.checkers.SetSourceScripts(lang.LangScripts(\"ja\"))\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB64-volume-report-after-reconcile-on-both-paths",
|
||
"why": "the FAITHFUL form of the ordering defect: the decision moved after reconcile on BOTH the draft-only and the editor path, so no neighbour loses its report for an unrelated reason. Only a fixture whose single unit is carried AND flagged can tell the two orders apart, and until this entry existed the draft-only path had none",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheVolumeReportIsAskedBefore",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\tif scope.bound() {\n\t\tres.Volume = &scope.stop\n",
|
||
"replace": "\tif scope.bound() {\n"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\tscope.reconcile(res.Chunks)\n\t\tr.Log.InfoContext(ctx, \"book run finished (draft-only)\"",
|
||
"replace": "\t\tscope.reconcile(res.Chunks)\n\t\tif scope.bound() {\n\t\t\tres.Volume = &scope.stop\n\t\t}\n\t\tr.Log.InfoContext(ctx, \"book run finished (draft-only)\""
|
||
},
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\tscope.reconcile(res.Chunks)\n\tr.Log.InfoContext(ctx, \"book run finished\", \"book\"",
|
||
"replace": "\tscope.reconcile(res.Chunks)\n\tif scope.bound() {\n\t\tres.Volume = &scope.stop\n\t}\n\tr.Log.InfoContext(ctx, \"book run finished\", \"book\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB65-labelled-preflight-skips-the-hop",
|
||
"why": "a labelled run CALLS its escalation hop, and BuildClient never refuses an empty key — it only becomes a header. A preflight blind to the hop lets the book open, buy the draft wave and 401 at the escalation: the same late failure as the gates, one field over",
|
||
"package": "./internal/config/",
|
||
"run": "TestTheLABELLEDPreflightDemandsEveryCallersKeyToo",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/pipeline.go",
|
||
"find": "\t\tadd(st.ResolvedHop)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB66-preflight-demands-the-configured-not-the-resolved-model",
|
||
"why": "routing is what a labelled book is FOR: the model in the yaml may not be the model that answers. A preflight that demands the CONFIGURED model passes a book whose RESOLVED model has no key, and the refusal moves from open time to the first draft call",
|
||
"package": "./internal/config/",
|
||
"run": "TestTheLABELLEDPreflightDemandsEveryCallersKeyToo",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/pipeline.go",
|
||
"find": "\t\tadd(st.ResolvedModel)\n",
|
||
"replace": "\t\tadd(st.Model)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WB67-flag-reason-in-the-conversion-spelling",
|
||
"why": "`X = FlagReason(\"…\")` is as ordinary a declaration as `X FlagReason = \"…\"`, and a walk that reads only the spec's type is blind to it — exhibited as a pair, the typed spelling caught and the conversion spelling not",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryFlagReasonIsRanked",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "const severityUnknown = 9\n",
|
||
"replace": "const severityUnknown = 9\n\n// planted\nconst FlagPlantedConv = FlagReason(\"planted_conv\")\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WF-length-bound",
|
||
"why": "row 271: a bank value rides verbatim inside the SYSTEM message of every paid call, so its length is a fence and not a nicety — lifting the bound lets one term become a document inside the block",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/wirefence.go",
|
||
"find": "\tif n := utf8.RuneCountInString(s); n > WireFieldMaxRunes {",
|
||
"replace": "\tif n := utf8.RuneCountInString(s); n > WireFieldMaxRunes && false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WF-control-ban",
|
||
"why": "row 271: a line feed in `dst` ENDS the line of the mapping and writes new lines into the system block — the ban on line-ending/reordering runes is the whole of the fix, and it must not be liftable while the battery stays green",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/wirefence.go",
|
||
"find": "\tcase unicode.IsControl(r):",
|
||
"replace": "\tcase unicode.IsControl(r) && false:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WF-render-backstop",
|
||
"why": "the fence's LAST point: door and seed loader make it unreachable today, so only a planting can say whether the renderer still holds it for a writer added later",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif WireUnfitRow(p.entry.src, p.entry.dst) {\n\t\t\tcontinue\n\t\t}\n\t\tline := p.entry.src + \" → \" + p.entry.dst",
|
||
"replace": "\t\tline := p.entry.src + \" → \" + p.entry.dst"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WF-door-refusal",
|
||
"why": "row 271: the user door is where a person finds out their correction cannot ride on the wire — without the refusal the term is accepted and then silently absent from every request",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/decisions.go",
|
||
"find": "\tif why := WireUnfitJoined(d.Src, d.Dst); why != \"\" {\n\t\treturn termKey{}, fmt.Errorf(\"%s\", why)\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WF-seed-refusal",
|
||
"why": "an operator's seed file is theirs to fix: dropping such a row on the way to the wire would leave a term they believe is in force absent from every paid call, with nothing said",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "\t\tif reasons := WireUnfitReasons(t.Src, t.Dst); len(reasons) > 0 {",
|
||
"replace": "\t\tif reasons := WireUnfitReasons(t.Src, t.Dst); false && len(reasons) > 0 {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "WF-version-fold-scope",
|
||
"why": "the fence changes the WIRE for a book holding an unfit row, and a wire change the snapshot cannot see is a silent re-payment outside the consent contour (the editor-unverified/neuter class, a third time)",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memory.go",
|
||
"find": "\t\tif WireUnfitRow(r.Src, r.Dst) {\n\t\t\thasUnfit = true\n\t\t}",
|
||
"replace": "\t\tif WireUnfitRow(r.Src, r.Dst) {\n\t\t\t_ = r\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-latch-is-a-cancel",
|
||
"why": "row 277, THE NAMED ACCEPTANCE PLANTING: it restores the PRE-PACK world in one act — no waiting for headroom, and a refusal that cancels the wave — because that pair is what killed the calls already admitted and made a book produce nothing at any price (measured live 04.09, reproduced twice). ⚠ THE FIRST VERSION OF THIS ENTRY ADDED ONLY THE cancel() AND SURVIVED, and the reason is worth keeping: with the wait in place a refusal does not become terminal until the sky is empty, so by the time the latch fires there is nothing left in flight for a cancel to kill. The defect needs both halves; so does the planting that reproduces it.",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tif !settleCannotHelp(scope, committed, estimate, ceiling) {",
|
||
"replace": "\t\tif false && !settleCannotHelp(scope, committed, estimate, ceiling) {"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\t\tr.events.ceilingReached(halt)\n\t\t\tlatchOnce.Do(func() { close(latched) })\n\t\t\treturn",
|
||
"replace": "\t\t\tr.events.ceilingReached(halt)\n\t\t\tlatchOnce.Do(func() { close(latched) })\n\t\t\tcancel()\n\t\t\treturn"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-wait-for-settle",
|
||
"why": "a refusal is not final while another call still holds a reservation: settle returns estimate-cost, so the headroom is still moving and re-asking is what turns a dead run into a delivered book",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tif !settleCannotHelp(scope, committed, estimate, ceiling) {",
|
||
"replace": "\t\tif false && !settleCannotHelp(scope, committed, estimate, ceiling) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-reserve-under-the-gate",
|
||
"why": "the admission and the in-flight counter must be ONE critical section: split, a worker that has committed its reservation but not yet recorded itself lets a refused sibling read zero in flight and declare the book finished while money is about to come back. ⚠ CATALOGUED AS A SURVIVOR, AND «SURVIVED» HERE DOES NOT MEAN «THE GUARANTEE HOLDS» — it means the tooling cannot ask the question. Reproducing the race needs the two critical sections interleaved on demand, which takes a hook in the scheduler this repository does not have; a sleep-based «proof» would pin a duration rather than the invariant. Read this green as «unverifiable by this instrument», never as a pin.",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reservegate.go",
|
||
"find": "\tg.mu.Lock()\n\tdefer g.mu.Unlock()\n\tresv, verdict, err := s.Reserve(bookID, estimate, c)",
|
||
"replace": "\tresv, verdict, err := s.Reserve(bookID, estimate, c)\n\tg.mu.Lock()\n\tdefer g.mu.Unlock()"
|
||
}
|
||
],
|
||
"expect": "survives"
|
||
},
|
||
{
|
||
"id": "MS-cancelled-wait-is-not-money",
|
||
"why": "a wait ended by ctx cancellation is NOT a money stop: reported as one, a run somebody stopped publishes a ceiling event with a shortfall and the platform tells a person to add money that would change nothing",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reservegate.go",
|
||
"find": "\tcase <-ctx.Done():\n\t\treturn waitAborted",
|
||
"replace": "\tcase <-ctx.Done():\n\t\treturn waitNothingInFlight"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-draft-stop-does-not-edit",
|
||
"why": "a money stop in the DRAFT wave must not start the edit wave: an unstarted member is found in draftByKey holding the zero value, so the editor is paid to edit a unit with a hole and then ships it",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\tr.reportEvicted(ctx, \"draft\")\n\t\treturn r.moneyStoppedResult(ctx, chunks, drafted, draftResults, editWave), err",
|
||
"replace": "\t\tr.reportEvicted(ctx, \"draft\")\n\t\t_ = drafted"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-shortfall-on-the-wire",
|
||
"why": "row 278: the missing amount died in the stderr of a transient unit — without it on the ceiling frame a buyer is told the run stopped and nothing about what would restart it",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/events.go",
|
||
"find": "\te.emit(runevents.TypeCeiling, runevents.Ceiling{Halted: true, Scope: halt.Scope, ShortfallMicroUSD: halt.ShortfallMicroUSD})",
|
||
"replace": "\te.emit(runevents.TypeCeiling, runevents.Ceiling{Halted: true, Scope: halt.Scope})"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-stream-version-bump",
|
||
"why": "the seam rule: a frame that gained a field must say so, or two streams differing in content claim one version — the constant's own comment records this exact miss, made once already",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/runevents/runevents.go",
|
||
"find": "const StreamVersion = \"1.3\"",
|
||
"replace": "const StreamVersion = \"1.2\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-volume-ledger-on-a-money-stop",
|
||
"why": "VolumeStop's seven counters are a PLAN; a spend stop leaves them counting units that were admitted and never done — Delivered over-counts, LeftFresh under-counts, and at Left()==0 the run announces it reached the end of the book. ⚠ ITS FIRST PIN WAS VACUOUS and this entry SURVIVED it: the fixture granted no volume at all, so res.Volume was nil whatever the code did. Only a run that HAS a grant can have one wrongly attached.",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "func (r *Runner) noteMoneyStop(ctx context.Context, res *BookResult) {\n\tres.Volume = nil",
|
||
"replace": "func (r *Runner) noteMoneyStop(ctx context.Context, res *BookResult) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-step-max-ignores-retries",
|
||
"why": "row 278: maxTokensForAttempt DOUBLES the budget on every regeneration and a refused RETRY does not degrade — a ceiling sized for attempt 0 admits a unit's first call and refuses its remedy on every resume forever",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\t\tfor escalations := 0; escalations <= p.maxRegen; escalations++ {",
|
||
"replace": "\t\tfor escalations := 0; escalations <= 0; escalations++ {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-executor-sizing-shared",
|
||
"why": "reader and writer of one arithmetic: the projection reproduces the executor's output budget, and a projection that sized calls its own way would drift from the reservations the run actually makes, invisibly",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\t\tbase := r.baseMaxTokensFor(sp.st, sizing)",
|
||
"replace": "\t\tbase := sizing"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-structure-provenance",
|
||
"why": "an order phrased in chapters against a DETECTED cut is an order against a guess; collapsing the values hides which one the reader has. RE-AIMED by the chapter-structure pack: the three-value if/else it used to target was replaced by the winning path's witness, so the mutation now collapses the witness instead",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/ingest.go",
|
||
"find": "\tif len(chapters) < 2 {\n\t\treturn StructureNone\n\t}\n\treturn witness",
|
||
"replace": "\tif len(chapters) < 2 {\n\t\treturn StructureNone\n\t}\n\treturn StructureDeclared"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-price-reaches-status",
|
||
"why": "the surface a buyer's platform reads before deciding must answer the price question too — a projection that only the manifest carries leaves status blind exactly where the money decision is made",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\trep.Price, rep.Structure = r.readModelPrice(withText)",
|
||
"replace": "\t_ = withText"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-price-fallback-path",
|
||
"why": "the manifest is an ACCELERATOR, and an accelerator that changes the ANSWER is a second source of truth: without the fallback a book with no current sidecar reports no price at all, silently, on the surface a buyer's platform reads before deciding",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\tfull, err := withText()\n\tif err != nil || len(full) == 0 {\n\t\treturn nil, \"\"\n\t}\n\t_, bp := r.projectBook(r.pricePlanFor(), r.outputUnits(full))\n\treturn bp, r.cutStructure",
|
||
"replace": "\treturn nil, \"\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-reservation-released-on-marshal-failure",
|
||
"why": "a reservation left behind is headroom the book paid for and cannot use, and since the stop now PUBLISHES the shortfall a leak makes the engine ask a person for more money than it needs. ⚠ CATALOGUED AS A SURVIVOR, and the argument is that the branch is UNREACHABLE rather than untested: it fires only if json.Marshal fails on llm.Usage, a struct of ints, which the encoding/json contract does not allow. No test can red it without faking the standard library, and the repair is kept because the money path must be correct on the paths nobody can reach as well as on the ones they can.",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tr.releaseReservation(ctx, resv)\n\t\tr.setJobStatus(ctx, job.ID, \"failed\")\n\t\treturn att, err\n\t}\n\t// Same as the billed-decode branch above: the burn walk's money is added, never replaced.\n\tatt.cumCost, att.runCost = burnedCost+cost, cost",
|
||
"replace": "\t\tr.setJobStatus(ctx, job.ID, \"failed\")\n\t\treturn att, err\n\t}\n\t// Same as the billed-decode branch above: the burn walk's money is added, never replaced.\n\tatt.cumCost, att.runCost = burnedCost+cost, cost"
|
||
}
|
||
],
|
||
"expect": "survives"
|
||
},
|
||
{
|
||
"id": "MS-two-error-slots",
|
||
"why": "a ceiling no longer cancels, so a run can latch on money and THEN break: sharing one slot lets whichever came first speak for both, and a first-wins ceiling makes a BROKEN run depart exit 4, which the platform records as `paused`",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\t\tmu.Lock()\n\t\t\tif haltErr == nil {\n\t\t\t\thaltErr = err\n\t\t\t}\n\t\t\tmu.Unlock()",
|
||
"replace": "\t\t\tmu.Lock()\n\t\t\tif firstErr == nil {\n\t\t\t\tfirstErr = err\n\t\t\t}\n\t\t\tmu.Unlock()"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-crash-outranks-the-wave-error",
|
||
"why": "a crashed process is not a pause and is not a graceful stop: routed behind the wave's own error it departs as the sibling's ceiling (exit 4) or cancellation (exit 5), and its stack is discarded",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\tif panicErr != nil {",
|
||
"replace": "\tif firstErr != nil {\n\t\treturn firstErr\n\t}\n\tif panicErr != nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-fewshot-toggle-respected",
|
||
"why": "the projection must ask the EXECUTOR'S question (SystemFor + fewShotEnabled), not add the template's fields up: the shipped c1 editor has few_shot OFF while its template still carries the block, so summing it over-charges every edit call of every book on that arm — invisibly, because a price 20% too high looks exactly like a price",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\t\t\tsp.tplTokens = EstimateTokens(tpl.SystemFor(fewShotEnabled(st))) + EstimateTokens(tpl.User)",
|
||
"replace": "\t\t\tsp.tplTokens = EstimateTokens(tpl.System) + EstimateTokens(tpl.FewShot) + EstimateTokens(tpl.User)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-source-rides-every-stage-that-asks",
|
||
"why": "the executor renders EVERY stage with RenderVars{Text, Draft}, and the shipped editor is BILINGUAL (D30.1) — dropping the source from a later stage's prompt under-states a unit's bill by roughly a sixth, in the direction that lets a platform sell a book for less than it costs",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\tif sp.carriesSource && !inputIsSource {",
|
||
"replace": "\tif false && sp.carriesSource && !inputIsSource {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-source-double-charged",
|
||
"why": "the FIRST stage's input IS the source, so adding it again double-charges the draft call — the mirror error of dropping it, and it would raise the minimum purchase a platform demands",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\tif sp.carriesSource && !inputIsSource {",
|
||
"replace": "\tif sp.carriesSource {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-wait-counter",
|
||
"why": "a test can only see the WAITING MECHANISM through a fact it leaves behind: without the counter the delivering-ceiling test asserts an outcome the run can reach without ever waiting — which is exactly how it passed on a fixture whose ceiling never refused anything",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reservegate.go",
|
||
"find": "\t\tg.mu.Lock()\n\t\tg.waits++\n\t\tg.mu.Unlock()\n\t\treturn waitSettled",
|
||
"replace": "\t\treturn waitSettled"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-draft-priced-per-unit-not-per-chunk",
|
||
"why": "the wave driver fans the DRAFT over chunks and the EDIT over units: pricing the draft per unit charges the template and the injection once where the executor pays them per member, and sizes the largest single reservation from the whole unit instead of from one call — the number a platform sets its minimum purchase by",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\tfor _, m := range u.Members {\n\t\tdraftTokens += p.walkDraftCalls(m, func(sp stagePrice, srcTok, in, out int, inIsSource bool) {",
|
||
"replace": "\tfor _, m := range u.Members[:1] {\n\t\tm.Text = src\n\t\tdraftTokens += p.walkDraftCalls(m, func(sp stagePrice, srcTok, in, out int, inIsSource bool) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-optional-call-queues-for-headroom",
|
||
"why": "an optional call that queued for the last dollars turns «do not start anything new» into «the optional spends what the mandatory needed» — and the escalation hop holds escMu while it waits, blocking every other worker's escalation. The rule lives in the gate precisely so a test can watch it happen",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/reservegate.go",
|
||
"find": "\tif !mandatory {",
|
||
"replace": "\tif false && !mandatory {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-terminal-branch-order",
|
||
"why": "the money ledger is attached whenever a ceiling was reached, and above the signature stop and the cancellation that branch swallows both: a bank-stop reports `failed` at exit 3 and a caught SIGTERM reports `failed` at exit 5. Two channels contradicting each other about one run is what this vocabulary exists to prevent — and re-ordering case arms is the edit a tidy-up makes without noticing",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/events.go",
|
||
"find": "\tcase errors.As(err, &sigStop):",
|
||
"replace": "\tcase err != nil && e.ceilingSaid:\n\t\te.emit(runevents.TypeFinished, runevents.Finished{Outcome: runevents.OutcomeFailed, Money: e.moneyLedger()})\n\tcase errors.As(err, &sigStop):"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-book-once-charge",
|
||
"why": "row 278: the terminology consolidation reads the WHOLE book's drafts once, so charging it per unit under-prices a short book exactly where the error hurts — and it is conditional on the gate AND the contrast key, the pair whose second half a deployment loses silently",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\tusd := g.Terminology.BudgetUSD",
|
||
"replace": "\tusd := 0.0"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-book-once-charged-per-unit",
|
||
"why": "the once-per-book charge added inside the unit loop is the very under-pricing inversion the field exists to prevent, only backwards: a long book would pay it a hundred times",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\t\tbp.ExpectedUSD += p.ExpectedUSD\n\t\tif step := r.stepMaxForUnit(plan, u, p); step > bp.StepMaxUSD {",
|
||
"replace": "\t\tbp.ExpectedUSD += p.ExpectedUSD + bp.BookOnceUSD\n\t\tif step := r.stepMaxForUnit(plan, u, p); step > bp.StepMaxUSD {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MS-ceiling-frame-idempotent",
|
||
"why": "under several workers one stop is refused several times; a `ceiling` frame per refusal makes a reader count refusals where it means to count stops",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/events.go",
|
||
"find": "\tif already {\n\t\treturn\n\t}",
|
||
"replace": "\t_ = already"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "PP-no-translator-stage-completion",
|
||
"why": "a pipeline whose first stage is not a translator has that stage DOING the translating: its completion is what the pair's fertility says a source of this shape produces, not the source's own token count. ⚠ THIS ENTRY SURVIVED ONCE AND THE SURVIVAL WAS THE FINDING: walkDraftCalls returned a draft size even when NO draft stage ran, so the fallback this mutation edits was DEAD CODE — a mutation cannot change a branch nothing reaches. The branch is live now, and the entry is what made it visible.",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\t\tin, out = up.PromptTokens, p.expectedDraftTokens(dense, sparse)",
|
||
"replace": "\t\tin, out = up.PromptTokens, up.PromptTokens"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-money-follows-the-ceiling-not-the-outcome",
|
||
"why": "acceptance F6/V2-4, found by BOTH verifiers independently: `Finished.money` follows «a ceiling was REACHED», not «the outcome word is ceiling» — the contract file said the latter, and a consumer in another zone building on either reading gets a different bug (money on `stopped`/`failed` is real; `ceiling` without seeded counters carries none)",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/events.go",
|
||
"find": "\tif !e.ceilingSaid || e.waves == nil {",
|
||
"replace": "\tif e.waves == nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-old-sidecar-must-not-silence-the-price",
|
||
"why": "acceptance V2-3: `price` and `structure` are ADDITIVE so the document version deliberately did not move for them — a sidecar from the previous build passes version, counters and key, comes back «current» carrying no price, and the fallback the code itself calls the invariant never runs. The book then reports NO price at all, silently, on the surface a buyer's platform reads before deciding",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\tif m := r.loadManifest(); m != nil && m.Price != nil && m.Structure != \"\" {",
|
||
"replace": "\tif m := r.loadManifest(); m != nil {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-engine-written-document-must-not-kill-the-run",
|
||
"why": "acceptance F8, widened: the mined delta AND the auto-bank are written by this engine from MODEL OUTPUT, and refusing such a document aborts a PAID run over our own answer while asking a person to hand-edit a file nobody authored. The operator's own seed keeps the refusal — that one is theirs to fix",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "\t\t\tif engineWritten {",
|
||
"replace": "\t\t\tif false && engineWritten {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-operator-seed-keeps-its-refusal",
|
||
"why": "the mirror of the split: dropping a row from the OPERATOR's seed would leave a term they believe is in force silently absent from every request — the refusal is what tells them",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "func ParseBankSeed(name string, raw []byte) (BankSeed, error) {\n\treturn parseBankSeed(name, raw, false)",
|
||
"replace": "func ParseBankSeed(name string, raw []byte) (BankSeed, error) {\n\treturn parseBankSeed(name, raw, true)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-classifier-budget-only-when-it-runs",
|
||
"why": "acceptance V2-5, and it is a class rather than a slip: `classify_types` gates the classifier PHASE, and the loader requires its budget be non-zero ONLY when that toggle is on — so charging the budget unconditionally bills a book for a pass that cannot happen ($1.00 of $2.00 on the shipped arm). ⚠ THE TEST OF THIS FUNCTION PINNED THE WRONG NUMBER: a gate defending the defect, which no review that reads green can catch",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\tif g.Terminology.ClassifyTypes {\n\t\tusd += g.Terminology.ClassifyBudgetUSD\n\t}",
|
||
"replace": "\tusd += g.Terminology.ClassifyBudgetUSD"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-input-identity-is-stated-not-counted",
|
||
"why": "acceptance V2-8: two token counts can coincide without being the same text — a pair whose fertility sits near 1.0 makes a draft the size of its source — and then a later stage that DOES receive the source stops being charged for it. Identity is a fact the caller has and the arithmetic does not",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/priceprojection.go",
|
||
"find": "\tif sp.carriesSource && !inputIsSource {",
|
||
"replace": "\tif sp.carriesSource && inputTokens != sourceTokens {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-competition-order-loses-a-chapter",
|
||
"why": "REPLACES DF-declared-means-every-boundary, whose target (the V2-7 predicate) the chapter-structure pack deliberately deleted: with competing paths a stray form feed can no longer mis-label a cut, because there is no mixed case left to judge. The property that took its place is the ORDER: neutralise form feeds on a copy, detect on the copy. Detect first and a header sharing a line with the previous chapter's prose is invisible — the chapter vanishes while the label stays correct, so a test checking only Structure passes over the loss",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/ingest.go",
|
||
"find": "\tworking := strings.ReplaceAll(decoded, chapterSep, \"\\n\\n\")",
|
||
"replace": "\tworking := decoded"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-book-level-figure-is-a-forecast",
|
||
"why": "acceptance F3: the book-level part of the price is a BOUND the run will not exceed, and calling it money `paid once` in the same breath as the expected total makes a short book read as dearer per chapter than a long one — a wrong conclusion drawn from our sentence rather than from the number",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "fmt.Fprintf(w, \"expected cost: $%.6f for the whole book (%d source chars)\\n\", p.ExpectedUSD, p.SourceChars)",
|
||
"replace": "fmt.Fprintf(w, \"expected cost: $%.6f for the whole book (%d source chars; $%.6f of it is book-level and paid once)\\n\", p.ExpectedUSD, p.SourceChars, p.BookOnceUSD)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-no-book-level-spend-says-nothing",
|
||
"why": "acceptance F3, the other half: a configuration with no terminology phase prices no book-level spend, and printing a $0.000000 ceiling invents a bound on passes that never run",
|
||
"package": "./cmd/tmctl/",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\t\tif p.BookOnceUSD > 0 {",
|
||
"replace": "\t\tif true {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "DF-a-dropped-row-is-not-silent",
|
||
"why": "the authorship split trades a dead paid run for a dropped row — and a drop nobody can read is the worse half of that trade: a mined term vanishes from every request and an operator asking why has nothing to answer with",
|
||
"package": "./internal/membank/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "\t\t\t\tbs.Dropped = append(bs.Dropped, t.Src)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "EPUB-preamble-joins-chapter-one",
|
||
"why": "a spine document the table of contents does not mention (a title page, a foreword) must join chapter ONE, the mirror of the txt preamble rule. Dropping it loses book text silently: the chapter count stays right and the provenance stays right, only the words are gone (D39.205)",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/epubtoc.go",
|
||
"find": "\t\t\tpre = append(pre, i) // held back",
|
||
"replace": "\t\t\t_ = i // held back"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "EPUB-service-pages-are-exactly-two-roles",
|
||
"why": "only the navigation document and the roles the book declares as toc/cover are excluded from the reading. Widening the rule deletes real chapters; narrowing it glues a text table of contents onto chapter one, which is the mass EPUB 2 case",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/epubtoc.go",
|
||
"find": "\tcase \"toc\", \"cover\":\n\t\treturn true",
|
||
"replace": "\tcase \"toc\":\n\t\treturn true"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-raw-follows-the-kept-index",
|
||
"why": "numbering is dense, so a chapter emptied by stripHeading takes no number. Selecting source titles by ordinal instead of by the chunker's own kept indices hands chapter N's title to chapter N+1, and nothing looks wrong because every chapter still has a title",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\tif i < len(doc.Titles) {\n\t\t\ttitle = doc.Titles[i]\n\t\t}",
|
||
"replace": "\t\tif len(out.titles) < len(doc.Titles) {\n\t\t\ttitle = doc.Titles[len(out.titles)]\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "EPUB-collapsed-targets-downgrade-the-provenance",
|
||
"why": "a book packing several chapters into ONE document with anchors declares more chapters than this package hands back, because it groups whole documents. Reporting that cut as `declared` puts the engine's own coarser answer out under the format's name — the same lie the spine used to tell, in a new place. The four-value vocabulary already has the true word for it: delimited",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/ingest.go",
|
||
"find": "\tif toc.collapsed > 0 {",
|
||
"replace": "\tif false && toc.collapsed > 0 {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "EPUB-service-role-at-a-point-keeps-the-document",
|
||
"why": "`<reference type=\"toc\" href=\"c2.xhtml#pos\"/>` says the table of contents BEGINS inside a document, not that the whole file is one. Excluding the file on that basis deletes whatever prose shares it, while the chapter count stays plausible and the provenance stays a confident `declared`. The form is in the owner's Kristoff EPUB today",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/ingest.go",
|
||
"find": "\t\tcase insideDoc:\n\t\t\tserviceInsideDoc++",
|
||
"replace": "\t\tcase insideDoc:\n\t\t\tservice[h] = \"toc\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "INGEST-every-count-reaches-the-operator",
|
||
"why": "the counts ARE the alarm this work ships instead of a validator, so one that reaches nobody is the mechanism missing rather than a small omission. Measured before the fix: 126 documents folded into 6 chapters printed zero bytes to stderr",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/ingest.go",
|
||
"find": "\tadd(\"documents_attached\", d.DocumentsAttached)",
|
||
"replace": "\t_ = d.DocumentsAttached"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "EPUB-unresolved-counts-only-what-it-promises",
|
||
"why": "counting a target that resolved fine but landed on a service or non-linear document as `unresolved` makes every ordinary EPUB 2 report a broken table of contents. An alarm that fires on the normal case is one nobody reads — and it dies by being ignored, not by being switched off",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/epubtoc.go",
|
||
"find": "\t\tif docs[i].service || !docs[i].linear {\n\t\t\tcontinue",
|
||
"replace": "\t\tif docs[i].service || !docs[i].linear {\n\t\t\tout.unresolved++\n\t\t\tcontinue"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MANIFEST-key-carries-the-structure-grammar",
|
||
"why": "the key decides whether a stored manifest is still current. Leave the grammar out and a sidecar written under a DIFFERENT grammar keeps validating, handing a reader a chapter tree whose boundaries the engine no longer draws",
|
||
"package": "./internal/pipeline/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\tShippingWave: r.shippingWaveTag(), Structure: r.structure.Fingerprint(),",
|
||
"replace": "\t\tShippingWave: r.shippingWaveTag(), Structure: \"\","
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-raw-epub-half-comes-from-the-label",
|
||
"why": "the EPUB half of title_raw had no test at all: removing label collection entirely left every chapter with an empty title and the battery green",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/epubtoc.go",
|
||
"find": "\t\t\t\t\t\tpending = href // the FIRST entry naming a document supplies its title",
|
||
"replace": "\t\t\t\t\t\t_ = href // the FIRST entry naming a document supplies its title"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "EPUB-conservation-no-document-leaves-unaccounted",
|
||
"why": "the counts this reader keeps are observability; this is the guarantee. Every readable spine document is either excluded on purpose or lands in exactly one chapter — a document that quietly leaves the reading takes book text with it while the chapter total stays plausible and the provenance stays a confident `declared`. ⚠ It does NOT subsume a WRONG exclusion: an excluded document is accounted for, so the fragment defect (M-1) is caught by its own tests, not by this",
|
||
"package": "./internal/chunk/",
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/epubtoc.go",
|
||
"find": "\t\tdefault:\n\t\t\tlast := &groups[len(groups)-1]\n\t\t\tlast.docs = append(last.docs, i)\n\t\t\tattached++",
|
||
"replace": "\t\tdefault:\n\t\t\tattached++"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-heading-belongs-to-the-chapter-not-the-edit-unit",
|
||
"why": "a chapter's title is restored on the CHAPTER's first chunk; an edit unit is a GROUP of chunks, so restoring it on each unit's first chunk prints «Глава 1» again in the middle of chapter 1 — in the very artifact `tmctl export` hands the platform. The two positions coincide on any chapter whose chunks share one unit, which is why the fixture pinning this asserts ≥2 units per chapter",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\t\t\tif ch.ChunkIdx == 0 {\n\t\t\t\t\tch.Heading = c.Heading // only a chapter's first chunk carries the title (chunker.go)\n\t\t\t\t}",
|
||
"replace": "\t\t\t\tif i == 0 {\n\t\t\t\t\tch.Heading = c.Heading // only a chapter's first chunk carries the title (chunker.go)\n\t\t\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-manifest-restores-the-heading-at-all",
|
||
"why": "the read path rebuilds chunks from the sidecar, and the title is the one field it must re-derive rather than read back. Drop the restore and every chapter arrives titleless: bookbuild then substitutes the chapter NUMBER, and the finished book ships «1», «2» where its chapter names belong",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\t\t\tif ch.ChunkIdx == 0 {\n\t\t\t\t\tch.Heading = c.Heading // only a chapter's first chunk carries the title (chunker.go)\n\t\t\t\t}",
|
||
"replace": "\t\t\t\tif false {\n\t\t\t\t\tch.Heading = c.Heading // only a chapter's first chunk carries the title (chunker.go)\n\t\t\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-export-reads-the-units-leader",
|
||
"why": "the export takes a unit's title from its LEADER, because only a chapter-opening chunk carries one. Read the last member instead and the title becomes \"\" on every multi-chunk unit: the export ships chapters with no name and ApplyHeading prepends nothing — a loss invisible to every assertion that lives on the cut",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/export.go",
|
||
"find": "\tce.FinalText = chunk.ApplyHeading(u.Members[0].Heading, ce.FinalText)\n\tce.Heading = u.Members[0].Heading // the same literal, given apart (see the field)",
|
||
"replace": "\tce.FinalText = chunk.ApplyHeading(u.Members[len(u.Members)-1].Heading, ce.FinalText)\n\tce.Heading = u.Members[len(u.Members)-1].Heading // the same literal, given apart (see the field)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-cut-puts-the-heading-on-the-chapters-first-chunk",
|
||
"why": "the chunker is where a chapter's title is PLACED, and everything downstream reads that placement by position. Put it on the chapter's last chunk instead and the manifest, which restores by position, hands the reader a title the cut never put there — while the chapter's opening unit ships nameless",
|
||
"package": "./internal/chunk/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/chunker.go",
|
||
"find": "\t\t\tchapterChunks[0].Heading = headingText // the chapter's first chunk carries the deterministic title",
|
||
"replace": "\t\t\tchapterChunks[len(chapterChunks)-1].Heading = headingText // the chapter's first chunk carries the deterministic title"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "ORDER-the-bank-is-published-after-it-is-seeded",
|
||
"why": "the run-start read-out is the artifact a reader gets when a run dies before its first wave, and it is written from the STORE. Publish it before seedGlossary materialises the bank and it shows the PREVIOUS run's rows — on a first run, nothing at all — while every later boundary rewrites the file correctly, so the battery stays green and only a reader who opened the file at that moment ever sees it. bookrun.go carried 0 of the catalogue's entries before this one",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bookrun.go",
|
||
"find": "\tif err := r.seedGlossary(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\t// The bank read-out (backlog row 125), at the FIRST boundary where a bank exists. Doing it here rather\n\t// than only at the stops means a book that never mines anything — or a run that dies before the first\n\t// wave — still leaves a readable bank behind, which is the state a reader has to be able to see.\n\tr.exportBank(ctx, \"run-start/seeded\")",
|
||
"replace": "\t// The bank read-out (backlog row 125), at the FIRST boundary where a bank exists. Doing it here rather\n\t// than only at the stops means a book that never mines anything — or a run that dies before the first\n\t// wave — still leaves a readable bank behind, which is the state a reader has to be able to see.\n\tr.exportBank(ctx, \"run-start/seeded\")\n\tif err := r.seedGlossary(ctx); err != nil {\n\t\treturn nil, err\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "ORDER-the-stop-holds-its-rows-before-it-publishes-them",
|
||
"why": "the signature stop refreshes the read-out so the signing screen sees the state as of THIS stop. The rows it presents reach the document through r.lastBankStopRows, so publishing before the assignment ships a document with an empty proposed section — the row-224 defect exactly, restored by a reordering of two adjacent lines",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\tr.lastBankStopRows = rows\n\t// The signature stop is the boundary the signing screen reads at (row 125): refresh the bank read-out\n\t// so the state behind the decisions is the state as of this stop, not as of the last run.\n\tr.exportBank(ctx, \"bank-mining/signature-stop\")",
|
||
"replace": "\t// The signature stop is the boundary the signing screen reads at (row 125): refresh the bank read-out\n\t// so the state behind the decisions is the state as of this stop, not as of the last run.\n\tr.exportBank(ctx, \"bank-mining/signature-stop\")\n\tr.lastBankStopRows = rows"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-proposals-reach-the-read-out",
|
||
"why": "row 224: the stop refreshes the bank read-out precisely so the signing screen has something to read, and the bank half of that document is empty of mined rows by construction (they are written in the auto-continue branch). Drop the proposed section and the screen is back to reading «total 0, signed 0» beside a stop opened for 82 surfaces",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t// The stop's rows ride along (row 224). r.lastBankStopRows is set in the stopping branch and nowhere\n\t// else, and a stopped run returns before every later boundary, so this is non-empty exactly at a\n\t// signature stop.\n\texp.Proposed = projectBankProposals(r.lastBankStopRows)",
|
||
"replace": "\t// The stop's rows ride along (row 224). r.lastBankStopRows is set in the stopping branch and nowhere\n\t// else, and a stopped run returns before every later boundary, so this is non-empty exactly at a\n\t// signature stop."
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "HEADING-ingest-and-chunker-answer-the-same-on-an-ordinary-header",
|
||
"why": "the chapter heading rule is spread over three predicates in two files and each half was pinned only by its own tests, which cannot see a DISAGREEMENT between them. Shrink the ingest's length guard and ordinary headers stop being cut as chapters while the chunker still reads them as headers — the book arrives as one chapter with its markers inside the prose, and every existing chunk test still passes because none of them ingests",
|
||
"package": "./internal/chunk/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/chunk/ingest.go",
|
||
"find": "const chapterHeaderMaxRunes = 60",
|
||
"replace": "const chapterHeaderMaxRunes = 6"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CLASSVOCAB-a-class-the-engine-keeps-must-be-named-in-the-pair-prompt",
|
||
"why": "the classifier's answer vocabulary is closed in Go and explained to the model ONLY by the pair's prompt, and an off-vocabulary reply is counted and dropped rather than refused. Add a class the engine will keep and no shipping prompt names it: the pass is paid for, the class never comes back, and nothing anywhere says so. ⚠ The pin lives in internal/pipeline (the canonical prompt form is implemented there) while the edit is in internal/terminology — the entry names the package of the PIN",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/terminology/classify.go",
|
||
"find": "var Types = map[string]bool{\"name\": true, \"place\": true, \"title\": true, \"term\": true}",
|
||
"replace": "var Types = map[string]bool{\"name\": true, \"place\": true, \"title\": true, \"term\": true, \"artifact\": true}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-proposals-belong-to-a-stop-and-to-no-other-boundary",
|
||
"why": "the read-out's `proposed` section means «these rows are waiting for a decision RIGHT NOW». It is safe to read that way only because r.lastBankStopRows is set in the stopping branch and nowhere else — a property held by one assignment and, until this entry, by prose. Set it in the auto-continue branch and every later boundary asks the signing screen to decide rows the run already banked unsigned",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\t\tr.exportBank(ctx, \"bank-mining/auto-continue\")",
|
||
"replace": "\t\tr.lastBankStopRows = rows\n\t\tr.exportBank(ctx, \"bank-mining/auto-continue\")"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-the-number-in-a-title-is-the-headers-not-the-ordinals",
|
||
"why": "a chapter carries TWO numbers — its ordinal (the key chunk_status is addressed by) and the number written in its own header — and they part company on any book whose chapters are not numbered consecutively, which is every book with a prologue or a skipped section. Re-deriving the stored title from the ordinal is silent on a consecutive fixture and moves the title on almost every chapter of a real one",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\t\t\tif ch.ChunkIdx == 0 {\n\t\t\t\t\tch.Heading = c.Heading // only a chapter's first chunk carries the title (chunker.go)\n\t\t\t\t}",
|
||
"replace": "\t\t\t\tif ch.ChunkIdx == 0 && c.Heading != \"\" {\n\t\t\t\t\tch.Heading = \"Глава \" + strconv.Itoa(c.Number)\n\t\t\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-the-built-book-carries-names-not-numbers",
|
||
"why": "the symptom this whole seam is named for, and the last hop of it: assembleBook substitutes the chapter NUMBER when the export carries no title, so a title lost anywhere upstream ships as «1», «2» in the finished book. Three positions above this are pinned; before this entry the hop a reader actually sees was prose",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/export.go",
|
||
"find": "\tce.Heading = u.Members[0].Heading // the same literal, given apart (see the field)",
|
||
"replace": "\tce.Heading = \"\" // the same literal, given apart (see the field)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TITLE-the-glued-heading-is-stripped-from-the-body",
|
||
"why": "the export GLUES the title onto the unit's text and the book writer prints it again as the chapter heading, so assembleBook strips the known prefix. Leave it on and every chapter of the finished book opens with its own title twice — no assertion downstream of the export could see it, because the export is byte-correct",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bookbuild.go",
|
||
"find": "\t\t\ttext = strings.TrimPrefix(text, ce.Heading+\"\\n\\n\")",
|
||
"replace": "\t\t\t_ = text"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "ARM-an-arm-is-the-production-config-with-a-different-editor",
|
||
"why": "an editor swap-arm exists to isolate ONE variable, and three arm headers claimed exactly that while differing from production by two dozen keys — the bank contour among them — because the test that called itself the guard compared three fields by name. The planting drops a key the LOADER does not care about (a cache ttl), so only the whole-config comparison can see it: a key that quietly differs is a second variable the arm is isolating without saying so. ⚠ The catalogue had no entry in ./internal/config/ at all before this one",
|
||
"package": "./internal/config/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "configs/pipeline-arm-glm.yaml",
|
||
"find": " cache_ttl: \"5m\"\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-the-sheet-carries-the-rows-own-frequency",
|
||
"why": "the frequency beside a proposal is what an owner weighs the term by, and it is the row's own count or it is nothing. Before 08.09 the parity assertion checked Freq only for `!= 0`, so a tenfold rescale in the fold published 120 occurrences for a term seen 12 times and left the package green",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\t\tFreq: row.Freq, Spread: row.Spread, Conf: row.Conf, Invented: row.Invented,",
|
||
"replace": "\t\t\tFreq: row.Freq * 10, Spread: row.Spread, Conf: row.Conf, Invented: row.Invented,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-kind-and-channel-are-not-exchangeable",
|
||
"why": "the fold RENAMES both (Type->Kind, Origin->Channel) and both are strings, so an exchange compiles and ships a sheet whose every row says its kind is `banknote` and its channel is `name`. Renamed neighbours of one type are the pair a hand-written comparison forgets",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\t\tSrc: row.Src, Dst: row.Dst, Kind: row.Type, Channel: row.Origin,",
|
||
"replace": "\t\t\tSrc: row.Src, Dst: row.Dst, Kind: row.Origin, Channel: row.Type,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-the-two-lists-answer-different-questions",
|
||
"why": "`contradicts` is what THIS RUN breaks, `bank_holds` is what the book already calls it — separate fields because they are separate decisions (mining.go says so where it fills them). Both are []string, so an exchange compiles; it is invisible against any fixture where both are empty, which is what the stop's fixture gave until its seed carried an unsigned row",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\t\tContradicts: row.Contradicts, BankHolds: row.BankHolds,",
|
||
"replace": "\t\t\tContradicts: row.BankHolds, BankHolds: row.Contradicts,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-an-invented-rendering-is-published-as-invented",
|
||
"why": "`invented` says no draft proposed this rendering — the class the type's own comment calls the one to read first. Forced false it is simply absent from the sidecar (omitempty), and the signing screen loses the flag that tells an owner to look",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\t\tFreq: row.Freq, Spread: row.Spread, Conf: row.Conf, Invented: row.Invented,",
|
||
"replace": "\t\t\tFreq: row.Freq, Spread: row.Spread, Conf: row.Conf, Invented: false,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "ARM-the-one-exemption-is-asserted-unused-not-merely-logged",
|
||
"why": "reasoning_max_tokens leaves the arm comparison only where the loader forces the value (pipeline.go:771, additive billing). Nothing runs an additive editor today, so the exemption never fires and its correctness is never exercised; widening the predicate makes it fire against every config, and before 08.09 the only trace of that would have been a log line on a green run",
|
||
"package": "./internal/config/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/armparity_test.go",
|
||
"find": "\treturn m.providerReasoning(model) == \"additive\"",
|
||
"replace": "\treturn m.providerReasoning(model) != \"\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-the-sheets-order-is-the-stops-ranking",
|
||
"why": "the section's own contract says its order IS the stop's ranking, so which row an owner reads first is information rather than layout. Nothing asserted it until 08.09: the fold's one-row fixture made order unobservable by construction, and every other assertion found its row by Src instead of by position, so a reversal shipped a sheet ranked backwards and left the package green",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\tout = append(out, p)\n\t}\n\treturn out\n}",
|
||
"replace": "\t\tout = append(out, p)\n\t}\n\tfor i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {\n\t\tout[i], out[j] = out[j], out[i]\n\t}\n\treturn out\n}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSTOP-nothing-consolidated-is-published-as-nothing",
|
||
"why": "Dst is \"\" when the role consolidated nothing — the field's comment says so in as many words — and that empty must reach the sheet. Substituting the source surface publishes 青茅山 -> 青茅山, a decision where none was made, in the one place whose whole purpose is to show what has yet to be decided. Invisible against any fixture whose rows all carry a rendering, which was every fixture in this package until 08.09",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\tout = append(out, p)",
|
||
"replace": "\t\tif p.Dst == \"\" {\n\t\t\tp.Dst = row.Src\n\t\t}\n\t\tout = append(out, p)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKTRUTH-a-drop-names-its-holders-signature",
|
||
"why": "the drop an auto-bank key collision leaves calls its holder 'the signed' whatever the holder's status, and the operator signs a bank on the strength of that line. Every fixture in the package held the key with an APPROVED row, so the word was true of all of them and the lie was invisible",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestADropNamesItsHoldersSignature",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "membank.StatusLabel(prior.Status)",
|
||
"replace": "\"signed\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKTRUTH-the-drop-headline-promises-a-signature",
|
||
"why": "the headline over the dropped rows promises 'the signed term wins' over a set of drops whose holders may every one of them be unsigned. It is the line an operator reads first and often the only one, because the per-row detail is a semicolon-joined tail",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheDropHeadlineDoesNotPromiseASignature|TestEveryOperatorMessageIsCatalogued",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankmaterialize.go",
|
||
"find": "in.remark(\"auto-bank rows dropped: their key is already held by a row gathered earlier (the holder wins, whatever its status; each drop below names the holder's signature)\",",
|
||
"replace": "in.remark(\"auto-bank rows dropped: their key is already held by a signed term (the signed term wins)\","
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKTRUTH-a-sticky-carry-counted-as-judged",
|
||
"why": "the unsigned-wire denominator counts rows the chunk can be JUDGED on, which is narrower than the rows the model was shown: a sticky carry is in the injection block but its src is in the previous chunk. Counting it makes every pronominal chunk read as a channel nobody follows, and the follow RATIO then means something other than what four doc comments said it meant",
|
||
"package": "./internal/membank/",
|
||
"run": "TestTheUnverifiedDenominatorIsNotACountOfRowsShown",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/mempostcheck.go",
|
||
"find": "if p.Sticky { // sticky context is not expected in the output",
|
||
"replace": "if false { // sticky context is not expected in the output"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "OPMSG-an-operator-message-reworded-off-the-catalogue",
|
||
"why": "an operator warning is a sentence somebody acts on, and the ones that rot are the ones nothing else reads: a message that STOPS a run is held by the test asserting the stop, a warning by nobody. The catalogue is what makes a wording change a reviewed diff instead of a silent one; this planting is the ordinary way it is defeated",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryOperatorMessageIsCatalogued",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/seeding.go",
|
||
"find": "r.Log.WarnContext(ctx, \"voice profile windows leave chapters uncovered (deliberate is fine; a typo is not)\",",
|
||
"replace": "r.Log.WarnContext(ctx, \"voice profile windows leave chapters uncovered\","
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SEAM-confidence-keyed-by-the-raw-surface",
|
||
"why": "the role's stated confidence is looked up by the candidate KEY. Keyed by the raw surface it silently takes the confidence of whichever candidate happens to key as that surface, or none — and every fixture in the module used simplified spellings, where key and surface are the same string and the choice cannot be wrong",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryStopSheetFindingReachesTheRowItBelongsTo|TestTheStopSheetJoinIsNotFooledByTheRawSurface",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "if v, ok := t.Conf[c.Key]; ok {",
|
||
"replace": "if v, ok := t.Conf[c.Src]; ok {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SEAM-self-contradiction-matched-by-the-raw-surface",
|
||
"why": "the run's own self-contradiction is matched to its sheet row by the candidate KEY. Matched by the raw surface, every traditional or katakana spelling loses its mark — the population the fold exists for, and the one no fixture had",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryStopSheetFindingReachesTheRowItBelongsTo",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "if cf.Key == c.Key {",
|
||
"replace": "if cf.Key == c.Src {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SEAM-bank-hold-matched-by-the-raw-surface",
|
||
"why": "the bank's existing rendering is matched to its sheet row by the candidate KEY, because that is the source consolidatedRows gives the proposal. Matched by the raw surface, a banknote candidate's mark goes to another row or nowhere",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryStopSheetFindingReachesTheRowItBelongsTo|TestTheStopSheetJoinIsNotFooledByTheRawSurface",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "if cf.Src == c.Key {",
|
||
"replace": "if cf.Src == c.Src {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SEAM-the-conflict-carries-the-raw-surface-as-its-key",
|
||
"why": "a consolidation conflict is FOUND by comparing candidate keys, so the key is what it must carry back. Carrying the raw surface instead re-introduces the two-sided key choice the ungrouped carrier was built to end, one file away from where it is read",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryStopSheetFindingReachesTheRowItBelongsTo",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/terminology/terminology.go",
|
||
"find": "out = append(out, ConsolidationConflict{Key: c.Key, Src: c.Src",
|
||
"replace": "out = append(out, ConsolidationConflict{Key: c.Src, Src: c.Src"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "STATUSWRITE-the-pre-decided-skip-claims-the-other-writers-cause",
|
||
"why": "two different writers put a `skipped` row in chunk_status and which one fires is decided by the wave layout, not by anything the row records. The DETAIL each writes is the only durable trace of which path produced it — and the author of the pack that documented this named the wrong mechanism three times running",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestASkippedRowSaysWHICHWriterWroteIt",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "detail := fmt.Sprintf(\"skipped: a member draft chunk of this edit unit was flagged (%s)\", flagReason)",
|
||
"replace": "detail := fmt.Sprintf(\"skipped: an upstream stage was flagged (%s)\", flagReason)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "STATUSWRITE-the-multi-stage-skip-claims-the-other-writers-cause",
|
||
"why": "the mirror of the entry above, on the writer that no fixture in the package reached until 08.09: breaking it survived the WHOLE package while breaking its sibling turned six tests red, so half of what the doc asserted had no witness at all",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestASkippedRowSaysWHICHWriterWroteIt",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "detail := fmt.Sprintf(\"skipped: an upstream stage was flagged (%s)\", flagReason)",
|
||
"replace": "detail := fmt.Sprintf(\"skipped: a member draft chunk of this edit unit was flagged (%s)\", flagReason)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSHEET-the-tuple-skip-swallows-a-seed-rows-disagreement",
|
||
"why": "a proposal sharing a bank row's UNIQUE tuple is skipped only where it can LAND on that tuple. Over a row the owner wrote it never lands — the emission drops it — so the bank keeps its rendering and the disagreement is permanent. Unconditioned, the skip hides the commonest shape there is: a hand-written seed row takes the default window and no sense, and so does a banknote proposal",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheCommonestSeedShapeStillReachesTheSigningSheet",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "if IsEngineUnsigned(b) && b.Src == c.Src && b.Sense == c.Sense",
|
||
"replace": "if b.Src == c.Src && b.Sense == c.Sense"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSHEET-the-one-carrier-of-engine-unsigned-inverted",
|
||
"why": "three places have to agree on what counts as the ENGINE's own unsigned row — the emission's seed-surface guard, the paid path's filter and the bank's tuple skip — and they agree by sharing one predicate. Inverting it is what a second spelling of the rule would eventually amount to",
|
||
"package": "./internal/membank/",
|
||
"run": "TestConsolidationKeyConflicts|TestConsolidationTupleSkipMirrorsTheStoreKey",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/membank/memseed.go",
|
||
"find": "return e.Source == \"mined\" && e.Status != \"approved\"",
|
||
"replace": "return e.Source == \"mined\" && e.Status == \"approved\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-a-settled-candidate-is-paid-for-anyway",
|
||
"why": "the role and the classifier were being paid for surfaces the bank already settles and the emission then throws away. The filter is the saving; without it every counter in the report still reads clean, because nothing downstream can tell a question that was asked from one that was not",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABankSettledCandidateIsNotPaidFor",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tif bankSettles(settled, c) {",
|
||
"replace": "\t\tif false && bankSettles(settled, c) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-filter-skips-a-candidate-whose-drafts-disagree",
|
||
"why": "the role is asked about banked surfaces ON PURPOSE: its answer is the mark that tells the owner the book already calls the term something else. Measured on the corpus, 366 of the 439 candidates the owner's seed holds had a draft that disagreed — so a filter that drops the disagreement half buys its saving by silencing them, and the sheet still looks complete",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestADisagreeingBankedCandidateIsStillPaidFor",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tif text.NormalizeTargetForm(v.Dst) != want {",
|
||
"replace": "\t\tif false && text.NormalizeTargetForm(v.Dst) != want {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-filter-reads-a-bank-that-moves-between-runs",
|
||
"why": "the filter reads the SEED surfaces, not the whole bank. Reading the whole bank folds in the engine's own auto-bank rows, which this very run rewrites — so the candidate set differs between run one and run two and the bank moves with it. Measured consequence is bigger than the pass: the second run refuses at the edit wave with «what moved: memory_version», i.e. the already-paid EDIT checkpoints are invalidated and the run demands --resnapshot. ⚠ Only an AUTO-CONTINUING pair of runs can see it: a run that stops at the bank boundary writes no auto-bank, and the first version of this pin stopped and let the planting through",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAResumeFindsItsCheckpointsAfterTheFilter",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "settled := bankSettledSurfaces(unsignedEngineSurfaces(r.glossaryRows()))",
|
||
"replace": "settled := bankSettledSurfaces(r.glossaryRows())"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-filter-copies-the-candidates-and-loses-their-mutations",
|
||
"why": "the classifier and the scorer MUTATE candidates in place, and the caller renders the sheet and the delta from the slice it passed in. Applying them to the filtered COPY instead leaves the sheet carrying heuristic types and stale rankings with every counter reading clean — the defect the filter's own first implementation had, invisible to every fixture in the package because they all run with the classifier off",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheClassifiersTypeStillReachesTheSheetThroughTheFilter",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tres.Reclassified = applyTypes(cands, classified)",
|
||
"replace": "\t\tres.Reclassified = applyTypes(paid, classified)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-rescoring-is-applied-to-the-filtered-copy",
|
||
"why": "applyTypes and ScoreVariants are TWO in-place mutations of the candidates, and the caller renders the sheet from the slice it passed in. Pinning one leaves the other free: applied to the filtered copy, the re-scoring never reaches the sheet, and the row keeps a `conform` ranking factor the classification has just retired — a reason printed to the operator that is no longer true of the term. Measured 09.09: correct code gives no signals on that row, the planting gives [conform], and the whole package stays green",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheClassifiersTypeStillReachesTheSheetThroughTheFilter",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tfor i := range cands {\n\t\t\tterminology.ScoreVariants(&cands[i], opts)\n\t\t}",
|
||
"replace": "\t\tfor i := range paid {\n\t\t\tterminology.ScoreVariants(&paid[i], opts)\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-one-time-cost-warning-reads-this-runs-own-checkpoints",
|
||
"why": "the warning about a one-time re-consolidation needs BOTH halves and each is knowable at only one moment: that the book had paid BEFORE this run, and that this run paid anyway. Asked after the passes, the probe sees the checkpoints THIS run just settled, so a fresh book paying for the first time is told it paid twice. That is the second viton of one bug — the version before it warned on every ordinary resume — and each shipped because its test asserted only silence, in a fixture where the firing branch was unreachable. ⚠ Two edits, and the second is not decoration: replacing the condition alone leaves paidBefore unused and the package does not compile, so the planting would report «nothing ran» — a mutation that cannot build is not a mutation that survived, but it is not a caught one either",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheOneTimeCostIsAnnouncedExactlyWhenItHappens",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "if res.BankSettled > 0 && run.fresh && paidBefore {",
|
||
"replace": "if paidNow, _ := r.Store.HasCheckpointForStage(r.Book.BookID, terminologyStageName); res.BankSettled > 0 && run.fresh && paidNow {"
|
||
},
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\tpaidBefore, perr := r.Store.HasCheckpointForStage(r.Book.BookID, terminologyStageName)",
|
||
"replace": "\tpaidBefore, perr := r.Store.HasCheckpointForStage(r.Book.BookID, terminologyStageName)\n\t_ = paidBefore"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-filter-swallows-a-candidate-the-emission-would-have-emitted",
|
||
"why": "the drop is output-equivalent for the delta only because the filtered population is EXACTLY the one reverseSectionTerms drops: banknote-only candidates on a seed surface. Widen it to a mined candidate and the filter removes a term that would have reached the artifact the owner signs — money saved by losing a row. Every fixture that reached this filter carried a banknote candidate, so the condition was true of them by accident",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheSettledTestAnswersOnEachOfItsConditions",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\tif c.Origin != terminology.OriginBanknote || len(c.Variants) == 0 {",
|
||
"replace": "\tif len(c.Variants) == 0 {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-filter-does-not-read-the-seed-rows-aliases",
|
||
"why": "the emission's own guard indexes a seed row by its src AND its aliases, so a proposal named by an alias is dropped there. Indexing only the src here pays the role for exactly those proposals and then throws the answer away — the waste this filter exists to end, surviving in the population hardest to notice",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheSettledTestAnswersOnEachOfItsConditions",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tfor _, a := range e.Aliases {\n\t\t\tout[text.NormalizeSourceKey(a.Alias)] = e\n\t\t}",
|
||
"replace": "\t\t_ = e.Aliases"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-corrected-type-never-reaches-the-role",
|
||
"why": "the classifier is BOUGHT for the type field and that field travels to the role for the whole batch. The paid subset is rebuilt from the candidates AFTER the in-place passes precisely so the request carries the corrected type; drop the rebuild and the money is spent, the run prints reclassified=N, and the wire still says what the heuristic guessed. Third form of one root — a subset held as its own array — and the first two were both found after their own tests went green",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheClassifiersTypeStillReachesTheSheetThroughTheFilter",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tpaid = pickCandidates(cands, paidIdx)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-filter-does-not-fold-the-seed-surface",
|
||
"why": "the seed surfaces are indexed by their NORMALIZED key because that is what a candidate carries. Indexing the raw src instead makes the filter silently stop firing for every traditional or katakana spelling — and BankSettled then reads 0, which is indistinguishable from «there was nothing to skip»",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheFilterFoldsBothSidesTheWayProductionDoes",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tout[text.NormalizeSourceKey(e.Src)] = e",
|
||
"replace": "\t\tout[e.Src] = e"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-agreement-is-compared-byte-wise",
|
||
"why": "whether a draft AGREES with the bank is the money decision itself, and it is judged under the same target-form fold the vote is counted with. Compared byte-wise, a rendering differing only by case or ё reads as a disagreement and is paid for — the waste this filter exists to end, restored in the population nobody looks at",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheFilterFoldsBothSidesTheWayProductionDoes",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\tif text.NormalizeTargetForm(v.Dst) != want {",
|
||
"replace": "\t\tif v.Dst != want {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-saving-loses-its-denominator",
|
||
"why": "the saving is the one line a later shift greps to learn what the filter bought, and «12 skipped» is unreadable without «of 300». The keys are pinned directly rather than by the operator-message catalogue, which guards texts and says arguments are outside it — so without this planting the denominator could go to zero unnoticed",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABankSettledCandidateIsNotPaidFor",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\"book\", r.Book.BookID, \"skipped\", dropped, \"of_candidates\", len(cands), \"still_paid\", len(keptIdx),",
|
||
"replace": "\"book\", r.Book.BookID, \"skipped\", dropped, \"of_candidates\", 0, \"still_paid\", 0,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "SEAM-the-normalizer-stops-folding-and-the-fixture-does-not-notice",
|
||
"why": "the stop-sheet fixture claims its candidates are the population whose KEY differs from its SURFACE, and it asks the real normalizer whether they are. Its first version asserted that against two typed literals — and the fold it named did not exist — so a normalizer replaced by identity would have left it green while every traditional spelling stopped keying at all",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryStopSheetFindingReachesTheRowItBelongsTo",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/text/norm.go",
|
||
"find": "\t\tif m, ok := trad2simp[r]; ok {\n\t\t\tr = m\n\t\t}",
|
||
"replace": "\t\t_ = trad2simp"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKSHEET-a-row-nobody-was-asked-about-reads-as-undecided",
|
||
"why": "an empty dst on the signing sheet is the same glyph for four different facts: the role declined, no reply line covered the term, the budget did not reach it, and — since the already-banked filter — nobody was asked because there is nothing to decide. Three of those mean UNDECIDED and one means SETTLED, and the review ranking files all four together, so without the mark the owner cannot tell the row he can skip from the rows he must read",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABankSettledCandidateIsNotPaidFor",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/mining.go",
|
||
"find": "\t\tif r.SettledByBank {\n\t\t\tb.WriteString(\" NOT ASKED(the bank already renders this surface and every draft agreed — nothing to decide)\")\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "MONEY-the-one-time-cost-warning-drops-its-fresh-half",
|
||
"why": "the warning needs BOTH halves — that the book had paid BEFORE this run and that this run paid anyway — and dropping the second turns it into a claim of re-purchase on every ordinary resume, printed beside cost_usd=0.000000. It survived once already: the guard against it grepped the message's PROSE, and the prose had been reworded, so the substring occurred zero times in the module and the assertion could not fire. Both sides now read one structured key instead",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAResumeFindsItsCheckpointsAfterTheFilter|TestTheOneTimeCostIsAnnouncedExactlyWhenItHappens",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "if res.BankSettled > 0 && run.fresh && paidBefore {",
|
||
"replace": "if res.BankSettled > 0 && paidBefore {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKMONEY-the-run-total-drops-the-classifier",
|
||
"why": "the run's TOTAL is built from the terminologist's RENDER cost alone, and the classifier settles on its own cost axis under its own budget — so the figure an operator reads while deciding whether to keep paying is short by the whole classify phase. Measured on the live run of 08.09: two thirds of the terminology phase's money never reached the total. The pin compares the total against the ledger's own sum, on a fixture where the two phases deliberately cost DIFFERENT amounts, because the fake model bills a fixed price per call and equal costs would let the wrong addend pass",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRunTotalIsThisRunsSpendIncludingTheClassifier",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\tres.TotalUSD += t.CostUSD + t.ClassifyCostUSD",
|
||
"replace": "\t\tres.TotalUSD += t.CostUSD"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKMONEY-the-two-bank-roles-share-one-position",
|
||
"why": "the bank roles are checkpointed under ONE synthetic stage at chapter 0 and number their batches in chunk_idx, so the classifier's batch 0 and the terminologist's batch 0 arrive at the same (chapter, chunk, stage) triple. Keyed on that triple alone the earlier of the two is filed as SUPERSEDED — money that bought nothing — and on the live run of 08.09 that was the classifier's $0.009013, the largest of the three ledger rows, reported to the operator as a loss while it was bought and used",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheLedgerSaysTheClassifierBoughtTheBank|TestTwoBankRolesInOneBatchAreTwoPositions",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/paidtail.go",
|
||
"find": "\tif u.Stage == terminologyStageName {\n\t\tp.role = u.Role\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKMONEY-the-role-column-leaves-the-ledger-query",
|
||
"why": "the decomposition can only tell the two bank roles apart if the role reaches it, and it reaches it through this SELECT. Dropping the column leaves every row's role empty, which is exactly the pre-fix collision with no visible cause — the struct still has the field and the code still reads it",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheLedgerSaysTheClassifierBoughtTheBank",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/store/ledger.go",
|
||
"find": "SELECT j.chapter, c.chunk_idx, c.stage, c.role, c.model_requested",
|
||
"replace": "SELECT j.chapter, c.chunk_idx, c.stage, '' AS role, c.model_requested"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKMONEY-the-role-enters-every-position",
|
||
"why": "the OTHER side of the same fix, and the one that would land a second money change unannounced. A repair call carries its stage's REAL name and its own role (repair.go), so folding the role into every position separates a repair from the stage call it repairs and quietly moves that money out of `superseded`. The role belongs to the bank stage alone, where the triple is not an address",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRoleSplitDoesNotMoveARepairsMoney",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/paidtail.go",
|
||
"find": "\tif u.Stage == terminologyStageName {\n\t\tp.role = u.Role\n\t}\n",
|
||
"replace": "\tp.role = u.Role\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-a-boundary-that-measured-nothing-claims-a-whole-bank",
|
||
"why": "the bank read-out is written at FIVE boundaries and the terminology pass runs at ONE, so a section published unconditionally answers «consolidated 0, unanswered 0» — which a consumer reads as «nothing is missing» — about a bank nobody looked at. This is the D39.202 class: the instrument answering its own question. The nil is the whole distinction",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABoundaryThatMeasuredNothingSaysNothing",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\tif t == nil {\n\t\treturn nil\n\t}\n\treturn &BankConsolidation{",
|
||
"replace": "\tif t == nil {\n\t\tt = &terminologyResult{}\n\t}\n\treturn &BankConsolidation{"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-a-classifier-cut-is-read-as-a-partial-bank",
|
||
"why": "the engine's own log warning fires on EITHER counter and said «PARTIALLY consolidated» on the run of 08.09, where the render pass was intact (batches_dropped=0) and only the classifier's budget cut a batch. Copying that rule into the projection makes a whole bank read as partial and sends an owner to re-buy renderings he already has — a false alarm built by the cure. The bank's completeness is the RENDER pass; the classifier's cut is the term TYPES and travels apart",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAClassifierCutIsNotAnIncompleteBank",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\tComplete: t.BatchesDropped == 0,",
|
||
"replace": "\t\tComplete: t.BatchesDropped == 0 && t.ClassifyBatchesDropped == 0,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-the-read-out-stops-carrying-the-completeness",
|
||
"why": "the counters exist and the projection exists, and the artifact the signing screen is built from still does not carry them unless this line runs — which is precisely the state row 253(б) describes: the engine knowing and the reader not seeing",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABankCutByABudgetSaysSoInTheReadOut",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\texp.Consolidation = projectBankConsolidation(r.lastTerminology)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-the-stop-screen-is-handed-no-completeness",
|
||
"why": "the CLI renders what the stop hands it, so dropping the field on the stop leaves the screen able to print the state and never given one — and the owner reads THIS surface first, before either sidecar",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheSignatureStopCarriesTheCompletenessToTheScreen",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\t\tConsolidation: projectBankConsolidation(r.lastTerminology),\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-the-never-asked-count-moves-under-the-stdout-cap",
|
||
"why": "a row the role was never asked about carries no rendering, so reviewRank sends it to 1000 — LAST — and the stdout table shows twenty rows: on any real book the whole class falls off the bottom. Leaving the per-row mark as the ONLY carrier makes the screen green on a narrow fixture and blind on a live one, which is the trap this surface was built around. ⚠ The first version of this entry swapped the summary and the table instead, and it SURVIVED — rightly: moving the line does not remove it, and a mutation that attacks nothing is a green record of a property nobody guards",
|
||
"package": "./cmd/tmctl/",
|
||
"run": "TestTheNeverAskedCountSurvivesTheStdoutCap",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\tif c.NeverAsked > 0 {\n\t\t// The saving, and it is stated because an empty rendering in the table below is the same glyph as\n\t\t// «the role declined» and «the budget did not reach it» — three facts, one of which is good news.\n\t\tfmt.Fprintf(w, \" %d term(s) were NOT ASKED about: the bank already renders those surfaces and every \"+\n\t\t\t\"draft agreed — nothing to decide, and they are marked in the table and the full sidecar.\\n\", c.NeverAsked)\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-the-whole-bank-says-nothing",
|
||
"why": "a screen silent when the bank is whole makes its own silence carry meaning, and no reader can tell that silence from the screen not knowing — while «did not measure» is a THIRD state here, not a shade of complete. A pin that only asserts the alarm sounds stays green on a surface that has stopped saying anything in the good case",
|
||
"package": "./cmd/tmctl/",
|
||
"run": "TestTheSigningScreenSaysHowCompleteTheBankIs",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\tcase c.Complete:\n\t\tfmt.Fprintf(w, \"Bank completeness: WHOLE — every render batch was bought (%d consolidated, %d declined, %d unanswered).\\n\",\n\t\t\tc.Consolidated, c.Declined, c.Unanswered)\n\tdefault:",
|
||
"replace": "\tcase c.Complete:\n\t\t_ = c.Consolidated\n\tdefault:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKMONEY-the-run-total-reports-the-contours-cumulative-spend",
|
||
"why": "CostUSD is what the render phase paid THIS run; CumUSD is what the contour has cost since the book began, replayed checkpoints included at what they cost then. On a FIRST run the two are equal to the cent, so a total built from the wrong one is invisible there — and on a resume it reports money nobody spent, beside a ledger that did not move. The pin's second leg is a resume precisely because the first cannot tell them apart",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheRunTotalIsThisRunsSpendIncludingTheClassifier",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/waverun.go",
|
||
"find": "\t\tres.TotalUSD += t.CostUSD + t.ClassifyCostUSD",
|
||
"replace": "\t\tres.TotalUSD += t.CumUSD + t.ClassifyCostUSD"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-the-signing-boundary-alone-publishes-nothing",
|
||
"why": "the read-out is refreshed at five boundaries and the signing screen is built from ONE of them — the signature stop. A section published everywhere EXCEPT there is green in every fixture that runs a book to completion, and absent in the only document an owner ever signs against. The pin reads the BYTES on disk at that boundary and checks as_of, so it cannot be satisfied by a later boundary's file",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheSigningBoundaryPublishesTheCompleteness",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\texp.Consolidation = projectBankConsolidation(r.lastTerminology)",
|
||
"replace": "\tif at != \"bank-mining/signature-stop\" {\n\t\texp.Consolidation = projectBankConsolidation(r.lastTerminology)\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-never-asked-is-fed-by-the-wrong-counter",
|
||
"why": "NeverAsked is the only counter of the six whose source is ZERO in every end-to-end fixture of this package, so a fold reading Declined into it stays green everywhere: the reflective guard only asks that a field be non-zero somewhere, and the screen's own fixtures build the struct by hand and never cross the seam. Distinct values per source are what make the wiring checkable at all",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEachConsolidationCounterComesFromItsOwnSource",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/bankexport.go",
|
||
"find": "\t\tNeverAsked: t.BankSettled,",
|
||
"replace": "\t\tNeverAsked: t.Declined,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-a-pass-that-asked-nothing-reports-bought-batches",
|
||
"why": "the already-banked filter can empty the paid set, and then NO batch was ever planned. «Every render batch was bought» is true of zero batches and reads as a report on work that happened — the D39.202 shape, committed by the surface built to remove it. Measured on a live fixture: the screen printed «WHOLE — every render batch was bought (0 consolidated, 0 declined, 0 unanswered)» about a pass that sent nothing and paid nothing",
|
||
"package": "./cmd/tmctl/",
|
||
"run": "TestTheSigningScreenSaysHowCompleteTheBankIs",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\tcase c.Complete && c.Consolidated+c.Declined+c.Unanswered == 0:",
|
||
"replace": "\tcase false:"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TOCSEAM-the-declared-unreadable-toc-never-leaves-the-log",
|
||
"why": "a book that DECLARED a table of contents and whose bytes could not be read falls back to the spine and is sold by characters. The engine has counted that since row 312 and told only its own log, while the far side of the seam reads the manifest — so `structure: delimited` crossed with no way to tell «this book never had one» from «it had one and we could not read it». Dropping the field from the built document restores exactly that silence",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheManifestSaysADeclaredTableOfContentsCouldNotBeRead",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\t\t// Always set, never left nil: absent is reserved for a sidecar written before the field existed.\n\t\tTOCUnreadable: &tocUnreadable,\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "TOCSEAM-the-count-is-published-as-a-constant-zero",
|
||
"why": "the field can be present, well-typed and always zero — the shape that reads as «measured, nothing wrong» on every book, including the broken ones. The zero case cannot catch it (its answer IS zero), so only a fixture with a declared-and-unreadable table of contents can, and that is why the pin builds a real epub whose navigation document the archive does not carry",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheManifestSaysADeclaredTableOfContentsCouldNotBeRead",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/manifest.go",
|
||
"find": "\tm := r.buildManifest(ch, chunks, structure, tocUnreadable, before)",
|
||
"replace": "\tm := r.buildManifest(ch, chunks, structure, 0, before)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "BANKCOMPLETE-the-log-calls-a-classifier-cut-a-partial-bank",
|
||
"why": "one warning fired by EITHER counter said «this bank is PARTIALLY consolidated» on a run whose render pass was intact and whose classifier alone was cut (measured 08.09: batches_dropped=0, classify_batches_dropped=1, consolidated=29). That is a false alarm about the object an owner signs, and it made the engine contradict its own read-out, which reports the bank's completeness from the render pass alone. A pair of surfaces that argue with each other about one run is worse than either half",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheLogAndTheReadOutAgreeAboutWhatIsPartial",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\tif res.BatchesDropped > 0 {\n\t\tr.Log.WarnContext(ctx, \"terminology: this bank is PARTIALLY consolidated",
|
||
"replace": "\tif res.BatchesDropped > 0 || res.ClassifyBatchesDropped > 0 {\n\t\tr.Log.WarnContext(ctx, \"terminology: this bank is PARTIALLY consolidated"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-everything-counts-as-delivered",
|
||
"why": "the money boundary is DELIVERY. Read as always-true it settles an estimate for a request that never left the process — a TLS handshake that hung, a dead host — which is the one case where «nothing was bought» is true by construction. That is the boundary inverted: paying for calls nobody received.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tif !tr.delivered(answered) {\n\t\treturn nil\n\t}",
|
||
"replace": "\tif false {\n\t\treturn nil\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-nothing-counts-as-delivered",
|
||
"why": "the same boundary read as always-false: every delivered-and-cut call goes back to booking ZERO, which is the defect backlog row 360 measured at 23–34% of an editor run. The pin has to fail on BOTH readings of one bit, or it is pinning the bit in one direction only.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tif !tr.delivered(answered) {\n\t\treturn nil\n\t}",
|
||
"replace": "\tif true {\n\t\treturn nil\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-our-own-deadline-is-retried",
|
||
"why": "a call OUR deadline cut is still being generated by the provider; retrying it buys that generation a second time. The old code did exactly this because it judged truncation by SIZE, and a body our deadline cut is small.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "func (e *AttemptCutError) retryable() bool { return e.Cause == CutByConnection }",
|
||
"replace": "func (e *AttemptCutError) retryable() bool { return e.Cause != CutByParent }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-self-cut-leaves-no-resolution",
|
||
"why": "THE THIRD LEAK, and the one that is invisible without this pin: without a resolved position the resume walks back to the provider and buys the call AGAIN through the resume door, so removing the retry alone would close the class halfway. Its test carries no money assertion on purpose — a red about the ledger here would be a right verdict for the wrong reason.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAfterASelfCutTheResumeCallsNobody",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\tif cut.Cause == llm.CutBySelfDeadline {",
|
||
"replace": "\tif false && cut.Cause == llm.CutBySelfDeadline {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-the-cut-costs-nothing",
|
||
"why": "the amount is the owner’s decision (D39.230 п.1): a call we cut is settled at the reservation estimate, not at zero. Booking zero understates the book against its own ceiling and hands the platform a margin it cannot measure.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "func settleUSDForCutCall(estimate float64) float64 { return estimate }",
|
||
"replace": "func settleUSDForCutCall(estimate float64) float64 { return 0 }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-stopped-run-leaves-no-mark",
|
||
"why": "the mark for a stopped position is what keeps a paid, unfinished chunk from reading as never started; inverting the stop test makes the guard mark everything EXCEPT a stop, which is the same silence in the mirror",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\tif !errors.Is(err, context.Canceled) || !errors.As(err, &cut) {",
|
||
"replace": "\tif errors.Is(err, context.Canceled) || !errors.As(err, &cut) {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-cancelled-mark-is-read-as-a-verdict",
|
||
"why": "`cancelled` records a STOP, not an answer. Served as a resolved verdict the resume re-does nothing, and the construction degenerates into its opposite — never re-doing anything that was interrupted — while every money assertion stays green.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\treturn FlagReason(cs.FlagReason) != FlagCancelled",
|
||
"replace": "\treturn true"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-paid-cut-checkpoint-is-replayed-as-a-result",
|
||
"why": "a `cancelled`/`connection_lost` checkpoint records money and NO result. Replayed as a completion it hands the stage an empty answer it never received, and the interrupted work is silently never re-done.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\treturn cp.FinishReason == cancelledFinish || cp.FinishReason == connectionLostFinish\n",
|
||
"replace": "\treturn false\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-the-redo-doubles-the-budget",
|
||
"why": "the re-done call must carry the budget the cut one was granted, and a REGENERATION after it must be ONE doubling and not two. Keyed on the attempt index instead of the doubling count, a burn followed by a truncated answer asks for FOUR times the base and reserves the money for it. ⚠ The simple case stopped proving this the day the burn walk moved inside runAttempt — the caller's index never advances for a burn, so both readings agree there and this mutation SURVIVED a green package. Only the compound sequence (burn → length → regenerate) separates them.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABurnFollowedByARegenerationDoesNotOverBuy",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tmaxTokens := maxTokensForAttempt(baseMaxTokens, escalations)",
|
||
"replace": "\t\tmaxTokens := maxTokensForAttempt(baseMaxTokens, attempt)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-stop-forgets-what-it-interrupted",
|
||
"why": "the FIRST cancellation exit. The retry loop used to return a bare ctx.Err() there, throwing away the only evidence that the stopped run had a request ON THE WIRE — so the runner could not settle a call it was never told about, and the position was left with no mark at all.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestACancelledRunKeepsBothTruthsInOneError",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\treturn zero, chainError(cancelledDuring(ctx.Err(), err), owedCut)",
|
||
"replace": "\t\t\treturn zero, ctx.Err()"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-non-2xx-becomes-a-purchase",
|
||
"why": "the status line is read BEFORE the body’s read error. Reversed, every 4xx/5xx whose small body happens to land on the deadline settles an estimate for a generation that never happened — the money boundary leaking through the one door that is supposed to be free.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestANonTwoHundredIsNotAPurchase",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\tretryable := retryableStatus(resp.StatusCode, data)",
|
||
"replace": "\tif false {\n\t\tretryable := retryableStatus(resp.StatusCode, data)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTDEADLINE-the-deadline-is-a-constant-again",
|
||
"why": "attempt_s read as THE deadline rather than as its floor is the original defect: 240 s is what the vendor rate gives for the DRAFT, and it was carried to an editor budgeted at 16 000 with a doubling to 32 000 — a call that could not finish inside it at any real speed.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestTheDeadlineClampsAtBothEnds",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\td := p.deriveDeadline(maxTokens)\n\tif d < p.AttemptTimeout {",
|
||
"replace": "\td := p.AttemptTimeout\n\tif d < p.AttemptTimeout {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTDEADLINE-attempt-s-stops-being-a-floor",
|
||
"why": "attempt_s being a FLOOR is what let this land on every shipping config at once — no provider loses a second it has today. Without the clamp a fast declared floor SHORTENS an existing deadline, and calls that fit today start being cut and billed as estimates.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestTheDeadlineClampsAtBothEnds",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tif d < p.AttemptTimeout {\n\t\td = p.AttemptTimeout\n\t}",
|
||
"replace": "\tif false {\n\t\td = p.AttemptTimeout\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTDEADLINE-an-unset-floor-divides-by-zero",
|
||
"why": "a config that omits tok_s_floor must wait the vendor default — too long, never not at all. A zero floor read literally makes the derived deadline meaningless and would cut every call the instant it went out, billing each one as an estimate.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestAnUnsetOrBrokenFloorFallsBackToTheVendorDefault",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tif floor <= 0 {\n\t\tfloor = defaultTokensPerSecFloor()\n\t}",
|
||
"replace": "\tif floor < 0 {\n\t\tfloor = defaultTokensPerSecFloor()\n\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-the-estimated-share-publishes-as-zero",
|
||
"why": "the machine-readable estimated pair beside committed_usd is the CONDITION the owner’s word came with, and the engine half of PD-441: without it the platform bills committed_usd and can say «at least this much» but never «at least X, up to Y».",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/paidtail.go",
|
||
"find": "\t\tif tok.PromptTokens == 0 && tok.CompletionTokens == 0 && tok.ReasoningTokens == 0 {",
|
||
"replace": "\t\tif false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-every-paid-row-is-called-an-estimate",
|
||
"why": "the other side of the same field. Marking every paid row estimated satisfies every positive assertion in the table and publishes a figure that says nothing — the shape a disclosure fails in quietly.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/paidtail.go",
|
||
"find": "\t\tif tok.PromptTokens == 0 && tok.CompletionTokens == 0 && tok.ReasoningTokens == 0 {",
|
||
"replace": "\t\tif true {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTWARN-the-default-floor-notice-is-never-said",
|
||
"why": "a provider with no measured speed runs on the vendor default and may wait a quarter of an hour. That is legitimate and must never be a surprise: the notice is what makes a new provider usable as DATA rather than as a mystery.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestTheFloorWarningIsSaidOnceAndOnlyWhenItApplies",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tif c.profile.TokensPerSecFloor <= 0 && d > c.profile.AttemptTimeout {",
|
||
"replace": "\tif false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTWARN-the-notice-is-said-for-a-provider-that-does-not-need-it",
|
||
"why": "the silent half. A notice printed for a provider whose calls all fit inside its own attempt_s is a warning about nothing, and an operator learns to scroll past the line that was supposed to explain a fifteen-minute wait.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestTheFloorWarningIsSaidOnceAndOnlyWhenItApplies",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tif c.profile.TokensPerSecFloor <= 0 && d > c.profile.AttemptTimeout {",
|
||
"replace": "\tif c.profile.TokensPerSecFloor <= 0 {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTWAIT-the-waiting-line-is-never-said",
|
||
"why": "with a deadline derived from the budget a call may legitimately run for a quarter of an hour, and one «calling model» line then leaves an operator watching a silence indistinguishable from a wedged process.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheWaitSaysSoWhileItLastsAndIsSilentOtherwise",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\tstopHeartbeat := r.logWaitingForProvider(ctx, st, ch, model, attempt, maxTokens)",
|
||
"replace": "\tstopHeartbeat := func() {}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTWAIT-the-interval-ignores-the-wait-it-reports",
|
||
"why": "the heartbeat is paced off the wait the engine actually granted. Fixed instead of derived, the line exists only for calls longer than the constant — which is every call nobody can observe and no test can afford to watch.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheHeartbeatIsPacedByTheWaitItReports",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\tq := deadline / 4\n",
|
||
"replace": "\tq := deadline\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCATALOG-a-long-waiting-provider-declares-nothing",
|
||
"why": "the catalogue gate is what makes «a provider the repository has never seen» work as DATA: one whose declared floors make its calls outgrow its own attempt_s must name either the speed it holds or how long we will wait. Dropped, the config says 240 s while the engine waits twenty minutes.",
|
||
"package": "./internal/config/",
|
||
"run": "TestAProviderThatWillWaitLongSaysSoInTheCatalog",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "configs/models.yaml",
|
||
"find": ", tok_s_floor: 50, queue_slack_s: 600, attempt_max_s: 1240 }",
|
||
"replace": " }"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTSEAM-the-estimated-pair-vanishes-on-a-clean-book",
|
||
"why": "the platform bills committed_usd from status --json and needs the estimated pair BESIDE it to say «≥ X, up to Y» instead of only «≥» (PD-441; the condition on D39.230 п.1). With omitempty the fields disappear exactly when the answer is «none of it was estimated», and a consumer cannot tell that from an engine too old to publish them — so a fully measured book reads as uncertain.",
|
||
"package": "./cmd/tmctl/",
|
||
"run": "TestStatusJSONPublishesTheEstimatedShareBesideCommitted",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\tEstimatedRows int `json:\"estimated_rows\"`\n\tEstimatedUSD float64 `json:\"estimated_usd\"`",
|
||
"replace": "\tEstimatedRows int `json:\"estimated_rows,omitempty\"`\n\tEstimatedUSD float64 `json:\"estimated_usd,omitempty\"`"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTFLAG-a-cut-chunk-is-the-mildest-thing-that-can-happen",
|
||
"why": "a flag with no entry in flagSeverity falls to severityUnknown, which is LAST — so a chapter whose worst problem is «we paid for a call, our own deadline cut it and the text is gone» would report any other flag as its worst. Measured on this very pack: both new flags landed there while the exhaustiveness gate stayed GREEN, because it could not read a FlagReason declared as a conversion of another package's constant (18 ranks against 16 constants it could see).",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryFlagReasonIsRanked",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\tFlagDecodeError: 4,\n\tFlagAttemptTimeout: 4,\n",
|
||
"replace": "\tFlagDecodeError: 4,\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTFLAG-the-rank-gate-goes-blind-to-a-converted-constant",
|
||
"why": "the gate's own guard. A FlagReason whose value is a conversion of another package's constant has no literal to unquote, and the walk used to `continue` past it — a silent skip inside an exhaustiveness test, which is the very defect the test exists to catch, one level up. Removing the resolver must make it FAIL LOUD about the unreadable value, never go quiet again.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestEveryFlagReasonIsRanked",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/flagseverity_test.go",
|
||
"find": "\t\"llm.CutBySelfDeadline\": FlagReason(llm.CutBySelfDeadline),\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-word-is-enough-to-burn-a-paid-answer",
|
||
"why": "the finish_reason string shares a namespace with whatever a vendor prints — the adapter already normalises invented values like «sensitive» — so keying the burn on the word alone throws away a real translation that happened to arrive under one of our own names, and buys it again. A genuine burn is written by this engine and is always textless.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestABurnIsMoneyWithoutAnAnswerAndNotJustAWord",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\tif cp.ResponseText != \"\" {\n\t\treturn false // a reply with content is an answer, whatever it calls its finish reason\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTVOLUME-a-stopped-position-is-counted-as-a-free-resume",
|
||
"why": "the volume planner asks «does this row resume without a provider call» with its own copy of the predicate. Not knowing about `cancelled`, it counts a unit whose last row is one as costing nothing — so a grant of ONE unit pays for two, and the volume report, which speaks only when something was carried, says nothing at all. Two copies of one question is how the disclosure went silent.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestACancelledRowIsNotAFreeResume",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tif !resolvedForResume(&cs) {\n\t\t\treturn false\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-reply-that-outruns-our-own-write-books-zero",
|
||
"why": "net/http hands back a response before WroteRequest fires (Request.write defers it onto the write loop; roundTrip returns on the response channel), so a provider answering EARLY — which DeepSeek documents doing while a request waits to be scheduled — can have its 200 overtake our own callback while the request body still drains. Read as «not delivered» that call books $0 and is re-asked under the full attempt budget: the very leak this pack closes, entering through its own door.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestAReplyThatOutrunsOurOwnWriteIsStillDelivered",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\treturn t.wrote.Load() || (answered && t.firstByte.Load())",
|
||
"replace": "\treturn t.wrote.Load()"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-failed-write-counts-as-a-delivery",
|
||
"why": "`WroteRequest` fires with an ERROR when the write itself failed: the provider received nothing, generated nothing and owes nothing, so settling an estimate charges a reader for a call nobody got. The guard had no pin at all until an adversarial pass removed it and watched both packages stay green.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestAFailedWriteIsNotADelivery",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\t\t\tif info.Err == nil {\n\t\t\t\tt.wrote.Store(true)\n\t\t\t}",
|
||
"replace": "\t\t\tt.wrote.Store(true)"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCEILING-a-cancelled-run-reports-a-money-stop",
|
||
"why": "a run that is ENDING did not stop on money, whatever the ledger says at that instant. Paying for a cut call (D39.230 п.1) made this reachable: a stop commits every flying call's estimate, which can carry a book past its own ceiling, and the next worker then publishes a `ceiling` event with a shortfall. The platform lets that event survive any exit code, so it records `paused` and tells a person to add money for a run that person stopped themselves. Caught by the existing wait-cancellation pin only intermittently — the guard first sat at the top of the loop, and waitForSettle answers «nothing in flight» BEFORE it looks at the context, so a worker fell through it to the halt.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestACancelledRunNeverReportsAMoneyStop",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tif ctx.Err() != nil {\n\t\t\tr.setJobStatus(ctx, job.ID, \"failed\")\n\t\t\treturn att, fmt.Errorf(\"pipeline: reserve $%.6f for %s/ch%d/chunk%d/%s: the run ended before the reservation was granted: %w\",\n\t\t\t\testimate, r.Book.BookID, ch.Chapter, ch.ChunkIdx, st.Name, ctx.Err())\n\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-stop-during-a-backoff-forgets-what-it-interrupted",
|
||
"why": "the SECOND cancellation exit. A run stopped during a retry backoff has a delivered, possibly billed attempt behind it — a cut connection, an undecodable 2xx — and a bare ctx.Err() there leaves the runner nothing to settle and the chunk no mark at all. Measured: a delivered connection_lost plus a stop inside the backoff booked $0 and wrote no chunk_status row. The window is the whole sleep, up to a minute on the shipping config, and it opens exactly where a flapping provider makes an operator reach for the stop.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestAStopDuringABackoffKeepsTheEvidence",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\treturn zero, chainError(cancelledDuring(ctx.Err(), lastErr), owedCut)",
|
||
"replace": "\t\t\treturn zero, ctx.Err()"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-money-a-redrive-unaccounted-for-reads-as-measured",
|
||
"why": "a redrive DELETES the checkpoints of the stages it re-attacks and leaves their spend committed («after a redrive committed(spend) >= SUM(checkpoints), the safe direction», store/chunkstatus.go). Derived from the surviving rows alone the published estimate falls to zero, and the platform is told that money nobody can account for was MEASURED — the one direction this figure exists to prevent. Measured on a live redrive: committed unchanged at $0.001056, estimated dropped to $0.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestMoneyWithNoCheckpointLeftIsStillAnEstimate",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/paidtail.go",
|
||
"find": "\tif gap := committedUSD - accounted; gap > 1e-9 {\n\t\trows, usd = rows+1, usd+gap\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-refusal-pays-when-the-reply-outruns-the-write",
|
||
"why": "the regression the early-200 fix introduced, and the more expensive direction of the same bit. A provider that REFUSES (401/403/413/quota-429) and resets while our body is still writing gives GotFirstResponseByte=true and a WRITE error, so http.Client.Do returns the write failure and the status line is never in our hands. Taking the response byte alone as proof of delivery paid an estimate for every one: measured 22 refusals in 25, one of them $0.80 for a request the provider declined — and the delivered-cut retry sent the whole body again.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestARefusalIsNotAPurchaseEvenWhenTheReplyOutrunsTheWrite",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\treturn t.wrote.Load() || (answered && t.firstByte.Load())",
|
||
"replace": "\treturn t.wrote.Load() || t.firstByte.Load()"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTVOLUME-a-retired-stage-costs-a-slot",
|
||
"why": "the per-row walk drops the stages this pipeline no longer runs — nothing will call them, so they cannot cost anything. Asking «does this row resume for free» BEFORE that drop makes a unit paid for a `cancelled` row of a retired stage, so an operator who edits the pipeline over a stopped run spends volume slots on calls that will never be made. Same error as the one this predicate fixed, in the opposite direction.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestADroppedStageIsFreeEvenWhenItsRowSaysCancelled",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/volume.go",
|
||
"find": "\t\tvar cur string\n\t\tvar w wave",
|
||
"replace": "\t\tif !resolvedForResume(&cs) {\n\t\t\treturn false\n\t\t}\n\t\tvar cur string\n\t\tvar w wave"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-redirect-carries-delivery-to-the-next-hop",
|
||
"why": "`Do` spans the WHOLE redirect chain and the delivery trace does not reset between its legs, so the first leg reaching a redirector sets WroteRequest for good — and a second leg whose connect is REFUSED still looked delivered. Measured through the ledger: $0.001056 booked for a `connect: connection refused` that never put a byte on any wire, AfterHeaders true, zero bytes from the target. A stale `http://` in base_url is enough to trigger it; following the redirect at all also carries an Authorization header to a host nobody chose.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestARedirectIsNotADelivery",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\treturn &http.Client{Transport: base, CheckRedirect: doNotFollowRedirects}",
|
||
"replace": "\treturn &http.Client{Transport: base}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTWAIT-the-waiting-line-claims-a-delivery-it-never-checked",
|
||
"why": "the runner cannot see the transport's trace, so «a call that has been delivered» was an assertion nobody had asked. On a request that never went out it printed eighteen times, telling the operator the opposite of what happened — the same defect as a flag naming the wrong cause (D39.93 п.2).",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheWaitSaysSoWhileItLastsAndIsSilentOtherwise",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\"still waiting for the provider on a call in flight\"",
|
||
"replace": "\"still waiting for the provider on a call that has been delivered\""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-the-local-client-still-follows-redirects",
|
||
"why": "the redirect guard first went only on the cloud client, whose own comment says the local provider «passes its OWN no-proxy client, so it is untouched» — true of keepalive, and read as permission for redirects too. Both go through the same attempt() and the same delivery trace, so a leg whose connect is refused still looks delivered. Measured on the local client after the cloud one was fixed: an AttemptCutError for a `connect: connection refused` with two hops. The money is $0 there only because the local model is priced at zero; the behaviour is wrong either way.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestARedirectIsNotADelivery",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/provider_local.go",
|
||
"find": "\treturn &http.Client{Transport: &http.Transport{Proxy: nil}, CheckRedirect: doNotFollowRedirects}",
|
||
"replace": "\treturn &http.Client{Transport: &http.Transport{Proxy: nil}}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTWAIT-a-per-attempt-deadline-is-printed-as-the-whole-wait",
|
||
"why": "the heartbeat wraps client.Complete — the WHOLE retry chain — while the only deadline it can name is one ATTEMPT's. Under the key `of` that printed «waited=4s of=1s», a contradiction that reads as exactly the hung process the line exists to rule out; measured at 18 such lines in one run. The key `attempt` was a second half of the same: the transport logs an `attempt` of its own, and two different numbers under one name in one stream is a question an operator cannot answer.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheWaitSaysSoWhileItLastsAndIsSilentOtherwise",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\"stage_attempt\", attempt, \"waited\", time.Since(started).Round(time.Millisecond).String(),\n\t\t\t\t\t\"attempt_deadline\", r.Models.AttemptDeadline(model, maxTokens).String())",
|
||
"replace": "\"attempt\", attempt, \"waited\", time.Since(started).Round(time.Millisecond).String(),\n\t\t\t\t\t\"of\", r.Models.AttemptDeadline(model, maxTokens).String())"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-the-anthropic-client-carries-its-key-through-a-redirect",
|
||
"why": "the third client this package builds, and the one where the redirect is a CREDENTIAL leak rather than a money one: it sends `x-api-key`, and net/http strips only Authorization/Cookie/WWW-Authenticate across a host change. Measured: the redirect target received the key verbatim and the call returned a nil error. The adapter is deprecated with no live provider, but clients.go builds it for any config declaring `kind: anthropic`.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestARedirectIsNotADelivery",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/provider_anthropic.go",
|
||
"find": "\t\thttp: &http.Client{CheckRedirect: doNotFollowRedirects},",
|
||
"replace": "\t\thttp: &http.Client{},"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-the-operator-screen-drops-the-estimated-share",
|
||
"why": "the human twin of the status --json seam, and it had no pin at all — removing the block left both packages green while renderStatusHuman itself is 76% covered. It is the surface an operator reads before deciding whether to keep paying, and the number beside `committed` is what makes that figure a RANGE rather than a measurement (the condition attached to D39.230 п.1).",
|
||
"package": "./cmd/tmctl/",
|
||
"run": "TestStatusHumanShowsTheEstimatedShare",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\tif rep.EstimatedRows > 0 {",
|
||
"replace": "\tif false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-stop-over-the-hop-leaves-no-mark",
|
||
"why": "the cancelled-position mark lived at the attempt loop's error return, and the escalation hop, the repair sub-step and the bank batches all leave runStage through OTHER returns. Measured by an acceptance verifier: a run stopped over the HOP settled the money and left chunk_status_rows=0 — the unmarked hole §4.2 forbids — while the resume made ZERO fresh calls because the hop addresses a fixed attempt 0 and the burned key there is hit forever.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAStopOverAnEscalationHopLeavesAMarkAndIsRedone",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\tdefer func() {\n\t\tr.recordCancelledStage(ctx, cancelledPosition{\n\t\t\tstage: st, chunk: ch, snapshotID: snapID, contentHash: contentHash,\n\t\t\tcumCostUSD: cumCost, attempts: attemptsMade,\n\t\t}, err)\n\t}()\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-burned-key-is-spent-only-inside-the-loop",
|
||
"why": "walking over a burned key first lived in runStage's loop, leaving the three callers OUTSIDE it with a key burnt forever — the hop most visibly, since it addresses a fixed attempt 0. Moving the walk into runAttempt gives every caller the rule; putting it back into the loop alone re-opens «money spent, work never re-done» for the hop, the repair sub-step and the bank batches at once.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAStopOverAnEscalationHopLeavesAMarkAndIsRedone",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tif cp == nil || !burnedByCut(cp) {\n\t\t\tbreak\n\t\t}",
|
||
"replace": "\t\tif cp == nil || true {\n\t\t\tbreak\n\t\t}"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-cut-checkpoint-is-written-under-the-wrong-key",
|
||
"why": "§4.3's real claim: the resume finds the CHECKPOINT of a self-cut. The ordinary resume resolves from chunk_status before any render, so a pin that stops there proves the first door shut and says nothing about the second — an acceptance verifier planted this and it SURVIVED a green package. The torn-store fixture enters through the door the checkpoint exists for.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestASelfCutResumesFromITSCHECKPOINTAndNotOnlyFromChunkStatus",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\t\tRequestHash: c.reqHash, JobID: c.job.ID",
|
||
"replace": "\t\tRequestHash: c.reqHash + \"z\", JobID: c.job.ID"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-classify-forgets-what-a-cut-checkpoint-means",
|
||
"why": "a replayed cut checkpoint with no verdict falls through to «empty completion» — and `empty` is RETRYABLE, so the chunk is re-bought on a DOUBLED budget. The chunk_status door hides it: this mutation SURVIVED on a green package until a fixture reached the checkpoint path directly.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestASelfCutResumesFromITSCHECKPOINTAndNotOnlyFromChunkStatus",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/disposition.go",
|
||
"find": "\tcase attemptTimeoutFinish:",
|
||
"replace": "\tcase attemptTimeoutFinish + \"-dead\":"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-a-delivered-cut-is-erased-by-a-later-terminal-status",
|
||
"why": "only the LAST error left the retry loop, so a chain that cut a delivered request and then met a terminal 4xx returned the status alone: the runner read «the request never went out», released the reservation and booked $0 for a generation the provider had made, leaving the position unmarked. Reachable in the ordinary way — a 400/401/403/413 on the retry after a broken socket.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestADeliveredCutSurvivesATerminalStatusLaterInTheChain",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\treturn zero, chainError(err, owedCut)",
|
||
"replace": "\t\t\treturn zero, err"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCALL-the-delivery-count-is-not-carried",
|
||
"why": "a broken connection is worth one retry, so the provider is asked to generate TWICE while the store books ONE estimate — they share a key and spend is written only through a checkpoint. Under-counting is the ratified direction (D39.196 п.2а), but a SILENT under-count is what row 360 was opened about: the count is what makes the gap readable in the ledger row and the log.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestABrokenConnectionAfterDeliveryIsRetriedOnceAndOnlyOnce",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\t\tchainCuts = append(chainCuts, cut)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-a-write-into-the-peers-window-is-taken-as-a-purchase",
|
||
"why": "`WroteRequest` says our bytes entered the peer's TCP window; it says NOTHING about the application behind it — a load balancer accepts while the backend never sees the request. Measured on a stopped run: 3 of 25 cancelled in-flight calls settled an estimate for a request no handler ever entered. On a non-streaming wire the only signal a provider's application acknowledged the request is a 2xx object reaching us, so money is drawn on that; delivery still drives RETRY. Charging a reader for a call nobody ran is the one direction the canon forbids (D39.196 п.2а).",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestAnAcceptedSocketThatNobodyReadCostsNothing",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\t\tBillable: answered && tr.afterHeaders(),",
|
||
"replace": "\t\tBillable: true,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-an-acknowledged-cut-stops-being-paid",
|
||
"why": "the other side of the same boundary: a call the provider DID acknowledge with a 2xx and then had cut short is exactly what row 360 was opened about, and booking zero for it takes the engine back to «No 2xx ever arrived: nothing was billed» — the comment that was false by construction on the shipping provider.",
|
||
"package": "./internal/pipeline/",
|
||
"run": "TestTheNineOutcomesOfACall",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\t\tBillable: answered && tr.afterHeaders(),",
|
||
"replace": "\t\tBillable: false,"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTKEEPALIVE-the-read-idle-bound-is-not-installed",
|
||
"why": "the h2 keepalive pair is the reason a long thinking-heavy call survives an idle proxy: with no DATA frame flowing, a PING is what keeps the socket open, and a dead connection then surfaces as a clean retryable transport error instead of a mid-generation RST that re-bills the call. Wiring the bound nowhere leaves the comment true and the transport bare.",
|
||
"package": "./internal/llm/",
|
||
"run": "TestTheKeepalivePairSitsOnTheTransportTheCloudClientBuilds",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\th2.ReadIdleTimeout = h2ReadIdleTimeout\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTBURN-the-paid-contract-ignores-the-burn",
|
||
"why": "with the burn ignored, ANY row at the key reads as paid — and the pre-gates then skip the budget check while the funnel, which cannot replay a burned row, goes and buys the work again outside every bound",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestABurnedRepairKeyDoesNotBuyARepairOutsideTheSubBudget|TestABurnedHopKeyDoesNotBuyAHopOutsideTheEscalationBudget|TestTheBankPaidProbeSeesThroughABurnedCheckpoint",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\t\tif !burnedByCut(cp) {\n",
|
||
"replace": "\t\tif cp != nil {\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTBURN-the-paid-contract-stops-at-the-first-key",
|
||
"why": "the funnel does not stop at a burned key — it walks to the next index at the same budget and buys there, so the answer to «already paid» often sits one index up. A probe that stops at the starting key hides a PAID, ANSWERED call, and the caller with no budget left discards work it already bought",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAPaidHopBehindABurnedKeyStillReplaysFree|TestAPaidRepairBehindABurnedKeyStillReplaysFree|TestTheBankProbeFindsThePaidBatchBehindABurnedKey",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\t\tattempt++\n\t}\n}\n",
|
||
"replace": "\t\treturn false, nil\n\t}\n}\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTBURN-the-repair-pregate-asks-a-fixed-index",
|
||
"why": "the one definition of «already paid» exists so three pre-gates cannot drift apart; a site that goes back to asking a fixed attempt index answers a different question from the funnel and discards a paid repair",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAPaidRepairBehindABurnedKeyStillReplaysFree",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/repair.go",
|
||
"find": "\treturn r.paidAfterBurns(rst, model, snapID, ch, ordinal, maxTokens, msgs)\n",
|
||
"replace": "\tcp, err := r.Store.GetCheckpoint(RequestHash(r.attemptRequest(rst, model, snapID, ch, ordinal, maxTokens, msgs)))\n\treturn cp != nil && !burnedByCut(cp), err\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTBURN-the-escalation-pregate-asks-a-fixed-index",
|
||
"why": "same drift on the hop: a fixed index hides a paid, successful hop behind a burned key, and the exhausted escalation budget then throws that translation away on every resume",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAPaidHopBehindABurnedKeyStillReplaysFree",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/escalation.go",
|
||
"find": "\tmayHop, err := r.paidAfterBurns(st, st.ResolvedHop, snapID, ch, 0, hopMaxTokens, msgs)\n",
|
||
"replace": "\tfbCP, err := r.Store.GetCheckpoint(RequestHash(r.attemptRequest(st, st.ResolvedHop, snapID, ch, 0, hopMaxTokens, msgs)))\n\tmayHop := fbCP != nil && !burnedByCut(fbCP)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTBURN-the-bank-pregate-asks-a-fixed-index",
|
||
"why": "same drift on the terminology batch, the site the other two were written from: a fixed index hides the batch the funnel already bought one index up, and the role sub-budget refuses to serve work it has paid for",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheBankProbeFindsThePaidBatchBehindABurnedKey",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\treturn r.paidAfterBurns(st, st.Model, snapID, ch, 0, maxTokens, msgs)\n",
|
||
"replace": "\tcp, err := r.Store.GetCheckpoint(RequestHash(r.attemptRequest(st, st.Model, snapID, ch, 0, maxTokens, msgs)))\n\treturn cp != nil && !burnedByCut(cp), err\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTKEEPALIVE-the-cloud-client-is-not-tuned-at-all",
|
||
"why": "the keepalive bounds are only worth their comment if the CLIENT THE ENGINE BUILDS carries them; a check written against a transport the test tuned itself passes even when the construction stopped tuning anything, which is exactly how the write bound's pin survived its own mutation",
|
||
"package": "./internal/llm/",
|
||
"run": "TestTheKeepalivePairSitsOnTheTransportTheCloudClientBuilds",
|
||
"battery": true,
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\th2 := tuneHTTP2(base)\n",
|
||
"replace": "\tvar h2 *http2.Transport\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTBANK-admission-is-a-prefix-again",
|
||
"why": "an already-paid batch costs nothing and is admitted whatever the budget says, so a refused batch can sit in FRONT of batches that are free to serve; a prefix bound drops every paid batch behind the first refusal, buying nothing and losing a consolidated bank — while the loop's own comment promises they are admitted regardless",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAnUnaffordableBatchDoesNotTakeThePaidBatchesBehindIt",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/terminologist.go",
|
||
"find": "\t\t\tcontinue // this one cannot be afforded; the ones after it may still be free\n",
|
||
"replace": "\t\t\tbreak\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCHAIN-the-exit-ranks-cuts-by-type-not-money",
|
||
"why": "the exit hands the caller ONE error; deciding which cut it carries by TYPE returns the ending cut whatever it cost, so a free cut nobody acknowledged erases the paid one behind it and the engine books $0",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestAFreeCutLaterDoesNotMaskThePaidCutEarlier",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\tif errors.As(owed, &oc) && oc.Billable && !fc.Billable {\n",
|
||
"replace": "\t\tif errors.As(owed, &oc) && false {\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCHAIN-the-owed-cut-is-joined-last",
|
||
"why": "errors.As hands back the FIRST match it meets walking the tree, so joining the free ending cut ahead of the paid owed one leaves the caller settling from the free one — the masking the branch exists to undo, restored by an argument order",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestAFreeCutLaterDoesNotMaskThePaidCutEarlier|TestTheCancellationExitCarriesWhatTheChainOwes",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\treturn errors.Join(owed, final)\n",
|
||
"replace": "\t\t\treturn errors.Join(final, owed)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCHAIN-the-stop-exit-drops-what-is-owed",
|
||
"why": "a run stopped right after an attempt can have a paid cut behind it and an ordinary retryable in front; returning only «cancelled + the last error» leaves the runner nothing to settle and the position unmarked",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestTheCancellationExitCarriesWhatTheChainOwes",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\treturn zero, chainError(cancelledDuring(ctx.Err(), err), owedCut)\n",
|
||
"replace": "\t\t\treturn zero, cancelledDuring(ctx.Err(), err)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCHAIN-a-plain-retryable-erases-the-paid-cut",
|
||
"why": "the chain can be billed for an attempt that is not the one whose error ends it; handing the caller only the terminal 503 books $0 for a generation the provider made",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestAPaidCutSurvivesAPlainRetryableLaterInTheChain",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\treturn zero, chainError(fmt.Errorf(\"%s: exhausted %d attempts: %w\", name, profile.MaxAttempts, lastErr), owedCut)\n",
|
||
"replace": "\treturn zero, fmt.Errorf(\"%s: exhausted %d attempts: %w\", name, profile.MaxAttempts, lastErr)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTROW-the-burn-money-is-overwritten",
|
||
"why": "the funnel walks past a burned key and buys the work again one index up; the row must carry BOTH the money that bought nothing and the money that bought the text. Overwriting the walk's total with the fresh call's cost puts chunk_status.cost_usd below SUM(checkpoints) for the position — money the book spent and the projection does not show",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestABurnedKeysMoneyReachesTheRow",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\tatt.cumCost, att.runCost = burnedCost+cost, cost\n",
|
||
"replace": "\tatt.cumCost, att.runCost = cost, cost\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTROW-the-hop-money-waits-for-the-verdict",
|
||
"why": "a hop that was CUT reports its cost and an error at the same time; adding the cost only on the success path leaves the deferred cancelled mark carrying the primary's money alone — measured at ledger 0.001176 against a row of 0.000120",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAStoppedRunLeavesEveryRowMatchingItsOwnLedger",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\tcumCost += esc.fb.cumCost\n\trunCost += esc.fb.runCost\n\tanyFresh = anyFresh || esc.fb.freshCall\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif esc.attempted {\n\t\tescalated = true\n",
|
||
"replace": "\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif esc.attempted {\n\t\tcumCost += esc.fb.cumCost\n\t\trunCost += esc.fb.runCost\n\t\tanyFresh = anyFresh || esc.fb.freshCall\n\t\tescalated = true\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTROW-the-repair-money-waits-for-the-verdict",
|
||
"why": "same shape on the repair sub-step: a repair call the engine cut reports its cost together with the error, and the row is the only place that cost can land",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAStoppedRunCarriesTheRepairsMoneyToTheRow",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/repair.go",
|
||
"find": "\t\tres.CostUSD += att.runCost\n\t\tres.CumUSD += att.cumCost\n\t\tres.Fresh = res.Fresh || att.freshCall\n\t\tif err != nil {\n",
|
||
"replace": "\t\tif err != nil {\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTSTATE-a-stopped-position-decides-the-unit",
|
||
"why": "a cancelled row records a position the engine was stopped over: the work was never done and the resume re-does it and pays again. Reading it as a decided unit puts it in the extrapolation's denominator, so one stop makes the book look further along and cheaper than it is — and the re-bill consent threshold, min($0.50, 5% x projected), shrinks with it",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAStoppedPositionIsNotADecidedUnit",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\t\t\tif !resolvedForResume(&cs) {\n\t\t\t\tcontinue\n\t\t\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTROW-the-attempt-count-waits-for-the-verdict",
|
||
"why": "the deferred cancelled mark reports the attempt count beside money that was really paid; writing the count only after the error check leaves `attempts=0` on a position whose ledger says 0.001056 — a row that reports work nobody did, and the number an operator reads to tell «one call was cut» from «the position never started»",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAStopOnAStagesFirstCallStillReportsAnAttempt",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\t\tattempt = att.attempt\n\t\tattemptsMade = attempt + 1\n\t\tif err != nil {\n\t\t\treturn nil, err // infra failure; the cancelled-position mark is the defer above\n\t\t}\n",
|
||
"replace": "\t\tif err != nil {\n\t\t\treturn nil, err // infra failure; the cancelled-position mark is the defer above\n\t\t}\n\t\tattempt = att.attempt\n\t\tattemptsMade = attempt + 1\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMONEY-response-bytes-alone-mean-payment",
|
||
"why": "a cut is billable only when the provider ACKNOWLEDGED the request with a reply; dropping the `answered` half makes response BYTES enough, so a broken proxy writing garbage — or any peer whose bytes arrive without a status line — books an estimate for a generation nobody made. This is the direction D39.196 п.2а forbids",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestResponseBytesWithoutAReplyAreNotAPurchase",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\t\tBillable: answered && tr.afterHeaders(),\n",
|
||
"replace": "\t\tBillable: tr.afterHeaders(),\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTSEV-cancelled-becomes-the-worst-problem",
|
||
"why": "the passport reports a chapter's WORST flag; `cancelled` is a state the next run erases, so ranking it above a durable finding hides the finding behind it. The exhaustiveness test proves only that every reason HAS a rank — the order itself was pinned by nothing",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheSeverityTableMeansWhatItsCommentsSay",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\tFlagCancelled: 8,\n",
|
||
"replace": "\tFlagCancelled: 0,\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTSEV-the-paid-and-lost-pair-stops-ranking-together",
|
||
"why": "decode_error and attempt_timeout are the same thing to a reader — the chunk is lost and the money is spent — and the table says so in its own comment; splitting them makes the passport call one of the two worse than the others for no reason a reader could act on",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheSeverityTableMeansWhatItsCommentsSay",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "\tFlagAttemptTimeout: 4,\n",
|
||
"replace": "\tFlagAttemptTimeout: 5,\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTSEV-an-unknown-reason-outranks-a-diagnosis",
|
||
"why": "a reason this build has never heard of — an older schema's row, or junk — must not out-rank a diagnosis the engine actually made; putting it first makes an unparsed string the headline of a chapter that has real findings",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheSeverityTableMeansWhatItsCommentsSay",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/status.go",
|
||
"find": "const severityUnknown = 9\n",
|
||
"replace": "const severityUnknown = 0\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCFG-a-cap-below-the-floor-loads-quietly",
|
||
"why": "the derivation clamps UP to attempt_s and then DOWN to attempt_max_s, so a cap below the floor wins: every call to that provider gets less time than the file declares, attempt_s stops being the FLOOR its doccomment calls it, and nothing logs the difference — the calls simply come back cut",
|
||
"package": "./internal/config/",
|
||
"battery": true,
|
||
"run": "TestACapBelowTheFloorIsRefusedAtLoad",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/models.go",
|
||
"find": "\tif t.AttemptMaxS > 0 && t.AttemptMaxS < t.AttemptS {\n",
|
||
"replace": "\tif false {\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCFG-the-timeout-check-is-not-wired-in",
|
||
"why": "the relation check exists and nothing calls it — the shape this pack has removed twice already, a guard living in the reader's head and not in the code",
|
||
"package": "./internal/config/",
|
||
"battery": true,
|
||
"run": "TestACapBelowTheFloorIsRefusedAtLoad",
|
||
"edits": [
|
||
{
|
||
"file": "internal/config/models.go",
|
||
"find": "\t\tvalidateTimeouts(bad, name, p.Timeouts)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCFG-a-measured-speed-floor-is-deleted",
|
||
"why": "a speed floor is the p10 of that provider's own request_log over hundreds of rows; deleting the line returns the provider to the vendor default, which is within two percent of the measured value — nothing breaks, the measurement is simply gone, and the catalogue gate cannot see it because a deleted line and a never-declared field are the same zero",
|
||
"package": "./internal/config/",
|
||
"battery": true,
|
||
"run": "TestEveryDeclaredDeadlineKnobStaysDeclared",
|
||
"edits": [
|
||
{
|
||
"file": "configs/models.yaml",
|
||
"find": " timeouts: { attempt_s: 240, max_attempts: 3, backoff_cap_s: 60, tok_s_floor: 35 }\n",
|
||
"replace": " timeouts: { attempt_s: 240, max_attempts: 3, backoff_cap_s: 60 }\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCOUNT-deliveries-counts-cuts-again",
|
||
"why": "the ledger line beside a cut says «the provider was asked N times; ONE estimate is booked for all of them» — the only place the gap between what was generated and what was billed is visible. Counting the CUTS instead of the DELIVERIES makes it understate a mixed chain: a 503 is an answer, so the peer read the request",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestTheDeliveryCountCountsDeliveriesNotCuts",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\t\tcc.Deliveries = askedToGenerate\n",
|
||
"replace": "\t\t\t\tcc.Deliveries = deliveredCutSeen\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTLINE-the-tail-splits-across-rows",
|
||
"why": "errors.Join separates its members with a newline, and an engine error carrying both a cut and what ended the chain is such a join; without collapsing it one table row prints as two and breaks the alignment of everything under it",
|
||
"package": "./cmd/tmctl/",
|
||
"battery": true,
|
||
"run": "TestTheOperatorTailIsOneLineAndKeepsTheNoteThatMatters",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/render.go",
|
||
"find": "\ts = strings.Join(strings.Fields(s), \" \")\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTLINE-the-note-goes-back-to-the-end",
|
||
"why": "the operator's column is bounded at 120 bytes and the transport error in front of the note is routinely longer, so a note appended at the end reaches nobody — and it is the only place the gap between what the provider generated and what the ledger booked is visible",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheAskedNTimesNoteReachesTheReader",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\treturn fmt.Sprintf(\"[the provider was asked %d times; %s] %s\", deliveries, booked, err.Error())\n",
|
||
"replace": "\treturn fmt.Sprintf(\"%s [the provider was asked %d times; %s]\", err.Error(), deliveries, booked)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTLINE-the-note-claims-money-that-was-not-booked",
|
||
"why": "on a cut the provider never acknowledged, nothing is booked; a line saying «ONE estimate is booked for all of them» beside a $0 row is a sentence a reader has to disbelieve before they can use it, and this is the one line where the generated-versus-billed gap is visible",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheAskedNTimesNoteReachesTheReader",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\tif cost <= 0 {\n\t\tbooked = \"NOTHING is booked: the provider acknowledged none of them\"\n\t}\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCHAIN-the-stop-exit-drops-the-cancellation",
|
||
"why": "a stopped run must read as cancelled even when the attempt in flight failed of its own accord — the exit code (5, not 1) and the `stopped` the stream publishes both hang off it. The attempt's own error knows nothing about the context, so only this exit can carry it out; the wire fixture beside this one cannot see the loss, because there the attempt's error is itself a parent-cancelled cut and carries the cancellation on its own",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestTheStopExitStillReadsAsCancelledWhenTheAttemptDidNot",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\t\treturn zero, chainError(cancelledDuring(ctx.Err(), err), owedCut)\n",
|
||
"replace": "\t\t\treturn zero, chainError(err, owedCut)\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTEXIT-a-stop-stops-mapping-to-five",
|
||
"why": "the three cut causes share one Go type and two of them are ordinary failures while the third is a person pressing stop; a mapping that stops telling them apart reports an operator's stop as an engine failure — and the number is what a runbook and the platform's stream both branch on",
|
||
"package": "./cmd/tmctl/",
|
||
"battery": true,
|
||
"run": "TestTheCutErrorTypeKeepsItsExitCode",
|
||
"edits": [
|
||
{
|
||
"file": "cmd/tmctl/main.go",
|
||
"find": "\tcase errors.Is(err, context.Canceled):\n\t\treturn 5\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTDEADLINE-the-vendor-budget-moves",
|
||
"why": "every provider without its own measured floor derives its attempt deadline from this pair; a budget four times too generous makes the deadline four times too SHORT, so calls the provider is still generating get cut by us — and since this pack a self-cut the provider acknowledged is PAID FOR. Five of the eight catalogued providers run on this default, and the formula test cannot see the change: both of its sides read these same constants, so a corruption moves the expectation with the result",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestTheVendorsPublishedPairIsWhatTheVendorPublishes",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tvendorHourlyTokenBudget = 128000\n",
|
||
"replace": "\tvendorHourlyTokenBudget = 512000\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTDEADLINE-the-vendor-window-moves",
|
||
"why": "the other half of the same pair: a window of a minute instead of an hour makes every derived deadline sixty times too long, and a run that looks hung is the only place it shows",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestTheVendorsPublishedPairIsWhatTheVendorPublishes",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/attemptcut.go",
|
||
"find": "\tvendorBudgetWindow = time.Hour\n",
|
||
"replace": "\tvendorBudgetWindow = time.Minute\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTMARK-the-stop-is-judged-by-the-first-cause",
|
||
"why": "the retry chain hands up the cut with the strongest MONEY claim, which is deliberately the earlier paid break when the later one is free — so a run a person stopped arrives carrying a cause that is not «stopped». A guard reading that first cause decides a stop is not a stop and returns in silence: the money is settled, the position gets no row, and the export shows a gap nobody can explain. §4.2 forbids a hole with no mark at any moment",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAStopBehindAPaidBreakStillMarksThePosition",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\tif !errors.Is(err, context.Canceled) || !errors.As(err, &cut) {\n",
|
||
"replace": "\tif !errors.As(err, &cut) || cut.Cause != llm.CutByParent {\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCOUNT-only-the-newest-cut-is-stamped",
|
||
"why": "the count is a property of the CHAIN, and chainError deliberately hands up an EARLIER cut when that is the one that owes money — carrying a number frozen at its birth. A chain that cut once and was then answered two 503s delivered three times and would report one, under-stating the very gap the ledger line exists to show; the later deliveries are not cuts, so nothing in the error tree knows about them",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestAnOlderCutCarriesTheChainsFinalDeliveryCount",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\tdefer func() {\n\t\t\tfor _, cc := range chainCuts {\n\t\t\t\tcc.Deliveries = askedToGenerate\n\t\t\t}\n\t\t}()\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTCOUNT-an-undelivered-attempt-counts-as-asking",
|
||
"why": "an attempt whose request never left asked nobody and bought nothing; counting it says the engine bought a generation it did not, in the one line where the generated-versus-billed gap is published — and it is the direction D39.196 п.2а forbids",
|
||
"package": "./internal/llm/",
|
||
"battery": true,
|
||
"run": "TestAnUndeliveredAttemptIsNotCountedAsAsking",
|
||
"edits": [
|
||
{
|
||
"file": "internal/llm/httpllm.go",
|
||
"find": "\t\tif deliveredAttempt(resp, err) {\n\t\t\taskedToGenerate++\n\t\t}\n",
|
||
"replace": "\t\taskedToGenerate++\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTROW-the-estimate-is-not-disclosed",
|
||
"why": "the ratified estimate is charged on ONE condition — that the row says it is an estimate (D39.230 п.1). A silent $0.001056 is indistinguishable from a provider-reported cost, and the owner's word does not apply to a row that does not disclose. The table of nine outcomes cannot see this: its column reads the CHECKPOINT's usage, not request_log.estimated",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheCutRowPublishesItsEstimateAndItsGap",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\trl.Estimated, rl.EstTokens = cost > 0, r.estOutTokens(c.chunk.Text)\n",
|
||
"replace": "\trl.Estimated, rl.EstTokens = false, 0\n"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "CUTROW-the-gap-is-not-published",
|
||
"why": "the provider was asked more than once and ONE estimate is booked for all of them; the row is the only place that under-count is published, and a silent gap is what row 360 was opened about",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheCutRowPublishesItsEstimateAndItsGap",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\t\trl.Err = cutErrLine(err, cut.Deliveries, cost)\n",
|
||
"replace": ""
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "STOP-cut-does-not-blind-an-earlier-verdict",
|
||
"why": "chunk_status is keyed (book, chapter, chunk, stage) and the stop mark is an UPSERT, so a run cut over a position that ALREADY held a verdict replaced it with flagged(cancelled) and an EMPTY final_hash — the only pointer to text a previous run bought and the export ships. Measured before the guard: ok / final_hash=770e5563 / $0.001820 became cancelled / \"\" / $0 while the checkpoint holding the prose stayed on disk, and the export shipped an empty chapter where it had shipped text (backlog row 375). Reachable through the re-pin route; the redrive route cannot reach it, because ResetChunkStages deletes the row and its checkpoints first",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestACutOverAPositionThatAlreadyShippedTextKeepsIt|TestACutOverAVirginPositionStillMarksIt",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/cutcall.go",
|
||
"find": "\t} else if prev != nil && resolvedForResume(prev) {",
|
||
"replace": "\t} else if prev != nil && resolvedForResume(prev) && false {"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "STOP-a-cut-by-a-person-is-not-a-failed-job",
|
||
"why": "a call a person cut was healthy when they cut it and the resume re-does it at the same budget, so `failed` — the read-model's word for a position that BROKE — sends whoever reads the jobs table hunting for a defect that is not there (backlog row 389; this is the site a stop actually travels through, and not one of the two the row names). The control pinned beside it keeps the word where it is true: a socket that dies on its own still records `failed`",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestAStoppedPositionIsNotRecordedAsFailed|TestABrokenSocketIsStillRecordedAsFailed",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/stagerun.go",
|
||
"find": "\tif errors.Is(cause, context.Canceled) {\n\t\treturn\n\t}\n\tr.setJobStatus(ctx, jobID, \"failed\")",
|
||
"replace": "\tr.setJobStatus(ctx, jobID, \"failed\")"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "STOP-the-spend-line-closure-cannot-reach-the-emitter",
|
||
"why": "the spend-line closure runs INSIDE the store's write transaction while the emitter's mutex is held ACROSS store writes, so taking that mutex from the closure closes a lock cycle — and it does not look like a deadlock from outside: the store's 10-second op timeout breaks it, and the symptom is `ensure job: context deadline exceeded` in tests that have nothing to do with the change (measured at thirteen, backlog row 387). The closure captures values so the receiver is not in scope at all; this plant puts it back",
|
||
"package": "./internal/pipeline/",
|
||
"battery": true,
|
||
"run": "TestTheSpendLineClosureCannotReachTheEmitter",
|
||
"edits": [
|
||
{
|
||
"file": "internal/pipeline/events.go",
|
||
"find": "\t\t\tlog.Error(\"could not render the spend event; the settle proceeds without it (the counter is cumulative — the next one carries the total again)\", \"err\", err)",
|
||
"replace": "\t\t\tlog.Error(\"could not render the spend event; the settle proceeds without it (the counter is cumulative — the next one carries the total again)\", \"err\", err, \"run\", e.runID)"
|
||
}
|
||
]
|
||
}
|
||
]
|