Skip to content

Add RFC process, specifications, layered tests, and a gated CI/CD pipeline - #1

Merged
Protonmatter merged 2 commits into
mainfrom
topic/quality-gates
Aug 18, 2026
Merged

Add RFC process, specifications, layered tests, and a gated CI/CD pipeline#1
Protonmatter merged 2 commits into
mainfrom
topic/quality-gates

Conversation

@Protonmatter

Copy link
Copy Markdown
Owner

What this changes

Introduces an RFC process, normative specifications, a five-layer test suite, and a CI pipeline that gates the Pages deploy on it. Also fixes two defects the work surfaced.

Why

The site deployed on every push with nothing verifying the result. For a site whose value is that it renders correctly, the interesting failures are silent ones: a path that stops resolving, a deck that stops advancing, prose that stops being true. None are caught by a build step, because there is no build step — they have to be caught by executing the pages.

Process — RFC 0001

Substantive changes are proposed as a numbered RFC, specified as requirements in docs/specs/ carrying RFC 2119 keywords and stable identifiers, and covered by tests annotated @REQ <ID>.

tools/spec-trace.js fails the build when a MUST-level requirement has no test, a test names a requirement that does not exist, or an identifier is defined twice — so the specification and the suite cannot drift apart.

71 requirements, 58 mandatory, 125 tests.

Pipeline — RFC 0002

Layer Runner Owns
Validation node --test Repository consistency, before a browser is involved (~1s)
Functional Playwright Does each page behave as specified?
End-to-end Playwright Can a visitor complete a journey across pages?
Regression Playwright Have invariants and past defects stayed fixed?
Visual Playwright Does it still look right? (CI-only)

ci.yml is a reusable workflow called both by the pull-request check and by pages.yml before it deploys, so the merge gate and the deploy gate cannot diverge. A red gate now leaves the live site untouched.

Browser tests stub external font requests for determinism and fail on any other off-origin request, enforcing the no-CDN rule at runtime as well as in source. Visual comparison is pinned to ubuntu-24.04 and skips loudly when no baseline exists rather than writing one and passing; baselines are seeded by the Update visual baselines workflow, which opens a pull request of images.

Defects fixed

Every deck rendered blank on any Windows clone. support.js loads the vendored React bundles with a Subresource Integrity digest, and SRI hashes exact bytes. With no .gitattributes, Git's default Windows behaviour rewrote LF to CRLF on checkout, the digests stopped matching, React never loaded — and because the runtime hides the authored markup before booting, the pages rendered nothing at all, with HTTP 200 and no broken links.

The published site was never affected, which is what made this so hard to diagnose: it only ever broke on a contributor's machine, with no visible cause. .gitattributes prevents it; SITE-016 detects it. Verified against a fresh clone as byte-identical to the committed blob.

Six pages had no <title> and no lang. The five .dc.html pages and pocket-planetarium.html declared neither, leaving browser tabs, bookmarks and print-to-PDF filenames unnamed — on decks explicitly meant to be printed.

Known deviations, now recorded rather than implicit

Both in docs/specs/site.spec.md:

  • D-1 — the Pocket Planetarium is served by a separate runtime copy that loads React and Babel from unpkg, so it is not CDN-free. The README no longer claims the whole site is.
  • D-2 — the deck runtime requests un-substituted {{ }} template placeholders before boot, producing three harmless 404s on the explorer.

Each is excluded from the requirement it violates in one named place, with a reason, rather than the requirement being quietly written loose enough to pass.

Requirements touched

Adds the initial requirement set: SITE, DOCS, DECK, LAND, BETA, EXPL, FLOW, VIS. All 58 mandatory requirements are covered; the two uncovered SHOULD-level ones (SITE-013, BETA-005) are reported as notes by design.

Checklist

  • npm test passes locally (21 validation, traceability OK, 104 browser)
  • New behaviour is specified in docs/specs/ and annotated @REQ in tests
  • README statements about the build are true again (slide counts, CDN claim)
  • Known deviations recorded in the specification

Visual impact

None. The six pages gain a <title> and lang attribute; nothing rendered changes. No visual baselines exist yet — seed them with the Update visual baselines workflow after merge.

…eline

The site deployed on every push with nothing verifying the result. For a site
whose value is that it renders correctly, the interesting failures are silent
ones: a path that stops resolving, a deck that stops advancing, prose that stops
being true. None are caught by a build step, because there is no build step.

Process (RFC 0001): substantive changes are proposed as an RFC, specified as
numbered requirements carrying RFC 2119 keywords in docs/specs/, and covered by
tests annotated @Req. tools/spec-trace.js fails the build when a MUST-level
requirement has no test, or a test names a requirement that does not exist, so
the specification and the suite cannot drift apart.

Pipeline (RFC 0002): validation (static, ~1s), functional, end-to-end, regression,
and visual layers. ci.yml is a reusable workflow called both by the pull-request
gate and by pages.yml before it deploys, so the two gates cannot diverge. Browser
tests stub external font requests for determinism and fail on any other off-origin
request, which enforces the no-CDN rule at runtime. Visual comparison is pinned to
the Linux runner and skips loudly rather than passing silently when no baseline
exists; baselines are refreshed by a workflow that opens a pull request of images.

71 requirements, 58 mandatory, 125 tests.

Two defects surfaced while writing this and are fixed here:

- Windows checkouts rendered every deck blank. support.js loads the vendored React
  bundles with a Subresource Integrity digest, and SRI hashes exact bytes. With no
  .gitattributes, Git rewrote LF to CRLF on checkout, the digests stopped matching,
  React never loaded, and because the runtime hides the authored markup before
  booting, the pages rendered nothing with no visible cause. The published site was
  never affected. .gitattributes prevents it; SITE-016 detects it.
- The five .dc.html pages and pocket-planetarium.html declared no <title> and no
  lang, so browser tabs, bookmarks, and print-to-PDF filenames were all unnamed on
  pages explicitly meant to be printed.

Two known deviations are recorded rather than hidden, in docs/specs/site.spec.md:
the Pocket Planetarium loads its runtime from unpkg (D-1), and the deck runtime
requests un-substituted {{ }} placeholders before boot (D-2). The README no longer
claims the whole site is CDN-free, which was untrue of the prototype.
Leaves configure-pages, upload-pages-artifact and deploy-pages pinned: a
breaking change in those would not surface until a merge to main, so they are
better reviewed as their own Dependabot pull request than bundled in here.
Protonmatter added a commit that referenced this pull request Aug 18, 2026
…undles (#2)

## What

Adds `.gitattributes` to `main`, taken verbatim from the
`topic/quality-gates` branch where it was written.

## Why this cannot wait for #1

Cloning this repository on Windows today produces a completely broken
working copy, and the cause is invisible.

`support.js` loads the vendored React, ReactDOM and Babel bundles with a
Subresource Integrity digest, and SRI hashes exact bytes. Git's default
on Windows (`core.autocrlf=true`) rewrites LF to CRLF on checkout, so
the digests stop matching, the browser refuses to execute the bundles,
and React never loads. Because the runtime hides the authored markup
before booting, the result is HTTP 200, no broken links, no console
error, and nothing on screen.

`.gitattributes` exists on `topic/quality-gates` (#1), but that does not
help anyone cloning before it merges, and it does not help the clone
itself: `git clone` checks out the default branch first, so `main`
having no `.gitattributes` is what does the damage.

## Verification

Cloning each branch with `core.autocrlf=true` and hashing the result:

| Bundle | `main` today | with this change | declared in `support.js` |
| --- | --- | --- | --- |
| `react.production.min.js` | `sha384-t63xaoqI4/…` |
`sha384-DGyLxAyjq0f9…` | `sha384-DGyLxAyjq0f9…` |
| `react-dom.production.min.js` | `sha384-8Y1L+f1y2tWR…` |
`sha384-gTGxhz21lVGY…` | `sha384-gTGxhz21lVGY…` |
| `babel.min.js` | `sha384-nFyaaMobkrf0…` | `sha384-m08KidiNqLdp…` |
`sha384-m08KidiNqLdp…` |

Serving each clone and loading `solar-system.dc.html`:

- `main` today: `window.React` undefined, 0 characters rendered
- with this change: React 18.3.1, page renders

## No renormalisation needed

The committed blobs are already pristine LF: their SHA-384 digests match
the SRI constants `support.js` declares, and they contain zero CRLF byte
pairs. This change only stops the checkout filter from touching them, so
no file contents change and no `git add --renormalize` is required.

## Merge order

Independent of #1 and safe to merge first. The file is byte-identical to
the copy on `topic/quality-gates`, so #1 will not conflict on it.
@Protonmatter
Protonmatter merged commit 75601b9 into main Aug 18, 2026
3 checks passed
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