Skip to content

Judge the files the build reads values out of, which no row did - #221

Merged
iderex merged 1 commit into
mainfrom
tests/the-build-input-population-leaves-out-the-files-the-build-reads
Aug 31, 2026
Merged

Judge the files the build reads values out of, which no row did#221
iderex merged 1 commit into
mainfrom
tests/the-build-input-population-leaves-out-the-files-the-build-reads

Conversation

@iderex

@iderex iderex commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes #219.

What was wrong

Three rows of the invariant gate are declared over "every tracked file the build
reads to render a page". The population behind that sentence held nineteen files
and every one of them was under templates/ or content/. Nothing under data/
was in it, although that is where the build reads the values a page states.

git ls-files templates content | wc -l
19

go run . build | grep '^read'
read templates/page.html.tmpl
read content/index.txt
read data/design-tokens.json (206 value(s))
read data/clients.json (none-released)
read data/roster.json (12 row(s))
read content/plugins (12 file(s), one per row)

Both run 2026-08-31 at a0da593, which is the base of this branch.

The absence was control flow rather than a decision. Four of those files are
sorted into a population that asks whether the file is present at all, and each
sorter returned from the walk, so the file never reached the test that would have
counted it as a build input as well.

What it cost

A server generation typed into the roster passed the whole gate. Taken at
a0da593, with the value put into data/roster.json and taken out again:

go run . invariants | grep '^  build-input-carries-no-server-generation'
  build-input-carries-no-server-generation: ok, 19 file(s) of every tracked file the build reads to render a page

go run . ci | tail -2
  invariants: ok, 39 rule(s) decided, 2 owed and not decided
7 of 7 legs ran. None was skipped.

That is the last condition of #91 - no server version in a template, in
content/ or in the roster, refused with the offending file named - reading as
met on a tree where the roster half could not be reached. The reading on #91 of
2026-08-17 and the comment on the deciding function both say the roster becomes a
subject of the row on the day a copy lands. The copy landed on 2026-08-25 and
#91 closed on 2026-08-26.

The same population decides where a token value and a client limit may be
written, so a colour or a millisecond typed into the roster was equally
unreadable.

What this changes

Each of the three rows reads every tracked file the build reads except the one
file that row is the authority for. That exception is why widening the single
shared population is not the repair: the token row would refuse the file that
declares the token values, and the budget row the file that declares the limits,
each for carrying the value it exists to carry. The roster and the security
contact are the authority for none of the three and were excluded from all three.

At this head the same experiment refuses, and names the file, the line and the
number:

go run . invariants | grep -A3 '^  build-input-carries-no-server-generation'
  build-input-carries-no-server-generation: REFUSED, 1 violation(s)
    it refuses a server generation written into what the build reads, in either the spelling a page states it in or the key a build manifest declares it under
    because which server generation a build is for is a fact about what was published rather than an opinion anybody records, and a generation typed here is right on the day it is typed; the release that moves to the next one leaves the page stating the old one, looking exactly as correct as it did before, and the reader it is wrong for is the one deciding what to install
    data/roster.json: line 5 states the server generation 10.11, and only a published release says which generation a build is for

Run 2026-08-31 at 8619199, with the value put in and taken out again; the tree
carries no such value.

The proof, and why the suite had none

Every existing case for these three rows hands bytes straight to a decide
function, so the decider is judged and the selection that chooses which files it
ever meets is judged by nothing. Two cases now reach the selection:
TestEachBuildInputRowSeesEveryFileTheBuildReadsExceptItsOwnSource over the
fixture tree, and TestRunRefusesAServerGenerationTypedIntoTheRoster over a run.

Both were run against a head carrying this branch's rows and subjects with the
population narrowed back to templates/ and content/, and both go red there:

--- FAIL: TestEachBuildInputRowSeesEveryFileTheBuildReadsExceptItsOwnSource
    data/roster.json in "every tracked file the build reads to render a page" is false, want true
    data/clients.json in "every tracked file the build reads to render a page" is false, want true
    data/design-tokens.json in "every tracked file the build reads to render a page" is false, want true
    data/security-contact.json in "every tracked file the build reads to render a page" is false, want true
    data/releases.json in "every tracked file the build reads to render a page" is false, want true
--- FAIL: TestRunRefusesAServerGenerationTypedIntoTheRoster
    Run accepted a roster stating a server generation

Run 2026-08-31. TestRunAcceptsARosterThatStatesNoGeneration is the neighbour
leg and stays green in both states, so the refusing case is not passing over a
run that refuses every roster.

The means

Go, in internal/invariant, where the population is assembled and the rows are
declared, plus one directory constant in internal/site beside the two it sits
with. The change is a selection over tracked files and the package already
carries the row shape, the fixture convention and the suite that proves a row
bites, so it needs no program the toolchain does not have.

The gate

go run . ci | tail -3
  sitemap: every page the build wrote is listed once, and every entry has a page behind it
  invariants: ok, 39 rule(s) decided, 2 owed and not decided
7 of 7 legs ran. None was skipped.

Run 2026-08-31 at 8619199.

What this does not do

It does not read data/catalogue.json or data/publisher.json differently from
the rest: they are in the population because they are under data/ and the build
reads them, and nothing here measures what a row would find in them beyond the
run above, which found nothing.

It does not touch the two rows that are owed and not decided, and it does not
move the count of rules the gate decides.

This change has had no second reader. Nothing here was reviewed by anybody
else, and the evidence above stands in place of one: the experiment that produced
the failure, the two cases that go red without the change, and the gate at this
head.

Three rows are declared over "every tracked file the build reads to render a
page", and the population behind that sentence held nineteen files, every one of
them under templates/ or content/. Not one file under data/ was in it, although
the build reads the roster for every plugin row and every plugin page, the token
copy for the design system page, the clients file for the sentence about the
clients, and the security contact, the catalogue, the publisher and the recorded
releases for the pages that state their values.

The absence was control flow rather than a decision. Four of those files are
sorted into a population that asks whether the file is present at all, and each
sorter returned from the walk, so the file never reached the test that would have
counted it as a build input. The remaining three were never named by any test.

What that cost is a guard that could not bite. A server generation typed into
data/roster.json passed the whole gate: build-input-carries-no-server-generation
reported ok over nineteen files, none of which was the roster. That is the last
condition of #91 - no server version in a template, in content/ or in the roster,
refused with the offending file named - reading as met on a tree where two thirds
of it were unreachable. The same population decides where a token value and a
client limit may be written, so a colour or a millisecond typed into the roster
was equally unreadable.

Each row now reads every tracked file the build reads except the one file that
row is the authority for. That exception is why widening the single shared
population is not the repair: the token row would refuse the file that declares
the token values, and the budget row the file that declares the limits, each for
carrying the value it exists to carry. The roster and the security contact are
the authority for none of the three and were excluded from all three.

It was found while re-reading the price #75 puts on making the roster sentence
the published repository description, which rests on that generation being
refused. Putting one into the roster and running the verb returned ok.

The proof is the part that was missing rather than the decider, which was always
right: every existing case hands bytes straight to a decide function, so nothing
judged the selection that chooses which files a decider ever meets. Two cases now
do, over the fixture tree and over a run, and both go red on the narrowed
population and green on this one.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex iderex self-assigned this Aug 31, 2026
@iderex
iderex merged commit cfa174a into main Aug 31, 2026
17 checks passed
@iderex
iderex deleted the tests/the-build-input-population-leaves-out-the-files-the-build-reads branch August 31, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The row refusing a typed server generation does not read the roster, and its subject says it does

1 participant