`buildGenerationSystemPrompt` takes a PHASE — the skeleton framing stops reaching a deepen session
The smallest card in the story and the only one that removes a CONTRADICTION rather than a cost.
The defect, measured
buildGenerationSystemPrompt(mode, extraContext, opts) takes a mode (initial / continue / augment) and has no phase parameter at all. runDeepenSession calls it with rulePacks: selector — so the PACKS are phase-scoped and the FRAMING around them is not. Measured on a real deepen system message for a translate card: 5,161 chars before the rule section, carrying three sections that are all skeleton instructions:
THE COMET SHAPE — one level per pass, drill the first childTHE ONBOARDING WORKFLOW — the 5-step TITLES-FIRST SKELETON, per levelTHE FIRST LEVEL IS EPICS (the onboarding root rule)
They instruct the model to "Plan EXACTLY ONE LEVEL this pass, then DRILL into the FIRST child" and name propose_node, complete_level and drill_into — three tools DEEPEN_PHASE_TOOLS does not contain. A deepen session is told to do things it is structurally unable to do.
This is not the same defect as the packs' and does not wait on the ACT decision: the evidence is the tool surface itself, and it needs no judgement.
⚠️ AMENDED 2026-08-29 (MOTIR-3918's own run) — the "5,161 chars" figure above is WRONG, and it UNDERSTATES the defect. It was measured by cutting the composed system message at the first occurrence of
SHARED PLANNING RULES— which is a CROSS-REFERENCE inside the framing ("…all in the SHARED PLANNING RULES below",RUNBOOK_PROMPToffset 5,217), not the start of the rule section. The command was right and the cut point was not: a denominator swap of exactly the shapecore.mdgate 2's QUANTITY limb names.Re-measured from the constants: the framing is 10,211 chars (the full deepen system message 127,705 minus the rule section 117,494), made of
RUNBOOK_PROMPT6,997 +GENERATION_FEWSHOT2,742 + the mode note and separators ≈472. Of that, the SKELETON-only portion is ≈8,416:
section chars RUNBOOK_PROMPT— THE COMET SHAPE610 RUNBOOK_PROMPT— THE ONBOARDING WORKFLOW (the 5-step TITLES-FIRST SKELETON)2,819 RUNBOOK_PROMPT— THE FIRST LEVEL IS EPICS747 RUNBOOK_PROMPT— THE ONBOARDING GROUNDING (the catalog epics are carved from)1,098 RUNBOOK_PROMPT— the RECONCILE half of YOUR RECONCILE + CAPTURE TOOLS≈400 GENERATION_FEWSHOT— "A level is authored skeleton-first (titles + edges, no prose)"2,742 The claim the figure supported is unchanged and is now larger: a deepen session is handed instructions it cannot follow. Planning bug MOTIR-3926.
Acceptance criteria
buildGenerationSystemPrompttakes the PHASE as an input rather than inferring it. Where the caller already passesrulePacks, the phase is read from that selector rather than accepted twice — one source, so the framing and the packs cannot disagree about which phase a session is.- A DEEPEN system message contains none of the three skeleton sections above, asserted on the assembled string by their own headings, and a SKELETON one still contains all three.
- A DEEPEN system message names NO tool absent from
DEEPEN_PHASE_TOOLS. Asserted by iterating that constant againstSKELETON_PHASE_TOOLSand searching the composed message — so a tool added to either surface later is covered without editing this test. - The framing a deepen session DOES need is identified and kept, not dropped wholesale: the card enumerates every section of the current framing with a verdict — skeleton, deepen, or both — and the PR carries that table. A section nobody can classify is the ACT decision's business, not this card's, and is named as such.
- The before/after size of the deepen framing is stated in the PR, and the deepen system message is measured end to end (it is 127,705 chars today for a
translatecard, of which 117,494 is rules). tests/plannerPromptAssemblyGate.test.tskeeps its existing assertions; the new ones go beside them, because that file is where the split is already asserted on real assembled strings.
Context refs
motir-aisrc/llm/treeGeneration.ts—buildGenerationSystemPromptand itsopts;runDeepenSession, which calls it;SKELETON_PHASE_TOOLS/DEEPEN_PHASE_TOOLS.motir-aitests/plannerPromptAssemblyGate.test.ts— the suite that records every session's system message before silencing the deepen ones, which is what makes this assertable at all.motir-aisrc/llm/planningRulePacks.ts—PlanningRuleSelector, which already carries the phase the framing needs.