(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
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
- A
modifyproposal whosepatch.descriptionMdcarries[label](motir-ref:planItem:<id>), where<id>is anaddon the same plan, materializes with that token rewritten to[label](motir:<the created work item's id>)— asserted end to end throughapprovePlan, not on the helper. - The same holds for
patch.explanationMd. - An unresolvable
planItem:ref in amodifybody is left inert and reported through the sameconsole.warnpath anadd's dangling ref uses — asserted, so the two ops fail the same way. - A
modifybody containing no such token is byte-identical after materialize (no incidental rewriting). - Whether
autoRelateWorkItemMentionsruns over a rewrittenmodifybody is decided and asserted — either it does and therelates_toedges appear, or it does not and a test says so. Not left to inference. git grep -n 'rewriteIntraPlanRefs' -- lib/services/plansService.tsshows the call sites covering both ops, andplansService's coverage stays at or above the per-file floor.
Context refs
lib/services/plansService.ts~1315–1360 — Pass 3, thecreatedAddsloop, and theconsole.warnfor a dangling ref.lib/mentions/workItemRefs.ts—INTRA_PLAN_REF_TOKEN_REandrewriteIntraPlanRefs; the token form is[label](motir-ref:planItem:<planItemId>).lib/dto/plans.ts:130— the contract line promisingmotir:<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:
PlanItemPatchomittedexplanationMdentirely, which made the FIELD unsettable on amodify. 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.