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
Back to roadmap
Done

(motir-core) A `modify` proposal's body cannot carry an intra-plan CHIP — materialize rewrites `motir-ref:planItem:` on `add`s only, so an amendment naming a card the same plan proposes ships an inert link

Bugopened by Zhu YueMOTIR-3804

Repo · motir-core. Found on 2026-08-28 while authoring the to-do-list plan (cmtdaoakn01fehvphc5q6s0dh), which modifys MOTIR-1344 to name a story the SAME plan proposes.

The defect

plansService.materialize's Pass 3 rewrites intra-plan item-link tokens — [label](motir-ref:planItem:<id>)[label](motir:<workItemId>) — inside

for (const { created, prefix } of createdAdds) { … rewriteIntraPlanRefs(created.descriptionMd …) … }

createdAdds holds add proposals only. A modify proposal's patch.descriptionMd / patch.explanationMd are applied to their target and never passed through rewriteIntraPlanRefs — verified at origin/main 268bb2a5b: git grep -n 'rewriteIntraPlanRefs' -- lib/services/plansService.ts returns exactly two call sites (lines 1325 and 1333), both inside that loop.

So a token in a modify body materializes verbatim, as a literal motir-ref:planItem:<id> href pointing at nothing. It does not chip, it does not resolve, and — unlike an add's dangling ref — it does not even reach the console.warn that reports one, because the rewrite never runs on it.

Why this is a PLANNING defect and not a niche one

It is the shape a re-plan takes by default. plan-procedure.md step 4 tells an author to "write every reference to another work item as an ITEM LINK, never a pure-text key", and to use motir-ref:planItem:<tempRef> for "an intra-plan sibling the generator is still proposing" — while THE REPLAN ACTION's own three ops are add the new, remove the superseded, and modify the survivor. Amending a survivor to name the card that took over part of its scope is the single most common thing a re-plan writes, and it is exactly the case the rewrite cannot reach.

The failure is silent in the worst direction. An author following the rule produces a broken link; an author who notices produces a prose reference and quietly violates the rule. This pass took the second road: MOTIR-1344's 2026-08-28 amendment names the new to-do-list story descriptively ("the sibling story this card is blocked_by") rather than as a chip, and says so in the body. That works and it is worse than a chip.

Fix direction

Run the same rewrite over a modify's patched bodies. The information is all present at Pass 3 — planItemToWorkItem is already built and already covers every add in the plan — so this is a second, small loop over the modifies whose patch carried a body, applying rewriteIntraPlanRefs to descriptionMd and explanationMd and surfacing an unresolved ref through the same console.warn. Consider whether autoRelateWorkItemMentions should run on a modified body too, or whether that would re-wire edges on a card the plan only meant to amend — decide it, do not leave it implicit.

Acceptance criteria

  1. A modify proposal whose patch.descriptionMd carries [label](motir-ref:planItem:<id>), where <id> is an add on the same plan, materializes with that token rewritten to [label](motir:<the created work item's id>) — asserted end to end through approvePlan, not on the helper.
  2. The same holds for patch.explanationMd.
  3. An unresolvable planItem: ref in a modify body is left inert and reported through the same console.warn path an add's dangling ref uses — asserted, so the two ops fail the same way.
  4. A modify body containing no such token is byte-identical after materialize (no incidental rewriting).
  5. Whether autoRelateWorkItemMentions runs over a rewritten modify body is decided and asserted — either it does and the relates_to edges appear, or it does not and a test says so. Not left to inference.
  6. git grep -n 'rewriteIntraPlanRefs' -- lib/services/plansService.ts shows the call sites covering both ops, and plansService's coverage stays at or above the per-file floor.

Context refs

  • lib/services/plansService.ts ~1315–1360 — Pass 3, the createdAdds loop, and the console.warn for a dangling ref.
  • lib/mentions/workItemRefs.tsINTRA_PLAN_REF_TOKEN_RE and rewriteIntraPlanRefs; the token form is [label](motir-ref:planItem:<planItemId>).
  • lib/dto/plans.ts:130 — the contract line promising motir:<id> / motir-ref:<tempRef> are "resolved at materialize", which is true of one op and not the other.
  • MOTIR-1344 — the card whose 2026-08-28 amendment had to fall back to a prose reference because of this.
  • MOTIR-3111 — the sibling defect in the same family: PlanItemPatch omitted explanationMd entirely, which made the FIELD unsettable on a modify. This one makes that field's CONTENT unresolvable. Same op, same blind spot, one layer apart.

Comments (0)

No comments yet — be the first to weigh in.

Sign in to comment

Reading is open to everyone; posting needs a Motir account.

Sign in