diff --git a/.dev-loop/INGEST_REPORT.md b/.dev-loop/INGEST_REPORT.md index 55ccfd1..ce27936 100644 --- a/.dev-loop/INGEST_REPORT.md +++ b/.dev-loop/INGEST_REPORT.md @@ -1,53 +1,247 @@ -# Knowledge consolidation — 15 open PRs (#17–#40) → one reconciled state +# Knowledge flush — 3 insight(s) -The 15 open `knowledge/*` PRs (created 2026-08-04 → 2026-08-05, before the -harvest processed-store dedupe fix in #41) contained 123 file-versions of ~75 -unique pages, with the same insight landing at up to 3 different paths across -up to 8 PRs. Per-PR review would re-import those duplicates, so — as with the -#6–#13 consolidation — this branch carries the reconciled end-state and the 15 -PRs are closed in its favor. +Drained the 3 pending rows in `~/.dev-loop/queue/` (session files `5d6b4056`, +`cea5f63a`, `ef805210`; the other 10 queue files were already empty). All three +carried `domain: testing`. None was dropped. + +Cross-Check: independent adversarial review (claude CLI headless, `--permission-mode plan`) returned BLOCK on the first commit with 17 findings — 2 critical, 9 major, 6 minor; all were accepted and fixed in the second commit, the most serious being a source miscitation inherited from an existing page and two `verified` confidences downgraded to `field-tested`. + +**Confidence after review:** `unasserted-return-fields` = `verified`; +`value-preserving-refactor-assertions` and `stale-artifact-baselines` = +`field-tested`. The first commit marked all three `verified`; the review showed +that for two of them the cited docs support only the background mechanism, not the +central directive, so they were downgraded rather than defended. ## Verified best-practice -Every adopted page's sources were carried from its originating PR's flush, where -they were live-verified at flush time; no new URLs were introduced during -consolidation (checked mechanically: every `http(s)` URL in every merged page -appears in a source PR's diff; every added body line in amended pages traces to -a source PR hunk — orphan-line verification). Confidence fields were kept as the -originating flushes set them, except client-side-rate-limiting where the union -of provider-doc citations (Okta, Auth0, GitHub, OpenAI, RFC 6585) supports -`verified` for the load-bearing claims. One subagent's fabricated content (12 -files matching neither main nor any PR, with invented source URLs) was detected -by the same verification and replaced with true PR content. +**1. A value-preserving literal→SSOT refactor cannot be guarded by an assertion that holds the config fixed → `field-tested`.** +Claim: when the config value renders byte-identical to the literal you removed, +every assertion that holds the config fixed passes on the reverted-literal version +too; the separating input is the config value itself, so the test must vary it +through a seam a caller or operator reaches. +Sources opened: [pytest monkeypatch](https://docs.pytest.org/en/stable/how-to/monkeypatch.html) +("All modifications will be undone after the requesting test function or fixture +has finished"; `monkeypatch.context()` applies patches "only in a specific scope"), +[unittest.mock](https://docs.python.org/3/library/unittest.mock.html) ("you must +ensure that you patch the name used by the system under test"), +[Stryker equivalent-mutants](https://stryker-mutator.io/docs/mutation-testing-elements/equivalent-mutants/), +[PIT basic concepts](https://pitest.org/quickstart/basic_concepts/). +What the review changed here, and why it matters: the first commit called this "the +equivalent-mutant condition" and cited Stryker/PIT as its mechanism. That was +self-refuting — an equivalent mutant is one *no input* separates, while this page +then tells you to separate the versions by varying the config. The two are now +cited as a near analogy with the difference stated explicitly. Three further +corrections: the `try/finally` rationale was factually wrong (a `finally` block does +run on assertion failure, so the honest reasons are "no restore code to review" and +"covers a patch applied in a fixture whose test body never runs"); the +wrong-patch-site failure mode is a *misleading red*, not a silent green (the +sentinel assertion fails on correct code); and the presence/absence halves were +described backwards — presence is the discriminator, absence is a supplement. +Confidence is `field-tested` because no cited document supports the central +directive; the only evidence for it is one in-house reproduction (manday renderer, +2026-08-05: `"DB ×%s" % E.DB_MULT` rendered exactly the removed literal `DB ×1.3`, +computed before the test was written; only the sentinel form reddened on the +reverted version). + +**2. A composite return's unread fields are unguarded, and cross-field invariants need their own assertion → `verified`.** +Claim: diff the returned field list against the fields assertions mention, confirm +each absence with a per-field mutation plus the harness no-op control, then assert +the relations binding the fields across the input grid. +Sources opened: [arXiv:2211.12003](https://arxiv.org/abs/2211.12003) (Alzahrani, +Spichkova, Harland, *Application of property-based testing tools for metamorphic +testing*) — "The core concept in MT is metamorphic relations (MRs) which provide +formal specification of the system under test"; +[hypothesis.works](https://hypothesis.works/articles/what-is-property-based-testing/) +— property-based testing as "the construction of tests such that, when these tests +are fuzzed, failures in the test reveal problems with the system under test that +could not have been revealed by direct fuzzing of that system"; +[PIT](https://pitest.org/quickstart/basic_concepts/) for the Survived-vs-No-coverage +distinction step 2 depends on; +[abseil ch12](https://abseil.io/resources/swe-book/html/ch12.html). +This page keeps `verified`: the two sources above back its central directive, and +its field reproduction carries measured numbers (58 passing assertions over a +`lo`/`sp`/`hi` return in which `lo` and `hi` appeared in none of them; four formula +mutations left all 58 green while the no-op control survived, so the harness +discriminated; the invariant over the full discrete grid found 13 combinations with +`sp > hi`). +Honest limit: Hypothesis's own `readthedocs` quickstart does not contain a +definitional "property/invariant" statement — I fetched it, found none, and cited +the maintainers' article rather than inventing a docs quote. + +**3. A previously published artifact needs its generation dated and a row-level diff before it is a baseline → `field-tested`.** +Claim: matching aggregates do not establish matching rows; date the artifact's +generation from its schema fields, and rebuild the before side by reverting only the +change under measurement when the generation differs. +Sources opened: [SLSA v1.0 provenance](https://slsa.dev/spec/v1.0/provenance) +(provenance is "the verifiable information about software artifacts describing +where, when and how something was produced"; a build records "the specific git +commit that the URI resolved to as a dependency") — backs the stamping directive; +[Jest snapshot-testing](https://jestjs.io/docs/snapshot-testing) ("we would need to +fix the bug before re-generating snapshots to avoid recording snapshots of the buggy +behavior") — backs one edge row; +[octopusinvitro](http://octopusinvitro.gitlab.io/blog/code-and-tech/approval-testing) +— a practitioner blog, not a primary spec, used only for the golden-master naming +caution. +What the review changed here: the first commit explained the failure with +non-injectivity of summation, which describes *cancelling* summands — but the field +evidence is a rollup parent **excluded from the total**, which was never a summand, +so no cancellation occurred. An engineer following the stated reason would hunt for +offsetting deltas, find none, and wrongly trust the total. The page now enumerates +three mechanisms and leads with exclusion (the one requiring no coincidence). The +generation-dating heuristic was also split: an *absent* field dates the file before +the field existed, a *present-but-empty* field dates it before the writer populated +it — two different conclusions the first commit conflated. +Confidence is `field-tested`: the three central directives (dating by schema field, +row-level full match, in-memory single-revert rebuild) cite nothing and rest on one +in-house reproduction (manday engine, 2026-08-05: counted-SP total 211.48 matched +HEAD exactly while the row diff found NEWRTB-2182 differing — 분석조사/0.19 vs +인프라/0.56 — with `dead: None` dating the file before the classifier's lookbehind +change, and the issue being a rollup parent excluded from the counted total). + +**Retraction.** The first commit's report asserted "Every URL cited below and on the +pages was opened in this session — no citation is carried over on trust." That was +false. Two of my new pages attributed the reachability–infection–propagation (RIP) +fault-detection model to +`https://stryker-mutator.io/docs/mutation-testing-elements/mutant-states-and-metrics/`. +I had not opened that URL; I copied the attribution from an existing wiki page. The +cross-check flagged it and I then fetched the page: it contains the mutant-state set +and the metric formulas and says nothing about reachability, infection, propagation, +or RIP. Both citations are removed from my pages (the Stryker URL is retained on +`unasserted-return-fields` for the mutant states it does document, and dropped +entirely from `stale-artifact-baselines`). + +**Pre-existing defect flagged, not silently edited.** The same miscitation exists in +`wiki/testing/quality/differential-run-agreement.md:92` ("reachability, infection, +and propagation (RIP) model for fault detection") and its directives at `:60` and +`:83` rest on it. That page is not mine to rewrite in this flush — the RIP model is +real but belongs to Ammann & Offutt, *Introduction to Software Testing*, not to +Stryker's docs. Flagged here and in `log.md` for the owner. ## Existing-layer check -- Merged-main near-dup scan before consolidation: pairwise Jaccard over - title + "When this applies" across all 141 merged pages → **0 flagged pairs**; - previously merged content carries no duplication. -- Cross-PR dedup during consolidation: 10 duplicate clusters collapsed to one - canonical page each (rate limiting 8→1, call-site enumeration 7→folded into - the canonical merged in #20, stderr/exit-0 diagnostics 4→1, sysroot 2→1, - env-off-switch 2→1, completion predicates 2→1, robots.txt 2→1, - harness-mediated results 2→1, leaked artifacts 2→1, orchestration category - naming unified). Three near-pairs kept distinct after trigger comparison, - with mutual `related:` links (differential setup vs interpretation; expansion - semantics vs off-switch design; import-time tactics vs level choice). -- 24 existing pages received union-merged amendments; additions already present - in main (from #16/#20) were skipped, and all non-canonical `related:` ids - were remapped to canonical page ids (post-merge broken-link scan: 0). +Pages read: testing-quality-tests-that-cannot-fail, testing-quality-minimum-case-set, testing-quality-harness-reverse-controls, testing-quality-differential-run-agreement, testing-quality-behavior-not-implementation, testing-quality-guard-shape-vs-consequence + +Read in full: the first five. `guard-shape-vs-consequence` was read as its PR #47 +hunks plus its index line (partial — stated rather than implied). Screened by their +`load when` lines in `wiki/testing/index.md` without opening the body, having found +no trigger overlap: completion-predicates, injected-clock-duration-assertions, +write-path-assertions, checks-that-cannot-pass, spec-artifact-checks, +schema-additions-under-a-golden-gate. I also read `wiki/qa/index.md` end to end to +test whether insight 3 belonged in `qa` rather than `testing` (see Routing). + +**Overlaps found, and merge-vs-create.** `tests-that-cannot-fail` owns the general +rule all three insights descend from ("a test proves something only if it can +fail"), but its trigger is retrospective — reviewing an always-green suite. All +three candidates trigger at authoring time on a specific shape. The house already +resolves this shape as *dedicated page + pointer row* (`write-path-assertions`, +`injected-clock-duration-assertions`, `schema-additions-under-a-golden-gate` are all +narrower cases of the same page, each linked from its Edge cases), so I followed +that precedent rather than growing a page already at 105 body lines. Merged instead +of duplicated: `tests-that-cannot-fail` +3 Edge rows, `minimum-case-set` +1 Edge row +(its "assert an observable outcome" is underspecified when the outcome is a +composite — the fields *and* their invariants), `differential-run-agreement` +1 Edge +row (its subject is two live runs; a stale artifact as one side is the adjacent +case). + +**Conflict found, and the resolution tightened after review.** +`behavior-not-implementation` step 2 asserts "a refactor that preserves behavior +keeps every test green", which pulls against insight 1's test — one that goes red +when someone re-inlines a literal, a change with identical output. I resolved it +with a condition-dependent Edge row on *both* pages rather than overwriting either. +The cross-check then attacked my first wording as a rationalization, correctly: it +justified the test by "configurability is behavior … through the seam an operator +controls", while another edge row extended the same test to a *module-local* +constant no operator can set — where re-inlining removes no observable capability +and the assertion becomes exactly what `behavior-not-implementation:41` orders +deleted. Generalized, that would let any implementation detail be relabelled a +capability and would quietly remove step 2's diagnostic force. Both rows now carry +the boundary: the value must be settable through an interface a caller or operator +reaches **without editing source** (config file, env var, DI parameter, CLI flag); +when it is not, step 2 stands unchanged and the guard moves to a static check. The +module-local edge row was rewritten to route there instead of asserting the +substitution test "still applies". That page's step 2 text is untouched, and its +`last_verified` was returned to `2026-07-10` — I added an edge row without +re-opening its three sources, so bumping the date would have claimed a verification +I did not do. + +**Related links** are now genuinely bidirectional for all five adjacent pages (the +first commit left two one-way: `minimum-case-set` → value-preserving and +`harness-reverse-controls` → stale-artifact-baselines; both back-links added). +One-way references remain, by design, to `checks-that-cannot-pass`, +`write-path-assertions` and `backend-common-change-impact-call-site-enumeration`. + +Verified programmatically: every `related:` id and inline `[page-id]` reference +**repo-wide** resolves; all three new pages are listed in `wiki/testing/index.md`; +every touched page's body is under the 120-line limit (max 105); template sections +and frontmatter keys present; no banned vague qualifier in a directive sentence (one +"usually" was caught and rewritten as the condition that decides it). The checker +was itself controlled — injecting a bogus `related:` id and a banned qualifier made +it report both, and it returned to PASS after restore. Note the limit that mattered: +that checker validates link *resolution*, never whether a source says what a page +claims, which is why the RIP miscitation above needed the independent review to +surface. + +## Open-PR check + +`gh pr list --repo choiyounggi/dev-loop --state open` → **#47** +(`knowledge/dch0202-20260806-130040`, label `dev-loop:knowledge`) and #48 +(`feat/tmux-coordinator-gaps`, code not wiki). I fetched #47's head and diffed +`origin/main...origin/knowledge/dch0202-20260806-130040 -- wiki/` in full. + +| Candidate | Verdict | Basis | +|---|---|---| +| 1. value-preserving refactor assertions | **new** | #47's only `tests-that-cannot-fail` change is a bats/bash-3.2 `[[ ]]` row plus three sources; no overlap of trigger or directive | +| 2. unasserted return fields | **new** | #47 touches no page about return-value coverage or invariants | +| 3. stale artifact baselines | **new** | #47 touches no page about baselines, snapshots, or impact measurement | + +Textual adjacency handled rather than ignored: #47 also edits +`wiki/testing/quality/tests-that-cannot-fail.md` and `wiki/testing/index.md`, the two +files this flush edits too. My edits were placed away from its hunks (Edge-cases +table and the `related:` line; three *appended* index rows below the last row it +touches), and where we both bump `last_verified` on `tests-that-cannot-fail` I set +the identical value it sets (`2026-08-06`, today) so the two branches converge +instead of conflicting. Whichever merges second should still be re-read at merge +time — the claim here is that no hunk overlaps, not that git is guaranteed silent. ## Routing decision -- New categories: `infrastructure/agent-orchestration` (5 pages; unified the - competing `orchestration`/`agent-orchestration` names), `databases/data-survey` - (1), `qa/deliverables` (1). All other pages route into existing categories. -- Canonical-path decisions: rate limiting → `backend/common/reliability/` - (sits beside timeouts-and-retries; 6 of 8 variants chose it); stderr - diagnostics → `platforms/processes/` (concern spans beyond shells); leaked - artifacts → `testing/data/artifact-leakage-from-a-suite`; call-site - enumeration → the existing `backend/common/change-impact/` page. -- All 38 new pages listed in their domain indexes (nearest-index rule; backend - routes via its python sub-index for bytecode-cache-staleness); INDEX.md domain - summaries updated for infrastructure/qa/databases. Full-wiki lint: frontmatter, - ids, related-links, index coverage, size, qualifiers, staleness → 0 findings. +`INDEX.md` routes all three to **testing** ("writing or structuring automated +tests: level choice, cases/assertions…"), and within it to the existing +**quality** category. No new category: quality already holds the +"is this assertion capable of failing / may I cite this verdict" family +(`tests-that-cannot-fail`, `harness-reverse-controls`, `differential-run-agreement`, +`spec-artifact-checks`), which is precisely what all three are. + +| Insight | Target | Page | +|---|---|---| +| 1 | `testing/quality` (new page) | `value-preserving-refactor-assertions.md` — `testing-quality-value-preserving-refactor-assertions` | +| 2 | `testing/quality` (new page) | `unasserted-return-fields.md` — `testing-quality-unasserted-return-fields` | +| 3 | `testing/quality` (new page) | `stale-artifact-baselines.md` — `testing-quality-stale-artifact-baselines` | + +Rejected alternatives, with the reason each was rejected: +- **Insight 2 → merge into `minimum-case-set`**: that page selects cases over the + *input* space (normal/error/boundary per behavior); insight 2 is coverage of the + *output* shape and the relations inside it. Distinct axis, so it became a page and + the two are cross-linked — the same split the house already made for + `write-path-assertions`. +- **Insight 3 → `qa` domain**: I read `wiki/qa/index.md` in full. `qa` owns + release-process quality (gates, regression scope, deliverable documents) and + explicitly sends automated-test-code concerns to `testing/`. Insight 3 is about + whether a comparison is valid evidence, which is what `testing/quality` already + hosts for non-test-code artifacts (`differential-run-agreement`, + `harness-reverse-controls` govern reports and PR bodies, not only suites). Routed + to `testing/quality` for that precedent, with a `related:` link to + `qa-deliverables-generated-artifacts-as-deliverable-source`. +- **All three → Edge rows on `tests-that-cannot-fail`**: rejected on the page's own + constraint. It is at 105 body lines against a 120 limit and #47 adds to it; three + full cases would push it over and bury three distinct triggers inside a page whose + routing line is retrospective auditing. + +## Reviewing this PR + +The two `field-tested` pages are the ones to read hardest: their central directives +rest on single in-house reproductions, described on each page, and are the parts no +external source backs. If you would rather not carry a page at that confidence, +`stale-artifact-baselines` is the most self-contained one to drop — the other two are +cross-linked from four existing pages. The `differential-run-agreement:92` RIP +miscitation is pre-existing and left for you to decide on. diff --git a/log.md b/log.md index c930fc2..2864ab2 100644 --- a/log.md +++ b/log.md @@ -43,3 +43,5 @@ Append-only. Format: `## [YYYY-MM-DD] hi` diff --git a/wiki/testing/quality/value-preserving-refactor-assertions.md b/wiki/testing/quality/value-preserving-refactor-assertions.md new file mode 100644 index 0000000..efa92c5 --- /dev/null +++ b/wiki/testing/quality/value-preserving-refactor-assertions.md @@ -0,0 +1,109 @@ +--- +id: testing-quality-value-preserving-refactor-assertions +domain: testing +category: quality +applies_to: [general] +confidence: field-tested +sources: + - https://docs.pytest.org/en/stable/how-to/monkeypatch.html + - https://docs.python.org/3/library/unittest.mock.html + - https://stryker-mutator.io/docs/mutation-testing-elements/equivalent-mutants/ + - https://pitest.org/quickstart/basic_concepts/ +last_verified: 2026-08-06 +related: [testing-quality-tests-that-cannot-fail, testing-quality-harness-reverse-controls, testing-quality-behavior-not-implementation, testing-quality-minimum-case-set, testing-quality-unasserted-return-fields, testing-quality-checks-that-cannot-pass, backend-common-change-impact-call-site-enumeration] +--- + +# Regression Tests for a Value-Preserving Refactor + +## When this applies + +You replaced a hardcoded literal with a read from config, a constants module, or +another single source of truth (`"DB ×1.3"` → `"DB ×%s" % E.DB_MULT`), the +current config holds the same value the literal did, and you are adding the test +that stops the literal from coming back. Also when reviewing such a test. + +## Do this + +1. **Render the output under the current config and compare it byte-for-byte + with the literal you removed, before writing any assertion.** When the two + strings are equal, the refactored and the reverted-literal versions produce + identical output *for that config value*, so every assertion that holds the + config fixed passes on both. The separating input exists — it is the config + value itself — and the test's job is to vary it. + +2. **Choose the assertion target from that comparison:** + +| Byte comparison of output vs. removed literal | Assert | +|---|---| +| Differs (the refactor changed the rendering) | The new output directly; the natural assertion already discriminates | +| Identical, and the value is settable through an interface a caller or operator reaches without editing source | The *dependency*: substitute the source value with a sentinel no literal would produce, then require the output to carry the sentinel | +| Identical, and the value is only reachable by editing source (module-local constant, build-time inlining) | Nothing at unit level — the reversion is invisible to any test that respects the public interface; put the guard in a static check (item 7) | + +3. **Pick the sentinel so that the reverted code fails loudly.** Use a value + outside the plausible range and distinct in rendering (`DB_MULT = 9.9` when + production is `1.3`), then assert the rendered sentinel (`"DB ×9.9"`) is + present. On the reverted version the output still reads `DB ×1.3`, so the + presence assertion is what fails — it is the discriminator. Add "the + production literal is absent" as a second assertion when the renderer could + emit both values; it is a supplement, not the detector. + +4. **Substitute through the seam the caller or operator uses**, not a private + field: the config object, the environment variable, the injected parameter, + the CLI flag. That keeps the assertion a behavior claim — *the output follows + the configured value* — which is why it does not conflict with + [testing-quality-behavior-not-implementation]; see its own edge row for the + boundary. + +5. **Restore the value with the runner's scoped patcher rather than a + hand-rolled `try/finally`.** pytest's `monkeypatch` states that "All + modifications will be undone after the requesting test function or fixture + has finished", and `monkeypatch.context()` applies a patch "only in a + specific scope". Both forms restore on assertion failure, so pick the fixture + for the reasons that do differ: there is no restore code to review, it undoes + patches applied in a *fixture* whose test body never runs, and it composes + with fixture teardown ordering instead of competing with it. + +6. **Patch the name the renderer looks up, not the name where the value is + defined.** `patch()` "works by (temporarily) changing the object that a + *name* points to", so "you must ensure that you patch the name used by the + system under test". When a module copied the constant into a local at import + time it never reads the substitution, and the sentinel assertion then fails + *on correct code* — read that red as a wrong patch site, not as a caught + regression, and move the patch to the name the renderer reads. + +7. **Know all three detectors and pick by reachability of the value:** + +| Detector | Use when | +|---|---| +| Sentinel substitution asserted in a test | The value is settable through a caller- or operator-reachable interface | +| A red control run against a copy with the literal restored | Once, to prove whichever assertion you wrote can fail at all ([testing-quality-tests-that-cannot-fail] for the restore mechanics) | +| A static check that greps for the literal outside the SSOT | The value is module-local or inlined at build time, so no test can observe the difference ([testing-quality-checks-that-cannot-pass] for authoring that gate) | + +## Edge cases + +| Case | Then | +|------|------| +| The constant is one of several the output renders | Substitute one constant per test so a red run names which read regressed; a single test substituting all of them cannot localize the reversion | +| The value is a float and the rendering rounds it | Pick a sentinel that survives the rounding (differs in a kept digit), and assert the rendered form rather than the raw value | +| The refactor moved the literal into the same module's own constant, no operator can set it | Take the static-check row of item 7 — asserting a substitution of a module-local private is an implementation-detail assertion, and re-inlining removes no capability a caller can observe | +| The config value legitimately equals a magic number the output already contains for another reason | Assert the sentinel-substituted output only, and drop the "production literal absent" supplement — it would match the unrelated occurrence and redden on correct code | +| Several call sites were meant to switch to the SSOT and you tested one | Enumerate the call sites and give each its own substitution assertion — a passing test on one site says nothing about the others ([backend-common-change-impact-call-site-enumeration]) | +| The sentinel assertion is red on code you believe is correct | Check the patch site first (item 6) — a name the renderer never reads produces exactly this red | + +## Instead of + +| If you are about to | Do this instead | Why | +|---------------------|-----------------|-----| +| Assert the rendered text (`assert "DB ×1.3" in out`) as the refactor's regression test | Substitute the value with a sentinel through its public seam and assert the output follows | The rendered text is identical for the shipped config value, so the assertion passes on the reverted literal too — it guards nothing it was written to guard | +| Read a green run plus coverage of the changed line as proof the SSOT read is enforced | Require a red run against a copy that has the literal restored | Holding the config fixed makes the two versions indistinguishable by construction; only varying it, or a red control, discriminates | +| Wrap the substitution in `try/finally` to restore the value | Use the runner's scoped patch fixture (`monkeypatch`, `patch` as a context manager) | Both restore on failure, but the fixture leaves no restore code to review and also covers a patch applied in a fixture whose test never runs | +| Assert a substitution of a module-local constant to guard re-inlining | Put that guard in a static check for the literal outside the SSOT | A value no caller or operator can set is an implementation detail; a test asserting it fails refactors and detects no behavior change | +| Skip the byte comparison and write whichever assertion looks natural | Compute the comparison first and let its result choose the assertion target | The comparison is what tells you whether the natural assertion can fail at all | + +## Sources + +- https://docs.pytest.org/en/stable/how-to/monkeypatch.html — "All modifications will be undone after the requesting test function or fixture has finished"; `monkeypatch.context()` applies patches "only in a specific scope" +- https://docs.python.org/3/library/unittest.mock.html — "Where to patch": `patch()` "works by (temporarily) changing the object that a *name* points to with another one … you must ensure that you patch the name used by the system under test" +- https://stryker-mutator.io/docs/mutation-testing-elements/equivalent-mutants/ — cited as the near analogy, not as this page's mechanism: for a mutant that leaves output identical, "There is no definitive way for Stryker to find and ignore them" and "the only solution is by finding these by hand". A value-preserving refactor differs in one decisive way — the config value is an input that *does* separate the two versions, which is what makes a test possible here +- https://pitest.org/quickstart/basic_concepts/ — the same analogy from PIT: an equivalent mutation is one whose result "behaves in exactly the same way as the original" +- Field reproduction 2026-08-05 (manday report renderer): the removed literal `DB ×1.3` and `"DB ×%s" % E.DB_MULT` under the shipped config rendered byte-identical, computed before writing the test — so `assert "DB ×1.3" in out` passed on both the SSOT version and the restored-literal version. Substituting `E.DB_MULT` with a sentinel and asserting the rendered sentinel was the form that reddened on the literal version