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-2278Done

The no-ungoverned-operation guard — fail the build when an actor-initiated write reaches the DB with no permission gate

Turn the inventory from an audit into a contract. A document describing which operations are governed is true on the day it is written and drifts the first time someone adds a route; a test that fails when an operation reaches the database without passing a permission gate keeps it true.

This is the INVERSE of the orphan guard. The story test gate asserts every key has a consumer — no permission exists that nothing enforces. This card asserts every actor-initiated operation has a gate — no operation exists that no permission covers. Together they close the model in both directions; either alone leaves one side unguarded, and it is this side that let 82 of 122 services ship ungoverned.

The shape

  • Walk the operations, not the file names. Enumerate app/api/**/route.ts and the 'use server' actions, resolve each to the service method it calls, and fail when a method that writes on behalf of an actor is reachable without passing an assertCan* / permission check.
  • An explicit allowlist, and every entry carries a REASON. A background job, meter, sweep or webhook handler has no actor and legitimately has no gate; so does an operation the inventory decided is workspace-scoped. Those go in the allowlist with the inventory row that justifies them — not as a bare path list, which is how an allowlist becomes a place to hide things.
  • The allowlist is pinned to the inventory. An entry with no corresponding decision in docs/decisions/permission-inventory.md fails the test, so the two cannot drift apart.

Scope boundary

In: the guard, its allowlist, the allowlist↔inventory pinning, and the negative cases that prove the guard can fail.

Out: fixing the operations it finds ungoverned — this card MEASURES and records; wiring the gates is the per-domain split. If the guard's first run surfaces operations the inventory did not anticipate, log them as bugs rather than absorbing the fix here (notes.html #27).

Acceptance criteria

  • A Vitest guard enumerates every app/api/**/route.ts and every 'use server' action and resolves each to its service method — asserted by a count check, so a new route cannot slip past by not being enumerated.
  • The guard fails when an actor-initiated write path is reachable with no permission gate and no allowlist entry.
  • Every allowlist entry carries a written reason and a pointer to its inventory row; an entry without one fails the test.
  • The guard's negative case is proven: deliberately removing a gate from a governed service makes it go red. A guard that has never been seen to fail is not evidence.
  • The guard passes on this card's own branch — every operation in the repo at that point is either gated or carries a justified allowlist entry. (Stated against the branch, not against post-merge main: this card must be satisfiable inside its own scope.)

Context refs

  • The inventory — the document the allowlist is pinned to.
  • The catalog growth — the keys the gates will consult.
  • tests/planning/planChangeArchitecture.test.ts — the shipped precedent for a filesystem-walking architecture guard in this repo.
  • lib/services/projectAccessService.ts — the assertCan* surface a governed path passes through.