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.
Amoleo Milestones is registered in pendingApps as of 6 Oct 2026. It is the newest public family product and follows Home in the pending sequence; when promoted, pending public products retain their relative order and sit before Accounts. Neither Home nor Milestones is added to the enforced apps array until that product is actually ready to ship in the live family lockup. Amoleo Pulse is not part of this order: it is the self-hosted local runtime/satellite, not a public family product.
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.