check:catalog + check:terms CI gates
Two CI gates that keep the Atlas Surface Operator substrate honest as it
grows. check:catalog enforces RFD-AGT-002 D-2/D-3:
every L1 surface declares a Page Card or an explicit unsupported
entry, and no card may claim a permission outside the real backend
namespace. check:terms enforces D-6: the retired
agent aliases stay dead in code.
Why these gates exist
RFD-AGT-002 D-2 forbids shipping a surface with neither a Page Card
nor an explicit unsupported declaration — silence makes
the model guess what a page can do. D-3 locks the backend permission
namespace to three write strings plus their reads; a card claiming a
fiction like firm:manage must be rejected. D-6 locks the
agent's name layers and bans zombie aliases
(assistant · bot · Orient · Atlas Presence Bar · app CCB)
in code, not just docs. These gates make all three checkable in CI.
What changed
-
src/lib/surface-registry/page-card/permissions.ts— the canonical backend permission set (the interim authority mirror of the RFD D-3 namespace, untilgen:permissionslands). -
src/lib/surface-registry/page-card/catalog.ts— the Tier-1 catalog index mapping every L1 (agent: "atlas") surface to a Page Card or anunsupportedreason, plus the purefindCatalogViolations()validator. -
src/lib/surface-registry/page-card/banned-terms.ts— the banned-alias list + the purescanForBannedAliases()matcher (whole-word for word aliases, literal for phrases) with a documentedcheck:terms-okline-level escape hatch. -
scripts/page-card/check-catalog.tsandscripts/page-card/check-terms.ts— the CLI gates, wired aspnpm check:catalog/pnpm check:termsand added to.github/workflows/ci.ymlright aftercheck:routes. -
Vitest mirrors (
catalog.test.ts,banned-terms.test.ts) sopnpm testenforces the same invariants locally. -
Annotated three pre-existing legitimate non-agent uses of "bot" (a
lucide robot glyph for an automated audit actor; a CAPTCHA error
message) with justified
check:terms-okmarkers.
The three acceptance criteria, proven
| Acceptance criterion | How it is enforced |
|---|---|
CI fails on a surface with neither a Page Card nor unsupported. |
findCatalogViolations diffs the L1 surface keys against the catalog; a missing key is a violation. Test: "fails when an L1 surface is missing from the catalog". |
CI fails on a card claiming a non-existent permission (e.g. firm:manage). |
Every card permission is checked against BACKEND_PERMISSIONS. Test: "fails on a card claiming a non-existent permission (firm:manage)". |
| CI fails on a banned alias in code. | scanForBannedAliases flags each banned alias on a whole-word/phrase match. Tests cover every alias + the no-false-positive case. |
Verification
Both gates pass on the current tree, and the full pipeline is green:
$ pnpm check:catalog
check:catalog OK — 22 L1 surfaces, all carded or explicitly unsupported.
$ pnpm check:terms
check:terms OK — no banned agent aliases in code.
$ pnpm lint # 0 errors (1 pre-existing unrelated img warning)
$ pnpm typecheck # clean
$ pnpm test # 121 files, 782 tests passed
$ pnpm build # success
- Gates are scripts (tsx + Node fs), so they run on the CI runner without ripgrep.
- Negative paths are unit-tested: each AC has a failing-case test that proves the gate actually rejects.
- No user-facing strings added (model-facing config, same i18n-exempt class as the surrounding surface descriptions); no color literals.
Scope notes
-
The permission set is hand-declared on purpose: the drift-proof
gen:permissionsgenerator (the D-2 build) is a separate Story this gate depends on. When it lands, the generated export replacespermissions.tsand the gate keeps its shape. -
The shipped
glossary.md(docs lane) is a sibling Story, not this one — this ticket is only the two app-side CI gates.
Files changed
11 file(s), +614 / -1 lines
| Path | Role | + | - |
|---|---|---|---|
| .github/workflows/ci.yml | Workflow | +11 | -0 |
| package.json | Config | +2 | -0 |
| scripts/page-card/check-catalog.ts | Script | +54 | -0 |
| scripts/page-card/check-terms.ts | Script | +84 | -0 |
| src/components/settings/audit-log/actor-badge.tsx | Component | +2 | -1 |
| src/lib/clerk-error-messages.ts | Library | +1 | -0 |
| src/lib/surface-registry/page-card/banned-terms.test.ts | Library | +38 | -0 |
| src/lib/surface-registry/page-card/banned-terms.ts | Library | +94 | -0 |
| src/lib/surface-registry/page-card/catalog.test.ts | Library | +76 | -0 |
| src/lib/surface-registry/page-card/catalog.ts | Library | +214 | -0 |
| src/lib/surface-registry/page-card/permissions.ts | Library | +38 | -0 |