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

Planning bug: MOTIR-3721 inherited its reader inventory from the ADR that authorised the retirement, so the readers the replacement CANNOT serve were never counted

Done
Description

The planning bug THE REPLAN ACTION on MOTIR-3721 owes (plan-procedure.md step 5). The replan itself is done — the card is re-scoped, split and re-parented by plan cmtcmifsv0076hwphxsvvgmpw; the lesson is recorded. This card carries the part the replan could not settle: whether the RULES tier needs a check, or whether this is gate 2 working as designed and simply not run.

What happened

MOTIR-3721 was authored to retire TWO columns, with a four-row reader table:

| githubPullRequestRepository.countOtherOpenByWorkItem | … | | githubPullRequestRepository.listCompletionFactsByWorkItem | … | | workItemRepository.findBySessionBranch | work_item.session_branch | … | | the Development surface's row source | … |

That table is the ADR's, near-verbatim — docs/decisions/work-item-delivery-links.md Q2 lists exactly the readers it was MOVING, card by card. The card inherited it as if it were an inventory of the readers that EXIST.

git grep sessionBranch lib app on origin/main returns at least six, and they are not one kind. The two the ADR's table never had reason to mention are the consequential ones:

  1. workItemLinkRepository.findBlockerSessionBranchesForItemsblockerReadiness.inheritedSessionBranch. Reads a card's BLOCKERS' branches to answer which lineage should this not-yet-started card join? The card has no pull request; its blocker may have none either — motir auto writes session_branch at mark_integrated and opens the pull request at the first IMPLEMENTED card, and the second card of a run is dispatched inside that window. work_item_delivery cannot answer it: there is no delivery row to read.
  2. The v1 CONTRACT. inheritedSessionBranch is on the ready row at contract version 1.6.0 (lib/api/v1/contractVersion.ts) and on the MCP payload; ReadyItemDispatchDto.sessionBranch is published too. Dropping the column is a versioned contract change with a CLI release story, which the card does not mention.

So AC 1 ("No reader … remains") and AC 2 ("Both columns are dropped") were unbuildable as written, and the card would have been discovered wrong by whoever ran it — which is what happened, at guard #4, in the parent run of MOTIR-3672.

Why this is worth a card rather than only a lesson

The existing rule already covers it and did not fire, and the reason is interesting. plan-rules/core.md gate 2 says a precondition is VERIFIED against shipped reality and "an abstract name in the plan is a CLAIM, not a shipped symbol." Every name in that reader table IS a shipped symbol — each one greps to a real function. The table is false as a SET, not as a list of claims, and gate 2 reads claims one at a time.

That is a gap worth deciding about, because the shape recurs wherever a card inherits an enumeration:

  • phase-deepen.md's ENUMERATION limb already says a card COUNTING a population owes the ref the count was taken on. A reader table IS a count, and this card's carried no ref — it carried a citation to a decision.
  • op-replan.md's (d) limb says a sweep is not its grep pattern and its silence is a result, never a scope verdict. The mirror case is untreated: an inventory INHERITED from a decision is not a search at all, and it reads as more authoritative than a grep because a human wrote it deliberately.

Candidate fixes — pick one, this card is where that is decided

  1. Extend the ENUMERATION limb: a reader / caller / consumer table in a card body is a COUNT and owes the command that produced it, on the card — git grep <symbol> <paths> and its result — even when every row is individually true.
  2. Extend gate 2 with a SET arm: verify not only that each named thing exists, but that nothing exists which the list omits, whenever the card's own criteria quantify ("no reader remains", "every consumer", "all callers").
  3. Nothing — say so. Argue that a card quoting an ADR's table is doing the right thing and the failure was simply gate 2 not being run, in which case this is a lesson and not a rule change.

The perverse incentive to name out loud: the better-written the decision, the more complete its table LOOKS, and the less likely anyone is to re-derive it. work-item-delivery-links.md Q2 is a careful, well-argued table — and that is exactly what made it safe to copy.

Evidence

  • docs/decisions/work-item-delivery-links.md Q2 — the source table, listing the readers each card of MOTIR-3655 moved.
  • git grep sessionBranch lib app on origin/main (94b41e7a1) — six reader sites; lib/repositories/workItemLinkRepository.ts:408 is the one the delivery table cannot serve.
  • lib/api/v1/ready/schema.ts:153 · lib/api/v1/contractVersion.ts:56 — the published field and the version that added it.
  • Found at guard #4, motir run MOTIR-3672, 2026-08-28, before any worktree was opened for the card.