Planning bug: a card's second half was inferred from a MODULE PATH — `lib/ai/codeContext` was read as "calls motir-ai" while its whole import closure is Prisma
Opened by Zhu Yue ·
Filed by the run of MOTIR-3719 (PR #2403), under the planner-bug home. No fix is owed in a repo — the correction is already applied to MOTIR-3719's body and to the shipped code comment. This is the telemetry record.
What the card claimed
MOTIR-3719 was filed as "a dead arm plus a live uncaught path". Its second half:
resolveCodeContext(...)can throw aMotirAiErrorand is issued before thetry… So the one liveMotirAiErrorpath on this page is uncaught — motir-ai being unreachable renders the server-error page rather than the degraded surface the code was written to show.
That claim also carried the card's whole priority argument, in explanationMd: "the live half is the cost".
It is false, and one command shows it
resolveCodeContext reaches no motir-ai client. On origin/main bed2bbc52:
$ grep -n "^import" lib/ai/codeContext.ts
1:import { githubInstallationRepository } from '@/lib/repositories/githubInstallationRepository';
2:import { githubRepoRepository } from '@/lib/repositories/githubRepoRepository';
3:import { withWorkspaceContext } from '@/lib/workspaces/context';
and the whole closure of those three is @/lib/db + @/generated/prisma/client. Every MotirAiError in the codebase is constructed in lib/ai/motirAiClient.ts (plus one site in projectLessonsService), which nothing on that path imports. The module's own header states it outright — "A DB read ONLY (the 891 mirror rows) — never a GitHub API round-trip on the submit path."
Not drift. git log --oneline e88d3d73..HEAD over the page, lib/ai/codeContext.ts and CodeHealthClient.tsx is EMPTY, so the three files are byte-identical between the card's base and main: the premise was false when written, not overtaken.
The mechanism, which is the part worth keeping
The claim was inferred from the module PATH, not from the module. The card's first half is exemplary — it enumerates every aiConventionService call site, walks all four paths through loadCodeHealthSurfaces, and proves the arm dead by exhaustion. The second half asserts a throw for a function under lib/ai/ whose name reads as an AI call, with no call site named and no import read.
Two properties make this hard to catch rather than careless:
- The two halves are stated in one voice. A reader who verifies the first half — which rewards verification richly — carries that confidence into the second, and the second is one sentence long.
- A directory is a plausible-looking authority.
lib/ai/holds the motir-ai client, its errors and its types;codeContextis genuinely for an AI planning job. The path is not misleading by accident, and it is still not evidence about what a function throws.
The cost, and why it is not zero
The false half is what made the card a defect rather than a cleanup: it set the priority argument and it framed the decision as a real fork (resolveCodeContext moves inside the try or the arm goes). With the premise falsified, option (a) is vacuous — moving a call that cannot raise a MotirAiError makes the arm no less dead — and the design asset had already settled the answer. The run spent its first block re-deriving a fork that had one arm.
What would have caught it
The card's own first half is the method: name the call site, or read the import closure. A claim that X throws Y is checkable in one grep for Y's construction sites plus one for X's imports — the same two commands that make the dead-arm proof convincing, applied to the sentence beside it. The authoring-side rule this belongs to is plan-rules/phase-deepen.md's precondition-verification limb: a claim about what a function CAN raise is a precondition, and a module path is not a reading of one.
Acceptance criteria
- Nothing to build. The correction is on MOTIR-3719 — the falsified section is struck through with its evidence rather than deleted,
explanationMd's cost argument is restated, and AC 2 names the limb that discharges it. app/(authed)/code-health/page.tsxcarries the finding at theresolveCodeContextcall site, so the next reader meets it instead of re-deriving it (PR #2403).
Discussion
No comments yet.
Adding to this discussion signs you in on app.motir.co and brings you back to this request.