Skip to content

docs: a link gate that reads what ships, and the last GCP project id - #125

Merged
fas89 merged 2 commits into
mainfrom
docs/final-four
Sep 15, 2026
Merged

fas89 merged 2 commits into
mainfrom
docs/final-four

Conversation

@fas89

@fas89 fas89 commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Four changes on disjoint files. The first is the one that matters.

1. A link gate that reads the artifact

Every link gate in this repo read source markdown:

gate what it reads coverage
links-check plugin (build: 'error') markdown link tokens, filtered on /\.md(?:[?#]|$)/ ~861 of ~1331 internal links
base-prefix-check ](/…) in docs/**.md absolute markdown links
lychee-source lychee over docs/**.md markdown + raw HTML anchors

None of them can see the bytes that deploy. Measured on 92925e5: a one-character typo in the config.ts navbar (/why → /whyy) leaves npm run docs:build exiting 0 while shipping 424 dead references across 212 of the 213 rendered pages.

scripts/check-dist-links.mjs walks docs/.vuepress/dist/**.html instead. On this build:

HTML pages walked     219   (floor 180)
href/src attributes   111,490
absolute, same-origin 107,738   (floor 90,000)  <- the class this gate owns
distinct targets      474   (floor 400)
resolved by rule      exact 100,779, dir index.html 6,864, extensionless +.html 95
excluded              scheme 970, fragment-only 2,773, relative 9, protocol-relative 0, empty 0

It prints that census on every run, because "passed" with no numbers is indistinguishable from "inspected nothing". No dependency — node: builtins only, 0.9s.

The anti-blindness machinery earned its keep immediately

Canaries run in-process before every scan, not as a separate step — a separate step can be deleted while the gate keeps reporting green. There are floors on pages, references and distinct targets.

The first version's canary fixture contained only <a href> elements and zero src attributes. So dropping src from the extractor moved the artifact counts by 219 of 107,738 — under every floor — and the gate printed Clean while no asset reference in dist was checked. That is this repo's signature defect reproduced inside the gate built to prevent it.

Reproduced and closed. The fixture now carries a resolving <img src> and a failing <script src>; the mutant fails four separate assertions:

[error] Canary failed: exact-rule resolutions 2, expected 3
[error] Canary failed: reported 2 findings, expected exactly 3
[error] Canary failed: finding kinds were [missing-base, missing-file], expected [missing-base, missing-file, missing-file]
[error] Canary failed: the src attribute class was not inspected — …
[error] The dist link checker cannot be trusted this run … Refusing to report on the real artifact.

The ok — summary now derives from the census as well. It was a fixed sentence reading "resolved 4 links … caught 1 missing file" for a fixture that resolved 5 and caught 2 — describing an earlier version of itself.

The coverage map, with one cell corrected

The step carries a table of which gate owns which class, so nobody deletes a sibling believing it redundant. One row claimed only the new gate sees raw HTML anchors. That was wrong, and it is measured, not inferred — appending <a href="/cli/init.html"> to a page makes lychee report File not found at 3:10. Corrected to YES. An overstated new gate is the same category of misleading note this change set out to remove.

2. The second GCP project id

fluid-crypto-tracker was mostly illustrative — 33 of 35 sites were shell, YAML, SQL or expected output. Two were not:

project_id = os.getenv("GCP_PROJECT_ID", "fluid-crypto-tracker")   # gcp.md:153 and :812

Both feed straight into bigquery.Client(project=project_id). A reader who copies the script and forgets the export writes to whoever owns that id, with no error. The second is a deployed Cloud Function on an hourly Cloud Scheduler trigger.

Both now fail loudly (usage line, sys.exit(1); the function returns 500). All 35 occurrences gone; remaining placeholders use the convention already present elsewhere in the same file.

3. The release banner

One 266-word paragraph that led with 0.15.0 while the same page's version line said 0.15.1. Now the upgrade break first (a 0.14.1-clean contract can fail), three scannable bullets, history collapsed with each version linking its own notes.

It also credited the MCP output-port gateway to 0.8.7. RELEASE_NOTES_0.8.6 headlines it; 0.8.7 says outright it adds no commands, flags or schema features. Corrected to 0.8.6.

4. The Monaco mechanism, recorded in the code

Monaco's CSS is 162,004 of the single 251,769-byte stylesheet (64.3%), contiguous from .monaco-aria-container, on all 213 pages — because @vuepress/bundler-vite 2.0.0-rc.31 hard-sets cssCodeSplit: false at dist/index.js:108. Its JavaScript is split correctly: 13.1 MB in async chunks that no prerendered page references.

The comment records that overriding the option is mechanically possible via viteOptions and that the fix is declined, not blocked, with the reason — a claim a maintainer could disprove in five minutes would poison the rest of it. Plus the commands to re-verify every number.

Comment only: +60/−0, every added line a //, and the built HTML byte-identical across all 219 pages.

Verification

Done here, not taken on report:

claim result
build blind to the navbar typo npm run docs:build exit 0
gate catches it exit 1, 424 refs / 212 pages
restored build 0, gate 0, 107,738 refs clean
src mutant caught, 4 assertions, exit 1
lychee reads raw anchors reproduced, File not found at 3:10
fluid-crypto-tracker 0 occurrences in tracked files
check_cli_docs.py green — 1093 documented invocations, every version claim agrees with the 0.15.1 pin
check_providers.py green
workflow YAML parses; gate step keys are name,run only — no continue-on-error, no if:

Known, not fixed here

  • docs/RELEASE_NOTES_0.7.11.md links ./.vuepress/cli-version.json, which is a live 404 (.vuepress/ is not copied into dist). The gate reports it warn-only and says why: RELEASE_NOTES files are frozen, and a red gate nobody may fix is a gate that gets disabled. Fix that link, then flip the step to --relative=error.
  • deploy-docs.yml uploads to Pages with no link gate between build and upload. Two individually-green PRs can ship a broken artifact, and a direct push to main is ungated. Adding node scripts/check-dist-links.mjs there is the follow-up.
  • examples/bitcoin-tracker/load_bitcoin_price_batch.py:70 and runtime/ingest_bitcoin_prices.py:50 still carry os.getenv("GCP_PROJECT_ID", "<<YOUR_PROJECT_HERE>>"). The placeholder is invalid so it cannot reach a real project, but it is the same shape and their sibling no longer has it.

Four things, and the first is the one that matters.

THE ARTIFACT GATE. Every link gate here read SOURCE MARKDOWN: the bundled
links-check plugin filters candidates on /\.md(?:[?#]|$)/ and so sees about 861
of ~1331 internal links; base-prefix-check greps `](/…)`; lychee-source runs over
docs/**.md. None can see the bytes that deploy. Measured on 92925e5: a
one-character typo in the config.ts navbar (`/why` -> `/whyy`) leaves
`npm run docs:build` exiting 0 while shipping 424 dead references across 212 of
the 213 rendered pages. scripts/check-dist-links.mjs walks
docs/.vuepress/dist/**.html instead — 219 pages, 107,738 absolute references,
474 distinct targets on this build, with the census printed, because "passed"
with no numbers is indistinguishable from "inspected nothing".

Its canaries run IN-PROCESS before every scan rather than as a separate step, a
separate step being deletable while the gate keeps reporting green, and there are
floors on pages, references and distinct targets. That machinery earned its keep
immediately: the first version's fixture contained only `<a href>` elements, so
dropping `src` from the extractor moved the counts by 219 of 107,738 — under
every floor — and the gate printed "Clean" while no asset reference in dist was
checked. Reproduced, then closed: the fixture now carries a resolving `<img src>`
and a failing `<script src>`, and that mutant fails four separate assertions. The
"ok" line now derives from the census too; it was a fixed sentence describing an
earlier version of itself.

The step's coverage map says which gate owns which class, so nobody deletes a
sibling believing it redundant. One cell in it was wrong and is corrected: lychee
DOES read raw HTML anchors in markdown, measured, not inferred.

THE SECOND GCP PROJECT ID. `fluid-crypto-tracker` was mostly illustrative, but
two sites were the same shape as the id removed earlier:

    project_id = os.getenv("GCP_PROJECT_ID", "fluid-crypto-tracker")

feeding straight into bigquery.Client(project=project_id). A reader who copies
the script and forgets the export writes to whoever owns that id, silently. The
second one is a deployed Cloud Function on an hourly schedule. Both now fail
loudly with a usage line; the function returns 500. 35 occurrences gone, the
remaining placeholders follow the convention already used elsewhere in the same
file.

THE RELEASE BANNER was a single 266-word paragraph and led with `0.15.0` while
the page's own version line said `0.15.1`. Now: the upgrade break first (a
`0.14.1`-clean contract can fail), three scannable bullets, history collapsed
with each version linking its own notes. It also credited the MCP output-port
gateway to `0.8.7`; RELEASE_NOTES_0.8.6 headlines it and 0.8.7 says outright that
it adds no features. Corrected to `0.8.6`.

THE MONACO MECHANISM is recorded where someone will meet it. Monaco's CSS is
162,004 of the single 251,769-byte stylesheet — 64.3%, contiguous from
`.monaco-aria-container` — on all 213 pages, because bundler-vite 2.0.0-rc.31
hard-sets `cssCodeSplit: false` at dist/index.js:108. Its JAVASCRIPT is split
correctly: 13.1 MB in async chunks that no prerendered page references. The
comment records that overriding the option is mechanically possible via
viteOptions and that the fix is DECLINED rather than blocked, with the reason,
plus the commands to re-verify every number. Comment only: +60/-0, and the built
HTML is byte-identical across all 219 pages.

Verified here, not taken on report: build 0 and gate 1 on the seeded typo (424
refs, 212 pages), both 0 restored; the src mutant caught; lychee reproducing the
raw-anchor finding; zero `fluid-crypto-tracker` in tracked files; check_cli_docs
green (1093 documented invocations, every version claim agreeing with the 0.15.1
pin); check_providers green.
…ent about it

`docs/RELEASE_NOTES_0.7.11.md` linked `./.vuepress/cli-version.json`. `.vuepress/`
is not copied into dist, so that resolved to nothing on the live site: 404 on the
file while the page carrying it returned 200. The gate reported it warn-only and
said why in its own header — a red gate nobody is permitted to fix is a gate that
gets disabled — so the leniency was load-bearing on the defect existing.

The link now points at the GitHub blob URL that `docs/contributing.md` already
used for the same file, in the same sentence. That changes a destination, not a
claim: the release note still says exactly what `0.7.11` did, which is the line
between fixing a link in a frozen file and rewriting its history. New destination
verified 200.

With the exception gone, `--relative` defaults to `error` rather than `warn`, and
the coverage map's relative row is a YES instead of a footnote. The flag still
accepts `warn` for a local run.

Verified: relative references fall 9 to 8 and scheme rises 970 to 971, which is
the one link moving from one class to the other, and the "Broken RELATIVE
references" section is gone. Then the tightening proved load-bearing rather than
assumed — a raw `<a href="./no-such-relative-target.html">` appended to
docs/why.md leaves `npm run docs:build` exiting 0 while the gate exits 1 and names
`why.html -> ./no-such-relative-target.html`; the same run with `--relative=warn`
exits 0. Seed removed, build and gate clean, self-test clean.
@fas89
fas89 merged commit 87b53d8 into main Sep 15, 2026
8 checks passed
@fas89
fas89 deleted the docs/final-four branch September 15, 2026 07:26

This branch was successfully deployed

1 active deployment
github-pages — 38beeb86 Deployed Sep 15, 2026 by fas89 via deploy #158
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.

1 participant