fix(806): guard populations over FILES derive from the git index, not a filesystem walk #818

Merged
timothy merged 6 commits from issue-806-guard-populations into main 2026-08-22 18:07:59 +02:00
Owner

fixes #806

testing.guard-derives-population-from-source (#774) says a completeness guard derives its population from an authoritative source, and its worked examples are an enum and the generated OpenAPI document. It was silent on the commonest population in our own guards — files in a directory — and every one of them answered with a filesystem walk. A walk is not authoritative: it reports build output, generated shims and editor droppings, and it differs per machine.

What shipped

Converted (a completeness claim over tracked files): test_guard_inventory.py, test_hook_fire_log.py, test_ci_image_pin_population.py (which also gained *.yaml — Gitea accepts both spellings, so a .yaml workflow adopting the toolchain image was structurally invisible), and test_remote_state_inventory.py (folded onto the shared derivation, so the rule has one implementation).

Assessed and recorded, not silently skipped: _repo_copy in test_ci_release_path_scan_job.py (not a completeness claim, but takes its file list from the index for hermeticity); test_ci_dropped_step_guard.py (no filesystem population at all); the decisions corpus (recorded as unexamined rather than cleared). This is not "replace every glob".

scripts/tests/tracked_files.py is the single derivation. test_guard_populations_derive_from_git.py proves it in two directions that are measured complements — exhaustive removal catches a hardcoded .exists() admit and memoisation; the call-log check catches an append-only source that yields nothing on this machine (#778's shape), which removal cannot see because it has nothing to remove.

Docs: the decision record gains the file-population case; docs/guard-inventory.md carries the per-guard audit table.

Review

Eight rounds of independent cold review, alternating Codex GPT-5.6 and Opus, every one in an isolated worktree from a cold review-only brief. Every round confirmed the four production derivations and the shared helper sound; every blocking finding after round 1 was in the proofs or in prose claims about them, which is worth recording because it says where the risk actually was.

Findings that changed the code, each executed by the reviewer rather than argued:

Round Finding
1 derived_guard_files still admitted untracked files — the CALLERS were index-gated but the paths they NAME were admitted on Path.exists(). #778's third shape, inside the guard this change calls its model
2 The proof removed one victim from a four-contributor union, so a mutant reverting one contributor passed. Now exhaustive — which immediately exposed that _git_ls_files was imported by name, so the patch never reached the ref-gating branch
2 The POPULATION-EXEMPT marker was a whole-file substring scan — a silent kill switch trippable from another file's prose. Withdrawn, not patched
3 The add-side test wrote probe files into the checkout under test: a test_*.py probe is collected by pytest, names collide across concurrent runs, finally doesn't survive SIGKILL. Withdrawn
4 The replacement discarded its own calls evidence on the success path — try/except Exception around an rglob was a false green
5 It also discarded derive()'s return value, so a lazy generator ran after the patch exited
6–8 A sequence of false or imprecise claims in prose: _EmptyScan was iterable but not an iterator (so os.walk still polluted the report); the boundary was stated as "synchronous", then "while the patch is active", both falsified by measurement — it is the call log; glob.glob routes through glob.iglob, not os.scandir; rebase-stale member counts

Two are worth calling out as process, not code:

  • One round caught that a git commit --amend had used only the staged tree, so the manifest entry and prose corrections were never in the commit — it would have pushed red. It found this by exporting git archive HEAD and running that, not the working tree. The committed tree is now verified the same way (via a throwaway worktree, since an archive isn't a git repo — the exact failure _repo_copy's docstring warns about).
  • The boundary sentence was wrong three times, always in the same direction, and the third time it was wrong in the copies after the original had been fixed. The instance list now lives in exactly one place, and the record carries that as its lesson.

Rebase

Rebased across two main moves. #790 ("execute every MUTATION grade instead of asserting it") landed mid-review and touches the same two files — the "green separately, red together" case, and it fired: three tests went red on the rebase. The new guard now declares an executable clause in mutation_manifest.py, using the real defect round 1 found; #790's harness runs it and reddens exactly the named proof for the declared reason. Reviewers mechanically diffed every #790 line to confirm nothing was lost.

Verification

  • 870 passed, 2 skipped — and re-verified against the committed tree, not just the working tree
  • ruff check + format clean; decisions-validate OK; catalog up to date; doc-narrative 9 advisories, all pre-existing
  • Mutations witnessed red on the final tree: append-only rglob with .husky/_ absent; enumeration swallowed by except Exception; a lazy generator; the hardcoded .exists() gate; tracked_children made recursive; the exemption removed; the AST matcher narrowed
  • No two proofs mask each other — each mutant reddens a distinct expected test

Known residuals, stated rather than implied

The call-log check is a regression guard against the shapes that arrive by accident, not a boundary: enumeration hoisted to module import time, from os import listdir early binding, and anything listing in another process all pass. importlib/star-import/re-export are invisible to the AST matcher. Nested workflow directories are out of scope. Each is written down where the code is, once.

fixes #806 `testing.guard-derives-population-from-source` (#774) says a completeness guard derives its population from an authoritative source, and its worked examples are an enum and the generated OpenAPI document. It was silent on the commonest population in our own guards — files in a directory — and every one of them answered with a filesystem walk. A walk is not authoritative: it reports build output, generated shims and editor droppings, and it differs per machine. ## What shipped **Converted** (a completeness claim over tracked files): `test_guard_inventory.py`, `test_hook_fire_log.py`, `test_ci_image_pin_population.py` (which also gained `*.yaml` — Gitea accepts both spellings, so a `.yaml` workflow adopting the toolchain image was structurally invisible), and `test_remote_state_inventory.py` (folded onto the shared derivation, so the rule has one implementation). **Assessed and recorded, not silently skipped**: `_repo_copy` in `test_ci_release_path_scan_job.py` (not a completeness claim, but takes its file list from the index for hermeticity); `test_ci_dropped_step_guard.py` (no filesystem population at all); the decisions corpus (recorded as unexamined rather than cleared). This is not "replace every glob". `scripts/tests/tracked_files.py` is the single derivation. `test_guard_populations_derive_from_git.py` proves it in two directions that are measured complements — exhaustive removal catches a hardcoded `.exists()` admit and memoisation; the call-log check catches an append-only source that yields nothing on this machine (#778's shape), which removal cannot see because it has nothing to remove. Docs: the decision record gains the file-population case; `docs/guard-inventory.md` carries the per-guard audit table. ## Review Eight rounds of independent cold review, alternating Codex GPT-5.6 and Opus, every one in an isolated worktree from a cold review-only brief. Every round confirmed the four production derivations and the shared helper sound; **every blocking finding after round 1 was in the proofs or in prose claims about them**, which is worth recording because it says where the risk actually was. Findings that changed the code, each executed by the reviewer rather than argued: | Round | Finding | |---|---| | 1 | `derived_guard_files` still admitted untracked files — the CALLERS were index-gated but the paths they NAME were admitted on `Path.exists()`. #778's third shape, inside the guard this change calls its model | | 2 | The proof removed one victim from a four-contributor union, so a mutant reverting one contributor passed. Now exhaustive — which immediately exposed that `_git_ls_files` was imported **by name**, so the patch never reached the ref-gating branch | | 2 | The `POPULATION-EXEMPT` marker was a whole-file substring scan — a silent kill switch trippable from another file's prose. Withdrawn, not patched | | 3 | The add-side test wrote probe files into the checkout under test: a `test_*.py` probe is collected by pytest, names collide across concurrent runs, `finally` doesn't survive SIGKILL. Withdrawn | | 4 | The replacement discarded its own `calls` evidence on the success path — `try/except Exception` around an `rglob` was a false green | | 5 | It also discarded `derive()`'s return value, so a lazy generator ran after the patch exited | | 6–8 | A sequence of false or imprecise claims in prose: `_EmptyScan` was iterable but not an iterator (so `os.walk` still polluted the report); the boundary was stated as "synchronous", then "while the patch is active", both falsified by measurement — it is the **call log**; `glob.glob` routes through `glob.iglob`, not `os.scandir`; rebase-stale member counts | Two are worth calling out as process, not code: - One round caught that a `git commit --amend` had used only the **staged** tree, so the manifest entry and prose corrections were never in the commit — it would have pushed red. It found this by exporting `git archive HEAD` and running *that*, not the working tree. The committed tree is now verified the same way (via a throwaway worktree, since an archive isn't a git repo — the exact failure `_repo_copy`'s docstring warns about). - The boundary sentence was wrong three times, always in the same direction, and the third time it was wrong in the *copies* after the original had been fixed. The instance list now lives in exactly one place, and the record carries that as its lesson. ## Rebase Rebased across two `main` moves. #790 ("execute every MUTATION grade instead of asserting it") landed mid-review and touches the same two files — the "green separately, red together" case, and it fired: three tests went red on the rebase. The new guard now declares an executable clause in `mutation_manifest.py`, using the real defect round 1 found; #790's harness runs it and reddens exactly the named proof for the declared reason. Reviewers mechanically diffed every #790 line to confirm nothing was lost. ## Verification - `870 passed, 2 skipped` — and re-verified against the **committed** tree, not just the working tree - ruff check + format clean; `decisions-validate` OK; catalog up to date; doc-narrative 9 advisories, all pre-existing - Mutations witnessed red on the final tree: append-only rglob with `.husky/_` absent; enumeration swallowed by `except Exception`; a lazy generator; the hardcoded `.exists()` gate; `tracked_children` made recursive; the exemption removed; the AST matcher narrowed - No two proofs mask each other — each mutant reddens a distinct expected test ## Known residuals, stated rather than implied The call-log check is a regression guard against the shapes that arrive by accident, not a boundary: enumeration hoisted to module import time, `from os import listdir` early binding, and anything listing in another process all pass. `importlib`/star-import/re-export are invisible to the AST matcher. Nested workflow directories are out of scope. Each is written down where the code is, once.
timothy added 1 commit 2026-08-22 15:50:58 +02:00
fix(806): guard populations over FILES derive from the git index, not a filesystem walk
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 10s
PR Gates / Docs update reminder (pull_request) Successful in 13s
PR Gates / decisions lifecycle (pull_request) Successful in 18s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 11s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 25s
review-verdict/h10 Awaiting review verdict for b0f42f1
Review verdict / Set review-verdict status (pull_request_target) Successful in 10s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 5m38s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m35s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m11s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 5m50s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 5s
b0f42f14a0
`testing.guard-derives-population-from-source` (#774) says a completeness guard
derives its population from an authoritative source, and its worked examples are an
enum and the generated OpenAPI document. It was silent on the commonest population
in our own guards — files in a directory — and every one of them answered with a
filesystem walk. A walk is not an authoritative source: it reports build output,
generated shims and editor droppings, and it differs per machine.

#778 measured the cost by getting the same population wrong three times in one PR
while implementing the milestone that exists to prevent it. The worst shape was
`Path.rglob` enumerating `.husky/_/` — 17 shims `npm ci` writes, gitignored and
untracked — which made that guard RED on every developer checkout and GREEN in CI,
whose `script-tests` job never runs `npm ci`. Only that one guard was fixed then.
This audits the rest.

CONVERTED (a completeness claim over tracked files):
  * `test_guard_inventory.py` — its `.husky/` walk excluded `.husky/_/` only because
    `_` is a directory, so the obvious "make it recursive" edit would have
    reintroduced #778's defect in the repo's own model guard. Both halves are gated
    on the index: the callers, and the `scripts/…` paths they name — the second was
    left on `Path.exists()` in the first cut and found by cold review.
  * `test_hook_fire_log.py` — an untracked scratch `.sh` in `.claude/hooks/` was
    demanded to carry instrumentation.
  * `test_ci_image_pin_population.py` — and `*.yaml` added: Gitea accepts both
    spellings, so a `.yaml` workflow adopting the toolchain image was structurally
    invisible while the test read as covering every workflow.
  * `test_remote_state_inventory.py` — folded onto the shared derivation, so the
    rule has one implementation rather than two.

ASSESSED AND RECORDED, not silently skipped:
  * `test_ci_release_path_scan_job.py::_repo_copy` — not a completeness claim, but
    it takes its file LIST from the index anyway for hermeticity, since `copytree`
    copied untracked files and `__pycache__` into a tree whose behaviour the probes
    measure. Content still comes from the working tree, and the copy is NOT a git
    repo, so neither file that step runs may use the helper — written down because
    `MARKED_JOBS` is left open as a residual gap, which invites editing exactly
    that file.
  * `test_ci_dropped_step_guard.py` — no filesystem population at all; its members
    come from the parsed workflow.
  * The decisions corpus keeps its walks and is recorded as unexamined rather than
    cleared. This is not "replace every glob".

`scripts/tests/tracked_files.py` is the single derivation.
`test_guard_populations_derive_from_git.py` proves it in two directions, which are
complements rather than a second opinion — measured both ways:
  * REMOVAL, exhaustively: every member of every registered derivation is dropped
    from the index in turn and must disappear while still on disk. One victim was
    not enough — `derived_guard_files` unions four contributors, so a mutant putting
    only one back on a walk passed. This catches a hardcoded `.exists()` admit and
    memoisation, which the other direction cannot.
  * The CALL LOG: a derivation may READ a file but must not LIST a directory. This
    catches an append-only source that yields nothing on this machine — #778's
    shape — which removal cannot see, because it has nothing to remove. Its limits
    are stated in one place and are true in both directions.

Both the population and the proofs are registered against a hand-written scope
mirror that carries its own equality check, and the import matcher's 16 forms are
pinned as a table so the next edit cannot silently re-open one.

The mutation this guard declares in `mutation_manifest.py` (#790) is the real
defect cold review found in its own first round.

Docs: `testing.guard-derives-population-from-source` gains the file-population case
and why the disk is not authoritative; `docs/guard-inventory.md` carries the
per-guard audit table, including the two no-change verdicts and the decisions
corpus recorded as unexamined.

Seven rounds of independent cold review (Codex GPT-5.6 and Opus, alternating) —
the per-round findings and their measurements are in the PR.

fixes #806

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
timothy added 1 commit 2026-08-22 16:21:53 +02:00
fix(806): assess test_pr_changed_files.py too, and correct three counts this change invalidated
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 11s
PR Gates / Docs update reminder (pull_request) Successful in 15s
PR Gates / decisions lifecycle (pull_request) Successful in 20s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 12s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 27s
review-verdict/h10 Awaiting review verdict for 0395567
Review verdict / Set review-verdict status (pull_request_target) Successful in 11s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 5m51s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m21s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m58s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Canceled after 5m47s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Canceled after 0s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Canceled after 0s
03955675a8
Two findings from the cold review of the pushed head, one of them the more
interesting kind: a defect in a file the issue did not list.

`test_pr_changed_files.py` DERIVED A WORKFLOW POPULATION FROM `Path.glob`, and it
carries two completeness claims over it — "no OTHER workflow writes the
review-verdict status", and the delimiter ban over every workflow. Cold review
demonstrated it in a disposable clone: one untracked `.yaml` dropped in
`.gitea/workflows/` reddens both guards while absent from `git ls-files`. That is
the developer-red/CI-green shape #806 exists to remove, in a guard that was never
assessed.

It was never assessed because #806's body names four files to check and I took that
list as the population — which is this issue's own defect, one altitude up, in the
work implementing it. My first sweep printed this file among the filesystem-derived
populations and I narrowed to the issue's list without saying so. Both call sites
now come from the index, `_workflow_files` is registered in `DERIVATIONS` so both
proofs cover it, and the audit table in `docs/guard-inventory.md` says plainly that
the issue's list was a starting point rather than the population.

THREE COUNTS IN `mutation-claims-are-executed.md` WERE INVALIDATED BY THIS CHANGE
and not updated: it said thirteen manifest entries, ten behavioural reds, and twelve
of thirteen clause-provable. Adding a fourteenth entry makes those fourteen, eleven,
and thirteen of fourteen. The record is where `test_mutation_harness.py`'s own
docstring sends a reader for that measurement, and the same commit corrected the
identical sentence in `docs/guard-inventory.md` — so the tree shipped two
same-dated documents contradicting each other on one number. Swept by subject this
time rather than by the phrase I had been shown; no other record is stale.

Also:
  * `docs/guard-inventory.md` still said "what falls outside that window" one
    sentence after arguing the boundary is not a window.
  * the same file's "one implementation rather than five" read as contradicting the
    commit message's "rather than two"; both are true at different scopes and the
    sentence now says which it means.

refs #806

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
timothy added 1 commit 2026-08-22 16:45:38 +02:00
fix(806): make the drifting counts count-free instead of correcting them again
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 19s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 20s
PR Gates / decisions lifecycle (pull_request) Successful in 22s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 15s
PR Gates / Docs update reminder (pull_request) Successful in 31s
review-verdict/h10 Awaiting review verdict for 10dba08
Review verdict / Set review-verdict status (pull_request_target) Successful in 39s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 4m38s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m32s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m58s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 5m52s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Canceled after 1s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Canceled after 0s
10dba0892e
Cold review of the previous commit found that IT had made
`docs/guard-inventory.md`'s "load-bearing for the six modules that import it"
false, by adding a seventh importer — in the file it was editing, inside the sweep
its own message claims to have done by subject. That is the third hand-maintained
count in this change to go stale, after `mutation-claims-are-executed.md`'s three
and `guard-inventory.md`'s "three of the thirteen".

Correcting a number that tracks a growing population buys one commit of accuracy.
So the counts that track populations are gone rather than updated:

  * "load-bearing for the six modules that import it" → "for every module that
    imports it"
  * "this file is not four near-copies" → "not one near-copy per derivation"
  * "adding a fifth derivation / a fifth index-derived guard" (twice) → "another"
  * "five guard modules would otherwise die" → "every module that imports this one"
  * "Exhaustive removal is cheap (~1s for 60 members)" → "about a second at the
    population sizes here"

The sweep was then done MECHANICALLY — a regex over every spelled-out and numeric
count in all five artifacts this change touches — rather than by eye for a fourth
time. What survives is verified true rather than assumed: "three of the fourteen"
and "36 guards … 14/6/16" (the latter machine-asserted by
`test_the_summary_counts_match_the_table`), "unions four contributors",
"five modules each derived a file population" (`DERIVATIONS` holds exactly five),
and the re-measured 61/39/48/58. `guard-inventory.md`'s "three of these four rows"
is #790's text about a different row set and is untouched.

The figures that remain are the ones that describe a FIXED structure or carry a
reproduction command; the ones that counted a set expected to grow are the ones
that kept breaking, which is the distinction the sweep now encodes.

Correction to the previous commit message: it called the second converted call site
"the delimiter ban over every workflow". That guard is
`test_every_workflow_expression_names_a_REAL_context_or_function`, an
expression-context check; the delimiter ban does not use `_workflow_files()`. Both
call sites are completeness claims over every workflow and both were demonstrated
red against an untracked `.yaml` at the predecessor sha — only the label was wrong.

refs #806

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
timothy added 1 commit 2026-08-22 17:06:13 +02:00
fix(806): finish the count sweep the previous message claimed, and name the pointer's file
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 9s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 18s
PR Gates / decisions lifecycle (pull_request) Successful in 15s
PR Gates / Docs update reminder (pull_request) Successful in 14s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 13s
review-verdict/h10 Awaiting review verdict for a92df75
Review verdict / Set review-verdict status (pull_request_target) Successful in 23s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 5m45s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m29s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m3s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Canceled after 2m2s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Canceled after 0s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Canceled after 0s
a92df7515a
Cold review returned MERGEABLE with two notes; both are corrections to claims this
change ships, so both are taken.

THE PREVIOUS COMMIT CLAIMED A REGEX OVER EVERY COUNT IN ALL FIVE ARTIFACTS and left
four. They were dated and true, which is why they read as acceptable, but a single
added `MUTATION` row stales all four at once — the same fuse as the counts already
removed, and the claim of a complete sweep is what made keeping them dishonest
rather than merely conservative:

  * "three of the fourteen entries" → "three entries" (it names them; the total
    contributes nothing)
  * "the remaining eleven redden behaviourally" → "every other entry reddens
    behaviourally"
  * "thirteen of the fourteen admit clause-level proof" → "all but one"
  * `guard-inventory.md`'s "three of the fourteen rows" → "three rows"

An independent sweep with a wider pattern now returns only counts over FIXED
structures — #790's four ranked rows, `derived_guard_files`'s four union terms,
#685's two guards — plus the machine-asserted `36 guards … 14/6/16`, and the
member figures, which are dated, carry their reproduction command, and say in place
that they illustrate the gap above the floor rather than being values to assert
against. That is the distinction worth keeping: a number describing a fixed shape,
or one that ships with the command that regenerates it, does not rot the way a
tally of a growing set does.

`docs/guard-inventory.md`'s new "listed in `DERIVATIONS` there" had the wrong
nearest antecedent — `DERIVATIONS` lives in
`scripts/tests/test_guard_populations_derive_from_git.py`, not in
`tracked_files.py`. Named explicitly.

refs #806

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
timothy added 1 commit 2026-08-22 17:23:19 +02:00
docs(806): a duplicated article, in the sentence the previous commit rewrote
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 9s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 19s
PR Gates / Docs update reminder (pull_request) Successful in 25s
PR Gates / decisions lifecycle (pull_request) Successful in 28s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 20s
review-verdict/h10 Awaiting review verdict for 9088556
Review verdict / Set review-verdict status (pull_request_target) Successful in 19s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 5m42s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m58s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m0s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 5s
908855644a
`docs/guard-inventory.md` shipped "one per module. The The registered derivations
are listed…". The rewrite replaced the line that began "The registered
derivations…" without removing the trailing `The` the previous line carried as that
sentence's wrapped start. Markdown soft-breaks the two lines, so the published page
showed both.

Worth a line rather than a silent fix: the evidence was on screen. The `sed` output
I used to verify that rewrite printed line 140's trailing `The` directly above line
141's `The registered` and I read past it — a verification that ran, produced the
right output, and was not actually read. Cold review caught it.

Swept for the same shape across the three prose artifacts this change touches, over
JOINED lines so a duplication split across a wrap is visible: no others.

One clarification to the previous message, which enumerated what the count sweep
returns and reads as exhaustive: that enumeration covers the prose this branch
authors. `guard-derives-population-from-source.md` also carries "the 95 closed
issues carrying a `## Closing record`" and "27 of the 69 process-failure records"
— counts over growing sets, but pre-existing (#774/#775) and attributed citations
of #773's analysis rather than live tallies, so they are left alone.

refs #806

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
timothy added 1 commit 2026-08-22 17:44:27 +02:00
docs(806): record the SPA page-size guard as assessed-and-deferred (#819)
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 7s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 18s
PR Gates / Docs update reminder (pull_request) Successful in 20s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 21s
PR Gates / decisions lifecycle (pull_request) Successful in 16s
Review verdict / Set review-verdict status (pull_request_target) Successful in 33s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 5m37s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m38s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m56s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m2s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 6s
review-verdict/h10 Review-verdict: MERGEABLE @ ded6cb1 (base: main)
ded6cb197c
The whole-repo sweep in cold review — looking past the file list #806 names, the
same method that found `test_pr_changed_files.py` inside this PR — turned up one
more instance of the shape: `web/src/api/pageSizeCallSites.guard.test.ts` derives
its population from `import.meta.glob('/src/**/*.{ts,tsx,mts,cts}')` and then
asserts exact completeness against `REGISTRY`. An untracked `.ts` under `web/src/`
containing a page-size call enters the population and fails as unregistered on that
checkout while CI never sees it.

NOT FIXED HERE, and the reason is a documented obstacle rather than scope fatigue.
The glob is a deliberate workaround: `@types/node` is kept out of
`tsconfig.app.json` because that project covers production browser code, and a
file-local `/// <reference types="node" />` was tried and REVERTED — under `tsc -b`
it leaked Node's ambient `setTimeout` into the whole app project and broke three
unrelated `window.setTimeout` mocks. Filtering the glob against `git ls-files`
therefore needs `node:child_process` in a project that excludes Node types, which is
the wall the previous attempt hit. #819 carries the evidence and two fix shapes that
sidestep it (a Vite `define` or a vitest `globalSetup`, both of which run where Node
IS available).

The direction is the safe one — it over-enumerates, so it fails loudly rather than
going blind — which is why it is a follow-up rather than a blocker.

Recorded in the audit table as DEFERRED rather than left silent, which is what #806
asks for every population it assesses: "completeness claim over tracked files, or
not — recorded either way". A guard nobody wrote down is indistinguishable from one
nobody looked at.

refs #806
refs #819

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Owner

Review-verdict: MERGEABLE @ ded6cb1

Twelve rounds of independent cold review (Codex GPT-5.6 + Opus alternating, isolated worktrees, cold briefs). Production derivations and the shared helper confirmed sound every round; final two rounds MERGEABLE on this head. CI fully green: 12 jobs, 1 skipped, no failures, no cancellations. One further instance of the shape found by whole-repo sweep and deferred to #819 with the obstacle documented.

Review-verdict: MERGEABLE @ ded6cb1 Twelve rounds of independent cold review (Codex GPT-5.6 + Opus alternating, isolated worktrees, cold briefs). Production derivations and the shared helper confirmed sound every round; final two rounds MERGEABLE on this head. CI fully green: 12 jobs, 1 skipped, no failures, no cancellations. One further instance of the shape found by whole-repo sweep and deferred to #819 with the obstacle documented.
timothy merged commit 29d7a06e65 into main 2026-08-22 18:07:59 +02:00
Sign in to join this conversation.