Foundations
Live order — enforced by check-lockup.mjs
Pending — registered, not yet in any live footer
Tracked position for when each is ready, not enforced anywhere yet. Promote one with node tools/promote-to-lockup.mjs --key <key> once it's actually going to appear in every live footer — that's also the moment every currently-passing repo needs its own footer updated to match.
Registered surfaces
| Surface | Notes |
|---|---|
| Amoleo-Website | The four product cards on the front page — each an |
| Amoleo-Website | The APPS list scripts/make-og.py reads when it renders the family names along the bottom of the OG share card (assets/og.png). |
Product order — the one sequence the family lists in
Pets, Connections, Rissbrook, Accounts. Oldest first, Accounts last — it is the infrastructure the other three will eventually sit on, not a product launched alongside them, so it reads as what it is rather than as a fourth peer. This order is not a style choice re-litigated per surface; it is fixed, and every place that lists more than one product has to use it.
The order is recorded once, in tools/lockup.json's
apps array — the footer lockup's own spec file, reused here rather than
restated. That file already had to answer "what order do the products go in"
for the footer; giving that answer a second home would be exactly the
copy-and-drift this workspace exists to prevent. If the order ever changes,
lockup.json is the one place that changes.
This document, and
tools/check-product-order.mjs, cover
what check-lockup.mjs doesn't: surfaces that aren't the footer. The
footer's own order was already enforced — every repo's lockup is one element
check-lockup.mjs reads directly. The order silently stopped being enforced
the moment a second surface listed all four products, because nothing was
watching that surface. That is exactly how Website's front-page cards and
make-og.py's OG-image generator got built correctly, by hand, matching the
footer — and would drift the same quiet way the brand colours and the
wordmark did if a future edit reordered one without anyone noticing, because
neither file is the footer and neither was covered by the footer's checker.
1. Known surfaces
| Surface | Repo | File | Checked by |
|---|---|---|---|
| Footer lockup | every repo | (per repo — see family-footer.md) |
check-lockup.mjs (already existed) |
| Front-page product cards | Amoleo-Website |
index.html |
check-product-order.mjs |
| OG-image generator's family list | Amoleo-Website |
scripts/make-og.py |
check-product-order.mjs |
The second and third rows are recorded in
tools/product-order-surfaces.json,
which holds only where to look, not the order itself — same relationship
header.json has to wordmark.json's base size. Each entry is a file and a
regular expression that captures a product key each time the surface names
one; the checker reads the keys out in the order they appear and compares
that against lockup.json's order, filtered down to whichever products the
surface actually mentions.
When you build a new place that lists more than one product — a nav menu,
a sitemap, a "featured products" block, a future Accounts dashboard — add it
to product-order-surfaces.json in the same commit. An unlisted surface is
invisible to the checker in exactly the way the front-page cards and the OG
generator were before this file existed.
A surface that only ever shows one product (a product's own header, its
own OG card, its own footer's self-entry) has nothing to order and does not
belong in this file — see family-wordmark.md §8: "It isn't expected to
appear in Pets, Connections, or Rissbrook, each of which only ever talks
about itself." The same logic applies here.
2. Running the checker
node tools/check-product-order.mjs
For each registered surface: read the file, extract every product key the surface's pattern matches, in the order they appear, and compare against the canonical order. A surface that lists three of the four products is fine as long as those three are in the right relative order — the check is about sequence, not completeness (a nav that only links Pets and Connections isn't wrong for omitting Rissbrook; it would be wrong for listing them the other way around).
3. What it does not check
- The footer.
check-lockup.mjsalready owns that surface completely, order included — duplicating it here would be the second copy this whole document exists to avoid. - Prose.
CLAUDE.md,MissionStatement.md, and other running text mention the four products in whatever order reads best in the sentence. That is not the same act as rendering a list to a visitor, and checking it would produce constant false positives for zero real protection.
Generated from docs/family-product-order.md + tools/lockup.json + tools/product-order-surfaces.json. Edit there and re-run npm run build in docs-site-src/ — never hand-edit a page.