Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-2277Done

Grow the catalog to the inventory — the full key set, its domains, and the `enforcement` marker

Take the inventory's map and make it the catalog. lib/permissions/catalog.ts currently holds eleven keys — the ones the catalog card derived from the eleven predicates in lib/projects/access.ts. This card grows it to every key the inventory justifies, and adds the one field that lets it do so honestly.

The shape

  • The full key set + domains. Every operation the inventory maps to a permission gets its key, in the same resource:action form, under the domain the inventory assigned it. The MOTIR-2260 guards (no duplicates, resource:action shape, every domain non-empty, both i18n catalogs total) all still apply — now over the larger set, unchanged in strength.

  • ⚠️ enforcement: 'enforced' | 'planned' on every entry. This is what resolves a real contradiction between two cards that are each individually right:

    • The story test gate guards that every PermissionKey has a production consumer outside lib/permissions/* — the ORPHAN guard, which is what stops a key existing that nothing enforces.
    • Re-pointing the ~150 assertCan* call sites onto the new keys is the per-domain split's job, deliberately not this story's.
    • So every key this card adds would be an orphan and turn that guard red.

    Resolve it in the MODEL rather than by weakening the guard: enforced means a gate consults it today, planned means the inventory justified it and the split has not wired it yet. The orphan guard then keeps full strength over enforced — nothing shipped loses protection — and planned becomes a commitment recorded in typed code instead of in a document that rots.

  • A planned key is never offered to a user. It is excluded from getRoleCatalog, so it cannot appear in the grid or, later, in the role editor. This is what keeps MOTIR-2260's principle intact across the gap: a switch never appears for something the code does not enforce.

  • The built-in role sets are re-derived over the grown catalog so that no actor's capabilities change. Admin holds every enforced role-gated key; Member and Viewer keep exactly what they hold today. The MOTIR-2261 parity truth table must pass unchanged — it is the proof that growing the vocabulary changed no behaviour.

Scope boundary

In: the catalog growth, the domains, the enforcement field, the i18n for every new key in both catalogs, the orphan-guard amendment, the role-set re-derivation.

Out: the inventory itself (the previous card); the no-ungoverned-operation guard (the next one); re-pointing any assertCan* call site (the split) — which is exactly why planned exists.

Acceptance criteria

  • lib/permissions/catalog.ts holds a key for every operation the inventory maps to one, each carrying its domain, its enforcement value, and its two i18n keys.
  • Every new key has a label AND a description in both messages/en.json and messages/zh.json; the shipped i18n parity test and the MOTIR-2260 totality guards pass unchanged over the larger set.
  • The orphan guard requires a production consumer for every enforced key, and is green. A test proves it still FAILS when an enforced key is deliberately left unconsumed.
  • A test pins the planned set against the inventory document, so a key cannot be parked as planned without a row justifying it.
  • getRoleCatalog returns no planned key — asserted directly, so an unenforced permission can never render as a switch.
  • The three built-in role sets are re-derived over the grown catalog and the MOTIR-2261 parity truth table passes unmodified — all 64 rows, all eleven predicates, no actor's capabilities changed.
  • Per-file coverage on every changed module meets the ≥90% floor.

Context refs

  • The inventory — the map this implements; its domain set is this card's grouping.
  • lib/permissions/catalog.ts · lib/permissions/builtinRoles.ts — what grows.
  • tests/permissions/catalog.test.ts — the MOTIR-2260 guards that must keep passing.
  • tests/permissions/accessParity.test.ts — the 64-row truth table that must pass UNMODIFIED.
  • The story test gate — owns the orphan guard this card amends.