Skip to content

Forge, Loom and Crawler — case study

The substrate

Forge, Loom and Crawler — the tools this page is made of

A site here is not a folder of HTML someone edits. It is a set of typed documents, compiled to a finished site by a program that runs sixty-two checks on its own output and refuses to ship if one of them fails. This page went through it.

Forge
The compiler: typed page documents in, audited static site out
Loom
The design system as code — one hundred and sixty-six page components, and the only code allowed to turn content into markup
Crawler
A robot browser that opens the finished site and reports what reading the code cannot tell you
Together
About two hundred and twenty thousand lines of Rust and five thousand tests

Why a site should be a compiler's output

The failure a small site actually suffers is not a bad design. It is drift: a fix applied to four pages and forgotten on the fifth, a colour pasted in by hand that does not match, an image that lost its alt text in an edit, a heading level that skipped a rung and quietly broke the page for anyone using a screen reader. None of that announces itself, and all of it accumulates.

If the pages are data and the markup is generated, that class of problem stops being a matter of care. A page cannot use a colour that is not defined. An unknown key in a page document is a hard error rather than a section that silently vanishes. And the audit runs on the generated output every single time, so the check is not something you remember to do before a deploy.

Delete the built site entirely, run the build again, and the bytes come back identical to what the server was serving a minute ago.

Reproducibility you can check from outside

That is worth stating as a test rather than a principle, so I ran it while writing this page. Copy this site's content to a scratch directory, delete the whole built tree, and rebuild: sixty-two phases, no findings, a little over a second. The regenerated files match the committed ones one for one, and the generated home page is byte-for-byte what the live server was handing out at that moment. It is a claim anyone with the repository can re-run, which is the only kind worth making.

$ rm -rf static/ && forge build --root .
  strict findings:     0
  duration:            1161ms
forge build OK
# # 62 phases; output byte-identical to what the server was serving

The one time it did not match, and why that was the point

Running the same comparison against a client site turned up a difference of exactly one line: the integrity hash pinning the stylesheet. The committed build had been generated against a version of the design system that had since moved on.

That is the mechanism working rather than failing. Because every page pins the exact bytes of the stylesheet it was built against, a design system that drifts underneath a site turns into a visible one-line difference instead of a site that renders with the wrong palette and nobody notices for a week. The deploy script for that site carries a note about the incident that led to the pin: a stale build tree shipped against fresh stylesheets, and the client's brand colours disappeared.

What the robot browser is for

Some defects only exist at runtime. Whether text actually has enough contrast against what ends up behind it, whether a control can be reached and seen by keyboard, whether a tap target is big enough for a thumb, whether a button does anything when clicked — none of that is decidable by reading the source.

So a hundred detectors exist to drive a real browser through a finished site and produce a typed report rather than a page of prose: forty documents, one per axis, with screenshots and text snapshots of what a screen reader would announce at three points in the journey. The useful part is the last file, which diffs a run against the previous one, so "is this worse than it was?" is a question a build can answer instead of a person.

What the build refuses to ship

Buttons that go nowhere

Every interactive element has to be declared. An action with no destination fails the build — which is how the two new links in this site's own navigation were caught the first time it ran.

Missing security headers and unpinned assets

Content policy, transport security and subresource integrity are checked on the generated output, not assumed from the server configuration.

Pages with nothing to say

There is a check against thin content and one against a page that is the same block repeated — which failed this project's own index page while it was being written, and was right to.

An honest note about scope

This is infrastructure for one person's sites. It is public and it builds, but it is not packaged for anyone else: there is no release, nothing on a package registry, and no install that does not begin with cloning three repositories. The robot browser in particular is the least portable piece — it needs a working browser runtime, and it failed to complete a run on one of my own machines the day I wrote this.

The three repositories

  • The build pipeline and its audit phases.. PlausiDen-Forge.
  • The design system, the component catalogue and the renderer.. PlausiDen-Loom.
  • The runtime auditor and its detectors. This one opens with my own do-not-use notice — it is published to be read, not installed.. PlausiDen-Crawler.