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-1801

Planning bug: `AgentRun` named TWO tables across the open-core boundary — 9.1.7/9.1.8 read motir-ai's metering store as the execution record, so the execution record was never owned

Done
Description

RECORD card — no PR. Captures a planning defect found on 2026-07-28 while planning the agent-runs foundation. The corrective cards are already created and the affected cards already re-scoped; this exists so the mistake is in the corpus.

What was wrong

The name AgentRun referred to two different tables in two different repos, and three Epic-9 cards were authored as if it were one:

  • motir-ai AgentRun — a METERING aggregate, generalized from PlanningRun (kind: planning | coding), holding token totals + credit cost. Owned by 9.1.6. Real and correctly planned.
  • motir-core AgentRun — the EXECUTION record: status, ordered events, PR link. Referenced by 9.1.7 ("persisted to the AgentRun + streamed"), 9.1.8 ("read from the 9.1.6 AgentRun"), and the Epic 9 body ("the token-usage report records an AgentRun") — and created by nothing.

Verified at rung 2: no AgentRun model exists in motir-core's prisma/schema.prisma on origin/main.

Why it was invisible

Every individual card read as complete. 9.1.6 legitimately owns a table by that name, so a reader checking "is AgentRun owned?" finds an owner and stops. The gap existed only between cards, in the assumption that one name meant one table — the same shape as the orphaned-deferral class, but disguised by a collision rather than by silence.

Two aggravating factors:

  1. The two tables cannot be merged, so this was never a naming nit. A browser-facing SSE stream in motir-core cannot read motir-ai's billing store across the open-core boundary, and a BYOK-local run never touches the gateway at all — it has no metering row while still needing a full execution record. Any card that "resolved" the reference by using 9.1.6's table would have been wrong in a way that only surfaced at implementation.
  2. Stale positional refs hid the hole. 9.1.8 cited "7.12.5 — the generalized AgentRun" and 9.1.7 cited "7.12.4 creditService". 7.12 has since drifted to contextual planning (MOTIR-911); metering + the ledger shipped under 7.2 / 7.2.5 / 7.2.7. A ref pointing at a plausible-sounding number that now means something else reads as "owned elsewhere" and stops the search.

The lesson

A shared TYPE NAME is a precondition to verify like any other — resolve it to a repo + a file, not to a card that mentions it. "Is X owned?" is answered by grepping the schema of the repo that must hold it, not by finding a card whose prose uses the name. In an open-core codebase, the same domain noun will legitimately exist on both sides with different meanings (execution vs metering), so the verification question is "which repo's X, and does that repo's schema have it?"

Corollary, already recorded elsewhere but reinforced here: cross-reference by stable key, never by positional number — the drifted 7.12.x refs actively concealed the gap.

What was done

  • Created the agent-runs foundation story under Epic 7 owning the motir-core EXECUTION record + the run surface, with its schema card carrying an explicit disambiguation note.
  • Re-scoped 9.1.6 (owns the metering record; must rename it or document which AgentRun it is; keys by runId; adds no motir-core columns), 9.1.7 (writes the shared ingest seam with origin: hosted; no hosted-only table/stream), and 9.1.8 (hosted MODE of the shared surface; cost joined by runId).
  • Corrected the stale 7.12.x refs on all three.
  • Added a permanent contract guard in the story vitest gate: no usage/token/cost column may exist on the motir-core record, so the two tables cannot be re-merged later.

Close-out

  • Confirm the re-scoped 9.1.x cards read correctly in the tenant.
  • Record the lesson in motir-meta/notes.html (a branch + PR — motir-meta is never committed to main directly) and add its lessons.base.ts row.
  • Decide whether "resolve a shared type name to a repo + file" is recurrent enough to promote from a LESSON to a RULE in plan-rules.md + SHARED_PLANNING_RULES; default is to leave it a lesson until it recurs.

Context refs

  • The foundation story and its schema card.
  • 9.1.6 · 9.1.7 · 9.1.8 — the three re-scoped cards.
  • motir-core/prisma/schema.prisma — where the execution record was absent; motir-ai/prisma/schema.prisma — where the metering record lives.