Replaces resolvePlanningRulePacks and legalPlanningRuleCells with the model MOTIR-3891 states. No rule text moves on this card — this is the selector and its enumeration only, so the re-cut lands against a selector that is already correct and already guarded.
packs(phase, kind, type?) =
core always
+ phase-{skeleton|deepen} the phase
+ skeleton ⇒ kind packs by KIND, for every kind
+ deepen, kind ∈ {epic, story, bug} ⇒ kind packs
+ deepen, kind ∈ {task, subtask} ⇒ TYPE packs
At deepen a cell is a kind cell or a type cell, never both. That makes the space a SUM: 5 skeleton cells + 3 deepen-kind cells + 14 deepen-type cells = 22, against the 52 the cross-product currently enumerates.
PlanningRuleSelector cannot express an illegal cell: supplying a type with epic / story / bug, or omitting it for task / subtask at deepen, is REJECTED with the cell named — not ignored, and not silently defaulted.legalPlanningRuleCells() enumerates the sum, and a guard asserts the count against the arithmetic above rather than against a copied literal, so a new kind or type moves it on purpose.task cell and a deepen bug cell share no kind-or-type pack — the sum is real, not a renamed product.PACKS_BY_TYPE and any kind map are TOTAL over their enums by TYPE, not by convention — a new member must not compile until it is routed.motir-ai src/llm/planningRulePacks.ts — resolvePlanningRulePacks, legalPlanningRuleCells, PACKS_BY_TYPE, PLANNING_RULE_PACK_ORDER, and the module header's TOTAL RESOLUTION / DISJOINT COVER invariants.motir-ai tests/planningRulePacks.test.ts — the cell-count assertion and the type-axis suite added by MOTIR-3853.motir-ai tests/plannerPromptAssemblyGate.test.ts — no-orphan and REACHABILITY.