Planning bug: a reversed direction was recorded as a COMMENT on the decision card it governed, leaving four of its seven criteria demanding the rejected shape
Found by motir run MOTIR-3876 on 2026-08-29, at guard #4, before a worktree was opened for the card. The correction is submitted as plan cmterykg000hjhvn8yhp81kom; this card is the telemetry.
The defect
The epic's direction was reversed in conversation on 2026-08-29 — motir-core is GPL-3.0 and may not carry Motir's marketing material, so surfaces move OUT of it rather than in. I recorded that decision as a comment on MOTIR-3879, the ADR card it governs, and moved on to re-planning the epic.
A comment is not a field. The card's own body still read "A — one origin per hostname; motir.co is served by motir-core, and the landing moves into it. RECOMMENDED", and four of its seven acceptance criteria still demanded the superseded shape:
- "Options B, C and D each have a rejected-alternatives row" — the decided arrangement is B-shaped.
- "whether the landing is ported or packaged" — moot; the landing is not moving.
- "marks
/p/*as DEFERRED to its own story … so no reader takes silence for a decision" — MOTIR-3877 had since decided it. - "The record does not decide
/p/*'s namespace" — the same, restated as its own criterion.
Why nothing caught it
readiness.ready, validate_work_item and the advisory array all read clean on MOTIR-3879 throughout — correctly. Every one of them is computed from edges, statuses and structured values, and nothing about the card's structure had changed. The defect lived entirely in the relationship between a comment and a criteria list, which no tier reads together.
And the card read BETTER afterwards, not worse. It now carried the decision, its reasoning and its date, so it looked like a card somebody had just been over — while the executable half still pointed at the rejected answer.
What made it worse than ordinary staleness
The card's deliverable is itself a DECISION, so its body carries its recommended option as an argument. A stale decision card does not go empty; it becomes a researched case for the answer that was rejected. Running it as written would have produced a record recommending the wrong arrangement. Running it "sensibly" — ignoring the stale half — would have produced a record that silently dropped proxy-matcher.test.ts, the strongest objection to the arrangement actually chosen. Neither is a good outcome, and only stopping was.
The still-open question this bug does NOT settle
Is there a mechanised form? The by-hand check is does this card's own comment thread record a decision its criteria contradict? That is a two-source read no current advisory family performs — reference scans a description for not-done keys, shape reads criteria against themselves, and neither looks at comments. A candidate: a staleness advisory that fires when a card's most recent comment post-dates its updatedAt body edit and its criteria contain a term the comment marks as decided-against. Cheap to state and easy to make noisy; sizing and design belong to whoever picks it up. It is a candidate, not a decision.
Acceptance criteria
- A decision is recorded, either way, on whether a comment-versus-criteria contradiction becomes a
validate_work_itemadvisory or stays a runbook step at guard #4. Both outcomes close this card; silence does not. - If it becomes an advisory, it fires on the fixture this card records (a card whose comment records a decision its criteria contradict) and does NOT fire on a card whose comment merely adds context or evidence — the common, healthy case.
- The lesson written for this occurrence is not duplicated by whatever is decided; a rule and a lesson are different tiers.
Context refs
- MOTIR-3879 — the card this happened to; its comments carry both the decision and the guard-#4 diagnosis
- MOTIR-3876 — the story being run when it was found
motir-meta/prompts/run.md— guard #4, which caught it by handmotir-aiPR #336 — the global lesson recorded for this occurrence