MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-2517

PLANNING BUG — a card read a migration's stated INTENT as the database's enforced STATE, so "nothing needs to be created, only adopted" survived every gate

Done
Description

Record card logged by THE REPLAN ACTION step 5 during motir run MOTIR-2435 on 2026-08-09. The correction is already applied; there is no PR and no code change. Recorded here so the corpus keeps the check rather than the anecdote.

What was assumed

MOTIR-2435 asserted, in bold, that "Nothing needs to be created — only adopted", named the Vitest fixtures as "the actual blocker … That is the bulk of the work; flipping DATABASE_URL is trivial by comparison", and placed "changing any policy" out of scope. It was sized 5 points / 120 minutes and read ready: true, openBlockers: [], validate_work_item valid: true, advisories: [].

The rung-2 evidence that it was false

Measured against a scratch database built by prisma migrate deploy from origin/main, and against the suite:

  • Four RLS tables — organization, organization_membership, workspace, workspace_membership — admit no INSERT. Every other RLS-enabled table covers all four verbs. As prodect_app, with the GUCs bound: ERROR: new row violates row-level security policy for table "organization". Sign-up and workspace creation are impossible under the role the card set out to adopt.
  • A permissive FOR INSERT WITH CHECK (true) is not the fix: INSERT … RETURNING still fails, because the SELECT policies apply to the returned row — and Prisma's create() always emits RETURNING.
  • The blocker was never only fixtures: 125 failure frames across three test directories, 55 of them in lib/ — application code. A 36-file sample failed 299 of 421 tests; 441 test files import @/lib/db.

The check that would have caught it

The card's premise was quoted from a MIGRATION COMMENT, and a migration comment states INTENT, not enforced state. The 2026-05-27 migration says, correctly and in good faith, "INSERT establishes a tenant rather than acting within one … Authorization for who can INSERT lives at the application layer." The card read that as the INSERT path is deliberately ungated. What the migration actually did was leave RLS enabled with no INSERT policy — which under a non-bypass role means denied. Intent and effect diverged inside one file, and only the database can tell you which one shipped.

This is the claim-vs-pointer gate, one layer down. That gate distrusts a ## Context refs line that EXPLAINS what a file contains, and its discharge is grep the file. Here the file WAS read, accurately, and quoted verbatim — so the gate is satisfied by its own terms and the card is still wrong. A code fact about a DATABASE is not discharged by reading the migration that created it; it is discharged by querying the database. pg_policies answers it in one query, and that query was never run by the plan-time pass — even though the same card ran five other live-database probes and tabulated them.

Two secondary defects, both already corrected:

  • Gate 14 (ACTOR axis) / notes.html #30"row_security_active returns true in every deployed environment" and "the password comes from the secret store" are world-state criteria on a coding_agent card, dischargeable only from a dashboard and a database console. Now MOTIR-2515.
  • The estimation gate — 5 points / 120 minutes for what is now four children plus an unknown number the inventory will file.

Fix applied — 2026-08-09

  • MOTIR-2435 re-scoped in place into a container; sizes cleared (a parent is sized by rollup); the "nothing needs to be created" premise and the "changing any policy" boundary amended on the record, with the measurement and its date, rather than deleted.
  • Four children created, wired as a chain: MOTIR-2512MOTIR-2513MOTIR-2514MOTIR-2515; the last three seeded blocked. (Amended 2026-08-10 during this card's close-out: this sentence described the tree for about four hours and no longer does — see "The tree has since moved" below. It is left as written because it is the record of what THIS replan did; the correction is additive.)
  • Nothing was archived — no card was superseded, only decomposed.
  • Lesson authored in motir-meta/notes.html (branch + PR; motir-meta PRs never carry a MOTIR- id).

Close-out condition

Verify the corrections above still hold in the tenant, and that the lesson is on origin/main — locate it by content, not by its number, which drifts on append collisions. Nothing else gates this card; in particular do not wire it blocked_by its own subject.


Close-out verification — 2026-08-10

1. The lesson is on origin/main — located by content

notes.html carries "Read a migration's stated INTENT as the database's enforced STATE", merged as motir-meta#155 (docs/migration-comment-is-intent-not-state) at 2026-08-09T23:49:33Z. Its Lesson and Prompt-engineering-hint sections carry the check, and it contains no conditional "on recurrence, do X" clause — the notes.html #221 trap is not armed here.

The number drifted exactly as this card predicted, and worse than predicted. origin/main holds 249 entries numbered up to 248, with TWO entries numbered #248: this lesson, and motir-meta#156's vendor-retirement lesson, which merged 3m39s later and kept its own number. The corpus footer still reads "Across the 248 mistakes". Filed as MOTIR-2531 — a defect in the shared corpus found during this run, logged rather than absorbed.

2. The rung-2 evidence — RE-MEASURED against a database, not re-read

The claim this card is about is a claim about a running system, so it was discharged the way the lesson says: a scratch PostgreSQL 15.18 built by applying all 123 origin/main migrations, then queried.

claimverdict
Exactly four RLS tables admit no INSERTEXACT. 66 RLS-enabled public tables; exactly organization, organization_membership, workspace, workspace_membership lack a PERMISSIVE policy with cmd IN ('ALL','INSERT').
"Every other RLS-enabled table covers all four verbs"EXACT. 62 of 62 others cover SELECT/INSERT/UPDATE/DELETE — zero holes. The four are uniquely holed.
ERROR: new row violates row-level security policy for table "organization" as the non-bypass role with the GUCs boundEXACT, verbatim. Reproduced.
FOR INSERT WITH CHECK (true) is not the fix — bare INSERT passes, INSERT … RETURNING failsEXACT. Reproduced: INSERT 0 1 for the bare statement, the same RLS error for the RETURNING form.
The migration comment quotationEXACT, verbatim at prisma/migrations/20260527134009_add_workspace_rls/migration.sql"Authorization for who can INSERT lives at the application layer (signup hook in 1.2.4, invite acceptance in 1.2.5)."
The role's attributesEXACT. prodect_app exists, rolbypassrls = f, rolsuper = f. motir_app does not exist on origin/main — consistent with MOTIR-2519's rename still being In Review.
"441 test files import @/lib/db"CORROBORATED, unit restated. On origin/main today git grep -l "@/lib/db" returns 588 files, of which 448 are test-shaped by name. 441 vs 448 is 1.6% — within the definition-variant band, not drift.
125 failure frames / 55 in lib/; 36-file sample 299 of 421NOT re-measured — reproducing these needs MOTIR-2513's two-client harness, which is still In Review. Recorded as unre-run rather than confirmed.

Every measurable claim on this card is exact. That is worth saying plainly rather than implying a drift that was looked for and not found.

3. The tree has since moved — the "Fix applied" shape is no longer the tenant's

The corrections HELD: MOTIR-2435 is a container, storyPoints 5 → — at 2026-08-09T23:40:48Z per its own history trail, estimateMinutes null, three ▶ AMENDED blocks on the record, and nothing archived. But the child set changed within hours, and the record should say so:

  • MOTIR-2519 was added at the HEAD of the chain — the prodect_appmotir_app rename, at Yue's request. MOTIR-2435's children are now 2519, 2512, 2513, 2514.
  • MOTIR-2515 moved OUT. It is now a task under Epic 8, blocked_by MOTIR-2435 — not a child. It was carved out precisely because its criteria are world-state ones (the gate-14 ACTOR defect this card records), and a manual cutover card does not belong inside a coding_agent container's commit sequence.
  • The 2512 → 2513 direction reversed. MOTIR-2513 (the two-client harness) is now the ready head; MOTIR-2512 is blocked_by 2513 and 2519, and MOTIR-2514 is blocked_by 2513. The harness is the instrument the policy fix is tested with, so it has to come first.

None of this falsifies the correction — it is the correction being executed. It is recorded because "four children in a chain ending at 2515" now points at a shape the tenant does not have.

4. Promote verdict — SHARPEN (a widening), though this card posed no question

Run and recorded so the next reader does not re-open it. All three candidate rules were on main before MOTIR-2435 was authored (createdAt 2026-08-07T20:10:09Z): the UNVERIFIABLE limb e09ffa6 2026-08-04, the claim-vs-pointer asserted-ABSENCE limb 2eb70d1 2026-08-05, gate 13's third limb 0192e05 2026-08-04. Under the ordinary discriminator that reads as a diligence miss ⇒ LESSON. It is not, on three grounds:

  1. The gate RAN and returned a false green. The claim-vs-pointer gate's sole discharge is one git grep on origin/main; the card performed it correctly and it returned the exact text quoted above. A rule that runs and under-delivers is a trigger problem; a rule nobody ran is a diligence problem.
  2. The enumeration stops one item short, between two adjacent limbs of one list. The precondition ladder's step 2 names only repo actions as discharges; its step 5 fires only for "a running system you have no read path to at all". This card had a read path — five live probes — so step 5 correctly stayed silent. A premise about a running system you CAN read has no named discharge anywhere.
  3. The twin limb already shipped and survived. Gate 13's A CONFIG FILE IS A CLAIM ABOUT THE DEPLOYMENT is the identical check for the platform substrate, accepted and not reverted; the lesson itself calls this "the same distinction … moved from the platform to the database." pg_policies / information_schema / row_security_active / pg_roles return 0 in BOTH homes.

No existing owner card (text contains "plan-rules.md", not done/cancelled → 6 hits, none owning a rule amendment) and no open motir-meta PR, so the house pair was filed fresh: MOTIR-2529 (motir-meta, plan-rules.md) → MOTIR-2530 (motir-ai, SHARED_PLANNING_RULES, blocked_by it, seeded blocked).