Compute the shipping state from what is published [#22] - #191
Merged
iderex merged 2 commits intoAug 25, 2026
Conversation
The table showed what a roster row declared and nothing else, so the one state word that is a fact rather than an opinion was the one word nothing computed. `internal/releases` holds what each repository has published, taken once by a verb somebody runs and read from the tree by every build. Two counts per repository rather than a verdict, because the rule that turns them into a word is `decisions/0009-what-counts-as-shipping.md` and can move; a record carrying the word would have to be retaken the day it does. The rule is applied in one place: a plugin ships when a finished release is published, the prerelease flag decides and the tag string is not read. Nothing in a build reaches the network. `go run . releases` asks and writes, and what asks is `internal/releases/github`, a package the build imports nowhere, so the separation is a property of the dependency graph rather than of anybody remembering it. A build renders what was last recorded and the landing page states the day it was recorded, which is what makes a page produced from a recorded answer readable as one. The record also answers whether a roster row's repository is there, which `data/repositories.json` answered until now. The two are one question asked once: a repository that answered with its release list is a repository that exists, and a second file carrying the same set is a second thing to keep current. That file is removed. Three rows now compute as shipping where their row declares otherwise, and the build does not refuse the disagreement, because refusing it is #23. The disagreement is named in the pull request that lands this rather than left for a reader to find. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
The static analysis rule refuses a read whose path the line itself does not show
to be assembled, and FromSlash converts separators rather than assembling a
path, so the new verb's read was one the call site could not vouch for.
Ran 5 rules on 32 files: 1 finding.
main.go
tools.semgrep.file-read-with-a-path-the-call-site-cannot-vouch-for
116: body, err := os.ReadFile(filepath.FromSlash(site.RosterFile))
Run by the static analysis workflow on the head before this one. The root the
verb works against is now named once and joined, which is also what it hands to
the refresh below it.
Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
iderex
deleted the
enhancement/the-shipping-state-comes-from-the-releases
branch
August 25, 2026 02:46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
The table showed what a roster row declared and nothing else, so the one state
word that is a fact rather than an opinion was the one word nothing computed.
Nothing in the tree read a release list, and nothing named the rule that decides
which releases count:
Run 2026-08-25.
184e34cis the mainline this branch was cut from.What this waited on was the roster, which is entry 6 of #7 and is answered, and
the record is now in the tree under #26.
What this does
internal/releasesholds what each repository the roster names has published:two counts per repository, finished and prerelease, and the day and the request
that took them. Two counts rather than a verdict, because the rule that turns
them into a word is
decisions/0009-what-counts-as-shipping.mdand can move,and a record carrying the word would have to be retaken the day it does.
The rule is applied in one place. A plugin ships when a finished release is
published; the prerelease flag is the signal and the tag string is not read,
which is what that record decides and what the case beside it asserts. A
declared state word is the floor and what is published raises it, which is
decisions/0001's rule.A prerelease is not dropped.
decisions/0009says it is the one piece of news aplugin that does not ship can offer and that it belongs on the page rather than
in the state, so the plugin's own page says how many are published in words that
cannot be mistaken for the table's word.
go run . releasesis the refresh path. It asks each repository the rosternames, reports what moved, and writes the record. It is not a leg of the gate,
for the reason
pinsandtokensare not: what it reads is somebody else'srelease lists, so its answer moves when they publish rather than when this tree
changes.
data/repositories.jsonis removed. It answered whether a roster row'srepository is there, and that is the same question this record answers: a
repository that answered with its release list exists, and a second file
carrying the same set is a second thing to keep current.
What this lands that somebody has to look at
Three rows now compute as shipping where the roster and the organisation profile
say otherwise, and the build does not refuse the disagreement. Refusing it is
#23, which names one of the three and does not name the other two.
Taken again with a different tool, so the counts are not one program agreeing
with itself:
Both run 2026-08-25.
ssois declaredbuild-upand the profile calls itShips, so the computedword agrees with what is published about it. The other two do not:
requestsis declaredshellandstatsis declaredbuild-up, and both havea finished release. #23 carries the
requestsinstance already; thestatsone is measured here for the first time and is not written into #23, because
that issue is being worked elsewhere and a second copy of a finding is worse
than none.
Nothing is served out of this repository, so no reader meets any of it today:
Run 2026-08-25.
Closes
Closes #22.
What was run
The gate, at the commit being pushed,
48cb1fb:No test was skipped, and none needed a display or elevation.
The state on the page is the computed one
Both run 2026-08-25 at
48cb1fb. The roster declares eightbuild-upand fourshell; what the page shows is six, three and three, so the word is computedrather than copied.
A build with the network unavailable
Nothing a build reads reaches the network, and that is a property of the
dependency graph rather than of anybody remembering it:
Run 2026-08-25 at
48cb1fb. What asks a repository anything isinternal/releases/github, which importsinternal/releasesand is imported bymain.goalone.The bound on that reading, stated rather than left for somebody to find.
internal/sitedoes still reachnet/httptransitively:That is
internal/tokens, whose fetcher thetokensverb uses and no buildcalls. So the reading above is about the release path and not about every
package a build links, and what stands behind the wider claim is that no build
verb calls a fetcher at all rather than a reading that could refuse one.
The guards bite
The record taken out of the tree, which is what a build with nothing recorded
would otherwise render:
The rule made to count any published release, which is the reading
decisions/0009refuses by name and the one a rule written quickly arrives at:The recorded moment dropped out of the sentence above the table, which is the
clause that makes a page produced from a recorded answer readable as one:
All three run 2026-08-25, each edit reverted before the next.
What the suite asserts that the runs above do not
The record fails closed on six shapes, each in its own words: bytes that are not
the record, a record carrying no repository, one with no repositories field at
all, one saying nothing about when it was taken, one taken at whitespace, and a
count a release list cannot answer. A refresh that could not read one repository
writes nothing, because a record missing a repository is a record that says the
repository is not there, and a network failure turned into a claim about
somebody's repository is the one failure a recorded answer must not have. Two
refreshes over the same answers in different orders write the same bytes, so a
run that changed nothing produces no diff. And the verb over a tree reports what
moved, what is new and what the roster no longer names.
What a server check caught that this machine's gate did not
The first head of this branch was refused by the static analysis workflow, and
the refusal is recorded here rather than removed with the commit that repaired
it. The rule refuses a read whose path the line itself does not show to be
assembled, and FromSlash converts separators rather than assembling a path:
The root the verb works against is named once and joined on
48cb1fb, which isalso what it hands to the refresh below it. That leg is not part of
go run . ci, which is why the gate on this machine was green over it, and thatgap is a property of the leg list rather than of this change.
The means
Go, and the shape this repository already uses for a verb that reads something
somebody else publishes:
pinsandtokensare both verbs rather than gatelegs for the same reason, and this one follows them. It differs from both in
writing rather than reporting, and that difference is argued at the function: a
pinned copy has an authority elsewhere and a difference is evidence about it,
while this record has no authority anywhere and is the answer itself, so a run
that only reported it would leave somebody typing counts into a file by hand.
JSON for the record, which is what every other file the build reads is written
in. Nothing is added to the module graph: the fetcher is
net/httpandencoding/json, and the record isencoding/jsonalone.The alternative refused was having the build ask. It would produce different
bytes on different days, fail with no network, and put a request to somebody
else's service inside the one verb that has to be reproducible.
Who read it
Nobody but me. The ruleset requires no approving review, so this is merged by
whoever opened it, and the evidence above stands in place of a second reader.