Planning bug: the no-lift LIMB teaches an edge that makes a plan INVALID — a `blocked_by` is always same-level, and the limb lives in THREE homes including a seeded global lesson
Type · rule defect — the corpus instructs the planner to produce a dependency shape that is not valid. Found · 2026-09-01, settling a conflict surfaced while planning MOTIR-4085's plan-validity gate.
The rule, and the limb that contradicts it
DEPENDENCY_GRAPH_IS_THE_BUILD_SEQUENCE states the invariant correctly:
"Every
blocked_byedge is between two SIBLINGS of the SAME KIND under the SAME PARENT … NO edge crosses a level or a parent — a cross-parent need is a dependency one level UP."
Then it takes it back:
"AND WHEN NO LIFT IS LEGAL, WIRE THE LEAF EDGE AND RECORD THE REASON ON THE CARD. … A DEPENDENCY THAT CANNOT BE LIFTED IS STILL A DEPENDENCY, AND THE READY SET MUST HOLD THE CARD OUT."
A blocked_by is ALWAYS same-level. There is no case where no lift is legal — the planner can always lift the CARD to the level where the dependency can be expressed, rather than wiring an edge that cannot be.
⚠️ THE LIMB'S THREE CASES ARE SYMPTOMS OF A WRONG TREE, NOT EXCEPTIONS TO A RULE
That is the diagnosis, and it is why the limb reads so plausibly. Each case it enumerates is real — and each is the tree telling you something:
- "the nearest common level is so high that the edge would say one whole area blocks another because one leaf needs one leaf" — the two cards are in the wrong places. Lift the CARD.
- "the container that would receive the edge is already
blocked_bythe other side, so lifting makes a cycle" — a cycle is a genuine planning error. The answer is to fix the structure, not to route around it at the leaf. - "only one child of a container needs the work" — the container is doing two jobs. Split it.
In each, the limb lets the planner RECORD the symptom instead of fixing the cause — and produces a plan that a container-validity check will not accept. The corpus is instructing a shape the product rejects.
⚠️ A cross-level blocked_by can still exist in data — a person may create one by hand in the UI, and nothing here changes that. We do not PLAN that way. The constraint is on the planner, not on the schema.
⚠️ IT IS IN THREE HOMES, AND ONE IS A LESSON
Retiring it from the packs alone leaves the planner being taught the same thing through another channel:
| home | who it instructs |
|---|---|
motir-ai src/llm/planningRulePacks.ts — DEPENDENCY_GRAPH_IS_THE_BUILD_SEQUENCE (phase-lay) | the product's planner LLM |
motir-meta prompts/plan-rules/phase-skeleton.md:49 | the runbook planner |
motir-ai src/seed/lessons.base.ts:235–236 | both, via lesson injection — a seeded GLOBAL lesson |
⚠️ The two corpora are NOT mirrors ([[planner-rule-corpora-dont-mirror]] — they instruct different planners and a rule may legitimately have one home), so this is not a mirror sweep. All three carry it because the rule genuinely applies to both planners, and the lesson reaches them by a third path. A fix that misses the lesson is the one that looks done and is not.
Reproduce: grep -rain "no lift is legal\|cannot be lifted" prompts/ src/ in each repo. ⚠️ -a is required — plain grep returns nothing on planningRulePacks.ts.
Fix direction
As its own claim, with its own evidence: delete the limb and state the invariant without an exception — a cross-parent need is lifted, and where lifting is awkward the TREE is wrong and gets restructured.
⚠️ THIS IS A DELETION, NOT A RE-WORDING, which cuts against the standing ADD, never re-word convention for the rule corpora. That convention exists to stop drift; this is a correction of a rule that instructs an invalid outcome, which is the case it does not cover. Say so in the PR rather than letting the guard's spirit be violated silently.
⚠️ The GLOBAL lesson needs a MIGRATION, not just a seed-row edit — run.md's rule for a global lesson is "the seed row + migration". A tenant that already has the lesson injected keeps being taught the limb until the migration retires it.
Acceptance criteria
- The limb is gone from all THREE homes, verified by
grep -areturning nothing for "no lift is legal" / "cannot be lifted" / "wire the leaf edge" across both repos. - The invariant is stated without an exception: a
blocked_byis between siblings of the same kind under the same parent, and a cross-parent need is resolved by LIFTING — the card, or the dependency — never by a leaf edge. - The three cases are answered, not deleted. Each was a real situation and a planner will meet them again; the replacement text says what to do — restructure, fix the cycle, split the container — so removing the escape hatch does not leave the planner stuck where it used to have an answer.
- The seeded lesson is retired by MIGRATION, so existing tenants stop receiving it. Asserted on a tenant that already has it.
motir-ai's conservation baseline is updated in the same change, since it is what proves no rule was re-worded by accident — a deliberate deletion has to move it deliberately.- The composed-cost delta is stated for the affected cells; removing text is still a corpus change.
- A plan carrying a cross-parent leaf edge is REFUSED by container validity. ⚠️ If it currently passes, that is a SECOND defect and gets its own card — this one fixes what the planner is told, not what the validator accepts.
Scope BOUNDARY
- IN: the limb's removal from the three homes, the replacement guidance for its three cases, the lesson migration, and the conservation baseline.
- OUT: the VALIDATOR. Whether container validity already refuses a cross-parent leaf edge is a separate question, named above — this card is about what the corpus TEACHES.
- OUT: hand-created edges. A person may still create a cross-level
blocked_byin the UI; the schema is unchanged. - OUT: the rest of
DEPENDENCY_GRAPH_IS_THE_BUILD_SEQUENCE, which is correct and stays — the chain / fan / same-level shapes are untouched. - OUT: MOTIR-4085, which consumes the validity verdict and does not define it. It names this conflict and this card settles it.
Context refs
⚠️ grep silently returns NOTHING on src/llm/treeGeneration.ts and planningRulePacks.ts — use grep -a or the Read tool.
motir-aisrc/llm/planningRulePacks.ts—DEPENDENCY_GRAPH_IS_THE_BUILD_SEQUENCE, tieredphase-lay.motir-aisrc/seed/lessons.base.ts:235–236— the seeded global lesson repeating the limb in its own words.motir-metaprompts/plan-rules/phase-skeleton.md:49— the runbook planner's copy.motir-aitests/planningRuleConservation.test.ts— the text baseline this deletion must move.motir-metaprompts/run.md— the global-lesson rule (seed row + migration).- MOTIR-4085 — where the conflict surfaced: plan validity versus what the corpus permits.