Glass Box Terrarium, explained from the inside

Last updated · Latest source on GitHub ↗

Draft: this page sits outside the guide's focus.

VerificationLesson

Invariants as tests: pinned links

Some rules about content must hold on every page, every day: a link into the code shows the code the author saw. A rule that only lives in a writing guide holds until someone forgets it. Written as a test, it holds on every change, and the test can say exactly which link broke it.

Know permanent links already? Skip to In Terrarium.

Outside the box · General

The concept

A link to a file on a branch, such as …/blob/main/src/app.ts, always shows the file as it is now. Months later the lines it pointed at have moved, or the file is gone. A permanent link names a commit instead of the branch, as in …/blob/<commit-sha>/src/app.ts, and shows that exact version for good. On GitHub, pressing y while viewing a file turns its address into one (GitHub Docs: Getting permanent links to files).

A rule like "links to code are permanent" is an invariant: something that must be true of all the content, all the time. You can check an invariant with an ordinary unit test in a runner such as Vitest: read every content file, find what breaks the rule, and fail with a message that names it. Keep the test beside the content it guards, so the two move together.

Full referenceVitest docs: Getting Started

Looking inside

In Terrarium

The Blog's writing Skill, .agents/skills/blog-post/SKILL.md, asks every link to a file or a line to name a full commit SHA, never main. The Blog checks that with a unit test in its own layer, layers/blog/tests/unit/blog-immutable-links.spec.ts. It reads every Markdown file under the Blog's content and fails on any file link whose ref is not a commit SHA:

layers/blog/tests/unit/blog-immutable-links.spec.ts ↗21–26as of
21// Only file-serving GitHub routes take a ref that can be a branch; `commit`,
22// `pull`, `issues`, `compare`, … don't, so they can't rot this way.
23const FILE_ROUTE_LINK = /github\.com\/[^/]+\/[^/]+\/(?:blob|tree|raw|blame|edit)\/([^/\s)]+)\//g
24// An immutable pin is a hex commit SHA (7–40 chars). Anything else — `main`, a
25// feature-branch name, a tag — is mutable and rejected.
26const SHA = /^[0-9a-f]{7,40}$/

A first check fails if the walk finds no content at all, so a moved folder can't make the test pass by checking nothing. A second rule in the same file fails on a GitHub link to another Blog post, which must use the post's own site address instead.

The test sits in the 's own tests/unit/ folder, so pnpm test runs it with every other unit test (Testing). That makes it part of the : a post with a branch link fails the test step and can't merge.

This sits uneasily with .out-of-scope/ci-enforced-doc-invariants.md, which turns down Gate and CI checks that block a change over a documentation or metadata invariant, such as a doc that names a retired term, and prefers a guard that refuses the bad action when it is taken. The Blog's test is such a check in all but name: it adds no Gate step of its own, but it blocks through one. The record doesn't say where the line falls, so read the test as one the repo has kept, not as an exception the record allows.

Why it's built this way

The Gate: ADR-0004 (docs/adr/0004-objective-safety-gate.md).

Where a Tenant's tests live: tests/README.md.

What to watch for

  • The test is looser than the Skill. The Skill asks for a full 40-character SHA; the test accepts any hex run of 7 to 40 characters, so a short SHA passes.
  • A pinned link can still be wrong. The test checks the shape of the ref, not that the commit exists or holds the quoted lines. The Skill has the author copy the SHA from git output for that reason.
  • Only file links are checked. Commit, pull request and issue links name something that never changes, so the test skips them.

Try it in your project

Pick one rule from your project's writing guide that a script could check. Write it as a test that reads every content file and fails with the offending text in its message, then break the rule once on purpose to see the message.

Where next

Last updated · Latest source on GitHub ↗

Something went wrong

This content couldn’t be loaded. Reloading the page usually fixes it.

Technical details
route: /t/glassbox/drafts/verification/invariants-as-tests