Planning bug: a POST-DEPLOY verification child is discharged by the merge it exists to outlive — the story's completion cascades `done` onto it 27 seconds later
MOTIR-3010 exists because a migration's correctness is not established by merging it. Its own text says so: "this card is not dischargeable from a merged pull request alone."
It was discharged by a merged pull request.
19:00:56Z motir-core#2153 merged → MOTIR-2999 (the story) → done
19:01:23Z MOTIR-3010 blocked → done ← the DOWNWARD cascade, 27s later
blocked → done is not even an interactive edge on the default workflow; the completion cascade writes it directly (status-derivation — "a parent set Done completes its children, including one nobody started"). Nothing malfunctioned. Every mechanism did what it says.
And the claim it was supposed to check is currently FALSE. Read live from the deployed tenant at 19:03Z, the MOTIR project's workflow carries seven statuses — todo · blocked · in_progress · planning · in_review · done · cancelled — and no implemented. main's CI was still running, so the deploy had not happened. The verification card was green about a database that did not yet contain the thing it verifies.
Why this is a PLANNING bug and not a cascade bug
The cascade is right for the ordinary case: a parent that is genuinely done has children that are genuinely done. What is wrong is a plan that puts a post-deploy obligation inside a container whose completion is triggered by a merge — the two clocks are different, and the plan silently assumed one.
Every ingredient was already known and written down on the card itself. What was missing is a rule that makes the shape impossible rather than a paragraph that describes it.
The two candidate fixes — the first is the one to take
- PLANNING RULE (recommended). A card whose discharge depends on an event AFTER the merge —
type: verificationreading a deployed database, a published-artifact check, a live-tenant read — must NOT be a child a merge can complete. Either it hangs OUTSIDE the story (a sibling that the storyblocks), or the run must not complete the story while such a child is open. The runbook's parent-run close-out is where this bites: it names the story in the PR title, and the merge closes the story. - PRODUCT GUARD (weaker alone). The downward cascade could refuse to complete a
verification-type child, or any child whose own description declares a post-merge dependency. This narrows the blast radius but leaves the plan shape intact, so a second story hits it a different way.
Worth noting what did NOT save us: the card was blocked at the time, by a dependency that had legitimately just merged. Being blocked is not protection — the cascade completes children regardless of status.
Acceptance criteria
- The rule is written where a planner reads it before building a story —
motir-meta/prompts/plan-rules.md— and names the tell: a card that reads state the merge does not produce cannot be a child the merge completes. - The rule states which of the two placements is correct (outside the container, or the container stays open) and why, rather than leaving it to judgement per story.
- The runbook's parent-run close-out (
prompts/run.md) says what to do when the story carries such a child: the story does not complete on the merge. - A worked example cites this instance — MOTIR-3010, 27 seconds, and the live read that showed the claim false.
- If the product guard is also taken, it is a SEPARATE card in
motir-core, linked from here, so the rule ships whether or not the code does.
Context refs
- MOTIR-3010 — the card, its history, and the live read that contradicts its status
- MOTIR-2999 — the story whose merge cascaded
motir-meta/prompts/plan-rules.md·prompts/run.md— the two homes a rule like this needstests/e2e/status-derivation.spec.ts— "DOWNWARD — a parent set Done completes its children, including one nobody started", the shipped behaviour this collided with- Related: the
merged ≠ deployeddistinction, which this is the planning-side instance of