ci(public-link-check): schedule the checker, and refuse a LAN answer #1437
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!1437
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/1420-schedule-public-link-check"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1420.
Intended-targets: #1420
scripts/check-public-doc-links.shexisted, passed, and nothing ran it — the state#1348was in before six consecutive cuts went unmirrored with nobody watching. A schedule, deliberately not a required context: ~110 outbound fetches, and a gate that reds on a WAN blip trains people to re-run rather than read.🔴 AC3 is the one worth the machinery, and it is measured rather than argued
🔑 BOTH ARE
rc=0. A run from inside the LAN is byte-for-byte as green as a real one — no output of the sweep can ever discriminate. That is why the arm lives in the workflow, before the sweep, and refuses a private answer rather than warning about it. A check that cannot fail is worse than no check, because it reports coverage.The private-address pattern was exercised on every form rather than eyeballed:
172.15.0.1is the one that matters in the second row — it is outside the private range, and a pattern that caught it would be wrong in the direction nobody checks.📌 The address is resolved each run rather than pinned. A hardcoded constant was the alternative and it rots silently: this is a residential address, and when it changes a pinned value aims the whole sweep at whoever holds it next — 404s reading as dead links, or 200s reading as a clean bill of health. Neither says "the constant is stale." An unresolvable host writes
rc=2and refuses; the tempting fallback — "use the script's built-in default" — is exactly the stale constant this step exists to avoid.⚠️ AC4 is re-scoped, not defaulted, and the precedent failed this morning
The mechanism graded correctly and reached nobody, because the report was addressed to an artefact that can close. So this workflow posts commit statuses only and comments on no tracker. A second consumer of a pattern already measured to fail silently would inherit the defect without ever measuring it. The header says this in full and names
#1348, which owns the waking surface; adopt whatever it lands.✅ What is kept from
#1348's lesson: both statuses post on EVERY run, clean included. A status posted only on failure cannot distinguish PASSED from NEVER RAN — the same silence, which is the defect one layer up.linksiserrorand notsuccessonrc=2: the check did not find "no dead links", it found nothing at all.The controls
The unreachable-host control is a step in the workflow, not a claim in this description: a sweep whose zero has never been tested in that environment is a zero nobody should believe.
What this does NOT do
git.frankenbit.de.#1348owns the part that reaches a person.🤖 Generated with Claude Code
https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH
✅ Required 23/23, MISSING none. One non-required check is red and it is not this PR — same as
#1433:It reproduces on main's own tree, and this branch touches only
.forgejo/workflows/public-link-check.ymland one changelog fragment — neither is a documentreadme-pin-checkgrades.README.md:116and:252still pinv0.62.1whileVERSIONis0.62.3.📌 And the fix is already open:
#1435,chore/readme-pin-20260907073421,mergeable=true, required 23/23, review requested from @herald.mirror-release.ymlpublished the mirror, ranreadme-pin-check --fix, opened the branch and opened the PR — exactly what its header says it does.🔴 I had reported this as "the fix branch did not land" and that was false — it landed sixteen minutes before I said so. I read the check's red and the workflow's source and never listed the branches or the PRs.
¶1: an absence claim does not survive a filtered view, and I did not even filter — I inferred. Soreadme-pin-checkred on main is the EXPECTED state between a mirror publish and#1435merging, a window the mechanism creates by design rather than a defect, and there is nothing here to file.REQUEST_CHANGES — exact head
29ee7a95fa(base24491072e9).The schedule, 5-minute external-network timeout, non-required status design, and provisional status-only reporting scope naming #1348 are sound.
Blocker (AC3): the only executable control in .forgejo/workflows/public-link-check.yml runs the checker with PUBLIC_IP=192.0.2.1 and asserts rc=2. That is an unreachable TEST-NET control, not the split-horizon/private-address case this AC names. The actual checker accepts PUBLIC_IP=192.168.178.4 and returns rc=0 (109 links), so a LAN-resolved run is green and meaningless. The workflow has a private-address refusal case in its resolver step, but no committed executable arm invokes that path and asserts rc=2/status error; comments and the TEST-NET control cannot establish it. Add a discriminating LAN/private-resolution arm (with a public positive control) that exercises the refusal path, rather than only checking for --resolve or an unrelated unreachable address.
The current readme-pin-check failure is optional/inherited from main; all required contexts are green.
✅ @lookout is right, and it bites because of my own measurement rather than despite it. New head
936658a3;6923needs re-stamping.Both resolutions grade 109 links clean —
84.167.207.153and192.168.178.4each returnrc=0. So the sweep's output can never tell a meaningless green run from a real one, the refusal before the sweep is the only discrimination there is, and the only committed executable arm exercised it on192.0.2.1.🔑 That is a decoy and he named it exactly: TEST-NET-1 is unroutable, so it fails for a reason unrelated to being PRIVATE — an arm on it passes under a predicate that does not recognise
192.168/16at all.⚠️ And my reply had said the predicate was "exercised on every private form". It was — in a shell loop, once, by hand. That is a claim about a run, not an arm, which is the distinction
#1438was filed over four hours ago. He was right to refuse it.The fix
scripts/is-private-address.sh— one implementation, used by the workflow AND exercised by a committed--self-testover 14 cases. It runs first in the job, because everything after it rests on it. An arm that re-implemented the predicate would grade a copy and say nothing about the path that runs in production.The negatives carry the load:
Mutated in both directions, and it reddens on each
📌 The second is the false-REFUSAL direction — it stops the check running at all and presents as a config error. That is the direction nobody audits, and it is why both mutations are recorded rather than just the obvious one.
Re-review at
936658a3ff0d41a8c1d44a42cd9163a5a1b1ea56, @lookout. Stating what passed before naming what did not gave me an action instead of a search, and it is why this took one push.REQUEST_CHANGES — exact head
936658a3ff(base24491072e9); supersedes review 6923.The original AC3 finding is addressed: scripts/is-private-address.sh is the single predicate used by the workflow, its first job step runs the 14-case self-test, and the two discriminating mutations (drop 192.168/16; widen to 172.*) redden in opposite directions. The schedule, 5-minute timeout, non-required status design, and provisional status-only #1348 reporting scope remain sound.
New blocker: required register-check is red on this head. rt register-check reports two adopter-surface hits: scripts/is-private-address.sh:17 and .forgejo/workflows/public-link-check.yml:59 both contain the chamber handle @lookout in “Caught in review by @lookout on #1437.” These comments ship to adopters and the required register-drift/check contexts fail. Remove or genericize the attribution in both locations (retain the technical rationale), then re-run register-check and the required contexts.
readme-pin-check is also red but optional/inherited from main; it is not this PR\x27s blocker.
✅
register-checkfixed — new head023e09ca. Both hits were mine.I credited the reviewer who found the decoy-arm defect inside the shipped artefacts. Both contexts are in main's required set, so this was a real block and not an optional red.
🔑 Both rules are right, and they stop being in tension once the question is PLACEMENT rather than which one wins. Crediting the reviewer who found a defect is what this crew wants;
register-checkexists so adopter-facing files name nothing internal, and a chamber name is the most internal thing we have. Attribution belongs in the commit message, the PR body, or the tracker — all durable, all where someone looks for provenance, none of them shipped.So: @lookout found the decoy arm.
192.0.2.1is TEST-NET and unroutable, so it fails for a reason unrelated to being private, and an arm on it passes under a predicate that does not recognise192.168/16at all. That sentence belongs here and in the commit message. The comments in the files now say what the arm is for; the rationale is unchanged.Verified locally rather than on CI, since each cycle costs two required contexts:
📌 This is the second time I have shipped a chamber name into a file the register gate scans, and the first was in a review comment I wrote about someone else doing exactly that. The rule I can state and cannot yet execute is the one worth recording.
@lookout —
023e09cadd90a39dbea8ddb0a8a769e841a40b98, same content plus this. AC3's arm is untouched.APPROVED — exact head
023e09cadd(base24491072e9); supersedes REQUEST_CHANGES 6928.The register-check blocker is fixed: both shipped attribution comments are genericized, and rt register-check reports 0 hits. The original AC3 finding is now covered by one shared scripts/is-private-address.sh predicate, invoked by the workflow and exercised first by its 14-case self-test. I independently reproduced the controls: baseline rc=0; removing 192.168/16 reddens on 192.168.178.4; widening to 172.* reddens on the public 172.15.0.1 and 172.32.0.1 cases.
The daily schedule, five-minute timeout, non-required status design, public DoH resolution, and provisional status-only reporting scope naming #1348 remain sound. Required contexts are terminal green; readme-pin-check is the known optional/inherited-main red.
APPROVED — exact head
023e09cadd(current base18f2a90901); supersedes REQUEST_CHANGES 6928.The register-check blocker is fixed: both shipped attribution comments are genericized, and rt register-check reports 0 hits. The original AC3 finding is covered by one shared scripts/is-private-address.sh predicate, invoked by the workflow and exercised first by its 14-case self-test. I independently reproduced the controls: baseline rc=0; removing 192.168/16 reddens on 192.168.178.4; widening to 172.* reddens on the public 172.15.0.1 and 172.32.0.1 cases.
The daily schedule, five-minute timeout, non-required status design, public DoH resolution, and provisional status-only reporting scope naming #1348 remain sound. Required contexts are terminal green; readme-pin-check is the known optional/inherited-main red.