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

(motir-core) The `@planner-bug-home` marker resolves through a mutable child row, so a single re-parent silently stops it resolving

Done
Description

Repo: motir-core · ONE PR. Found 2026-08-05 while verifying that re-filing the captured-mistakes story into the agent-loop epic was safe.

⚠️ Corrected 2026-08-05 — the original impact claim was WRONG

This card first said the marker path was live and "every auto-filed planner bug 404s", so the inward loop, the outward loop and log_planning_bug could no longer file. That is false, and it was asserted without reading the deployment. Read off the platform:

fly machines list -a motir-ai   →   MOTIR_META_BUG_PARENT_KEY = 'MOTIR-1465'
fly secrets list -a motir-ai    →   no MOTIR_META_BUG_PARENT_KEY (nothing overrides it)

Production sends the literal numeric key, not the marker, so isPlannerBugHomeMarker() is false and fileBug takes the getWorkItemByIdentifier branch — a direct identifier lookup that never reads parentage. Filing has worked throughout, including during the window this card called an outage. MOTIR-1466 taught motir-core to resolve the marker but motir-ai was never re-pointed to send it; fly.toml's own comment records the unfinished handoff ("after which this is re-pointed").

So there is no outage — dropped from high to medium. What remains is a real latent defect on a path nothing currently takes, which must be fixed before that re-point happens.

The defect

aiWorkItemsService.fileBug resolves @planner-bug-home in two hops:

  1. find the epic titled PLANNER_BUG_HOME_EPIC_TITLE;
  2. take its first child of kind story as the bug parent.

Hop 2 returns nothing today: MOTIR-1464's ~26 children were all bug/task once the home story was re-parented, so the marker raised WorkItemNotFoundError → 404. getFirstChildOfKind(epic, 'story') is a positional read of mutable structure wearing the clothes of a stable marker — the same fragility the marker was introduced to escape when it replaced the numeric key. And because the consumer swallows a failed file by design, the failure would be invisible.

The fix — resolve the home STORY by its own title

Auto-filed bugs land in MOTIR-1465 (Yue, 2026-08-05). The marker resolves in ONE hop to the story whose title is PLANNER_BUG_HOME_STORY_TITLE, looked up project-wide, so where that story sits in the tree is irrelevant — no move_to_parent anywhere can void it. The epic is no longer a resolution input; getFirstChildOfKind is deleted from the service and the repository so the positional read cannot return.

The live story was renamed to exactly Captured planning-mistake bugs, so one literal is shared by the constant, the resolver and the migration's SQL.

Acceptance criteria

  • Resolving @planner-bug-home no longer depends on the home epic having a story child positioned first; the mechanism it does depend on is one a move_to_parent cannot void, and the card states which mechanism was chosen and why.
  • The marker resolves to MOTIR-1465 — auto-filed bugs land under that story, not the epic and not project-root.
  • A test drives resolution against a fixture whose home epic has only non-story children and asserts it still resolves — the exact state that was live, so the regression cannot recur unobserved.
  • A test re-parents the home story to an unrelated epic and asserts the marker still resolves to it.
  • A resolution failure is observable: it raises or logs at a level a human sees, rather than being swallowed on the background filing path. A test asserts the failure signal.
  • PLANNER_BUG_HOME_MARKER keeps its current literal value, so no motir-ai config change is required (the cross-repo contract in lib/ai/plannerBugHome.ts stays intact and this stays ONE repo, ONE PR).
  • The existing tests/integration/migrations/ensure-planner-bug-home.test.ts literal-sync assertion stays green.

Out of scope — the follow-up this uncovered

Re-pointing motir-ai's MOTIR_META_BUG_PARENT_KEY from MOTIR-1465 to @planner-bug-home is MOTIR-1466's unfinished half and is NOT this card — it ships in motir-ai (one env value + a deploy) and would straddle the repo boundary. Until it happens this fix changes nothing in production; after it happens, the numeric key stops dangling on a tenant rebuild. Needs its own card.

Context refs

  • lib/services/aiWorkItemsService.tsfileBug; the two-hop marker resolution.
  • lib/ai/plannerBugHome.tsPLANNER_BUG_HOME_MARKER, the title constants, and the header documenting the cross-repo config contract.
  • prisma/migrations/20260701130000_ensure_planner_bug_home/migration.sql — the one-shot backfill, and its own note that it is not a per-deploy guarantee.
  • app/api/internal/ai/work-items/route.ts — the service-auth route the marker arrives on (MOTIR-1450).
  • motir-ai fly.toml [env] + src/services/lessonService.ts — the consumer, still on the numeric key; unchanged by this card.