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

Planning bug: a card's FIX DIRECTION carries the authority of its root-cause evidence without the method — MOTIR-2763 mis-counted its referrer set (2 of 4) and named a test lane that cannot mount the element under test

Done
Description

Filed by motir run MOTIR-2763. The correction is already applied in that card's PR; this is the planner-telemetry record.

What was wrong

MOTIR-2763's Root cause section is exemplary and every one of its claims re-verified exactly on origin/main: three shipped facts at file:line, a measured table of control/orb rects at 1280×800, document.elementFromPoint resolving to the orb at both centres.

Its Fix direction section, written in the same register, made two forward-looking claims that nothing had checked. Both are false.

1. "⚠️ Two surfaces hard-code a viewport-relative height tuned against today's 24px" — there are four

#fileconstantin the card?
1app/(authed)/boards/_components/BoardColumn.tsx:142h-[calc(100dvh-12rem)]✅ named
2app/(authed)/plans/[id]/page.tsx:95h-[calc(100dvh-10rem)]✅ named
3components/planning/RoadmapView.tsx:169h-[calc(100dvh-13rem)]missed
4packages/design-system/theme.css:1242height: calc(100dvh - 14.5rem)missed

The card's own root-cause section records that its clearance grep covered "app/, components/, packages/design-system/ and app/globals.css" — but that grep was for orb/FAB clearance, and the height enumeration was evidently made over app/ alone. #4 is the sharpest miss: its neighbouring comment literally quotes the board column's 12rem, making it the single loudest referrer in the repo.

Shipping the enumerated two would have pushed the roadmap canvas and every 3d-immersive board past the fold — a regression introduced by the fix for a layout bug.

2. "Extend the shipped tests/e2e/home.spec.ts and issue-list-flow.spec.ts" — that lane cannot mount the orb

AC 4 requires the guard to "fail against the pre-fix layout". In those two files it cannot, ever:

  • showPlanWithAi = isMotirAiConfigured() && Boolean(activeProject), and isMotirAiConfigured() (lib/ai/availability.ts:15) is a server-side, process-wide read of MOTIR_AI_URL + MOTIR_AI_SERVICE_TOKEN. No per-test override, no client seam page.route can reach.
  • playwright.config.ts (the main lane, where both files run) sets neither. That is a standing decision, not an oversight, and it has its own regression guard: tests/e2e/ai-callout-gate.spec.ts asserts the orb's absence there and its header records why — adding the pair once mounted the AI affordances across every authed spec and broke the mobile settings drawer at 375px, where the orb intercepted the hamburger.

So a click({ trial: true }) guard placed as instructed would have passed on the unfixed code — there is no orb there to intercept anything — and stayed green forever. A criterion that reads as the card's strongest was unmeetable at the named location.

Disposition (already done in the MOTIR-2763 PR, not deferred)

  • All four height sites converted, and the clearance made a single inherited custom property so it stays conditional on the orb at all four — packages/design-system/theme.css is a stylesheet and cannot read a React prop, so a variable was the only mechanism that satisfies AC 3's "no dead space when AI is unconfigured".
  • The guard ships as tests/e2e/cloud-orb-clearance.spec.ts on the cloud lane (playwright.cloud.config.ts sets both AI vars), asserting the orb is mounted first so the hit test cannot pass vacuously.
  • MOTIR-2763's AC 4, fix-direction ⚠️ clause and context refs amended on the record with this evidence — as a COMMENT. Its DESCRIPTION still carried every one of the false claims until this card's run wrote them into it; see Close-out below.
  • Lesson written as notes.html #289#292. (Corrected 2026-08-17. #289 is the unrelated MOTIR-2757 lesson; the entry was located by CONTENT, not by number — citation collision is the norm here, not the exception.)

The rule this asks for

plan-rules/kind-bug.md (or type-bars.md's bug bar) should carry: a bug card's fix direction is a hypothesis, not a checklist. Two authoring bars follow —

  1. A counted enumeration ("the two files that…") must record the SEARCH that produced it, scoped to the whole repo (app/, components/, packages/, *.css), not just the directory the defect was found in. A count without its search is a guess wearing a number.
  2. A named test home must name the LANE and state that the lane can reach the asserted state. Where the element under test is gated by a server-side env read, the card must name the config that sets it — the failure mode is not a red test but a permanently green one.

Close-out (motir run MOTIR-2907, 2026-08-17)

The open question above — RULE or lesson? — was this card's real deliverable, and it is settled: SHARPEN. Not on the count axis but on the ORDER axis, which is the stronger one. plan-rules/type-test.md already carries "a type: test card must VERIFY every named SCAFFOLD it leans on … EXISTS in the repo the test RUNS IN and is REACHABLE from its runner" (MOTIR-849MOTIR-1445). It shipped, it is always-injected, and the miss happened anyway — because the card that named the test home was type: code, so the pack never loaded. A check already written as a rule that fails again is a TRIGGER gap, not a missing rule, and a widening is cheaper and more defensible than a new gate.

Placement deviates from the section above, deliberately. kind-bug.md / type-bars.md's bug bar would not have fired: the failure is not bug-specific — any card can carry a remedy section, and the general case is a code card with an "approach" heading. The limb landed in phase-deepen.md (phase = deepenany leaf, any kind, any type) as a PROSPECTIVE LIMB on the precondition-verification rule, whose steps 1–5 enumerate only what a card CONSUMES. A remedy's counted set and named test home are readable in the repo today, which makes each a step-2 discharge rather than a prediction. Plus tell (d) on type-test.md's scaffold rule, cross-referenced both ways.

  • motir-meta PR #212phase-deepen.md (+34), type-test.md (+11). Corpus baselines pinned BEFORE the edit: measure.py worst cell 2069/1991 → 2103/2024 — the overage grows by 1 line, not 34, because the cap is derived from the landing and both rise together. conserve.py goes 2 → 3 failing packs, which is MOTIR-2773 (it has no verdict for added), not a defect in the diff. core.md untouched — MOTIR-2878 and MOTIR-2702 both coordinate around that file.
  • Mirror: MOTIR-2924 (motir-ai), blocked_by this card. Target is the named constant VERIFY_EVERY_PRECONDITION in src/llm/planningRulePacks.ts, tagged phase-deepen in CORPUS_ORDERnot treeGeneration.ts, which only re-exports the composition now. The gap was measured, not asserted: prospect / retrospect / fix direction / counted / test home all 0 against a same-file baseline of precondition 24 · enumerat 19 · reachab 16.

Re-verified rather than accepted (rung 2)

the claim aboveverdict
all four height sites convertedorigin/subtask/MOTIR-2763-orb-clearance touches all four
tests/e2e/cloud-orb-clearance.spec.ts ships on the cloud lane✅ present, 269 lines
lesson written✅ authored and merged — but as #292
MOTIR-2763 "amended on the record"⚠️ comment only. Its description still read "⚠️ Two surfaces", named home.spec.ts / issue-list-flow.spec.ts in fix direction (4), scoped AC 4 to /items and /home, and listed two height files in its context refs. A description is the spec; a comment is not. Written into the description during this close-out — which is this card's own rule, applied to itself.