From 0c78b0038113c3136c76ca50ddd33f07bbc94bc9 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 19:14:21 +0200 Subject: [PATCH] feat(sf): wire ADR-011 progressive planning dispatch rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 'planning (sketch + progressive_planning) → refine-slice' rule in auto-dispatch.ts, fired BEFORE the existing 'planning → plan-slice' rule. Activates when: - state.phase === 'planning' - prefs?.phases?.progressive_planning === true - slice has is_sketch=1 in the DB When all three conditions hold, dispatches the refine-slice unit using the existing buildRefineSlicePrompt + prompts/refine-slice.md (both ported in earlier commits). Otherwise falls through to plan-slice (graceful downgrade — current behavior is preserved when the flag is off, which is the default). Why this matters: without progressive planning, the milestone planner has to either fully-plan every slice upfront (rots quickly) or hand- wave each slice (executors overscope). Sketch+refine lets the planner write 2-3 sentences of scope per slice and have refine-slice expand it just-in-time using prior slice summaries as context — keeping each plan sized for the actual current reality. Defensive read of slice.is_sketch with try/catch: pre-migration installs without the column simply fall through to plan-slice, no error. The DB DDL migration will land separately as part of the full progressive- planning rollout. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/resources/extensions/sf/auto-dispatch.ts | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/resources/extensions/sf/auto-dispatch.ts b/src/resources/extensions/sf/auto-dispatch.ts index 5144e09eb..4deedb775 100644 --- a/src/resources/extensions/sf/auto-dispatch.ts +++ b/src/resources/extensions/sf/auto-dispatch.ts @@ -22,6 +22,7 @@ import { buildPlanSlicePrompt, buildReactiveExecutePrompt, buildReassessRoadmapPrompt, + buildRefineSlicePrompt, buildReplanSlicePrompt, buildResearchMilestonePrompt, buildResearchSlicePrompt, @@ -60,6 +61,7 @@ import { getMilestone, getMilestoneSlices, getPendingGates, + getSlice, getSliceTasks, isDbAvailable, markAllGatesOmitted, @@ -976,6 +978,43 @@ export const DISPATCH_RULES: DispatchRule[] = [ }; }, }, + { + // ADR-011 progressive planning: when a slice was created as a sketch + // (slices.is_sketch=1) and the phases.progressive_planning preference is + // enabled, dispatch refine-slice instead of plan-slice. The refine unit + // expands the stored sketch_scope into a full plan using prior slice + // summaries as authoritative context. When the preference is off, sketches + // fall through to the normal plan-slice rule below — a graceful downgrade. + name: "planning (sketch + progressive_planning) → refine-slice", + match: async ({ state, mid, midTitle, basePath, prefs }) => { + if (state.phase !== "planning") return null; + if (!state.activeSlice) return null; + if (prefs?.phases?.progressive_planning !== true) return null; + const sid = state.activeSlice.id; + const sTitle = state.activeSlice.title; + let isSketch = false; + try { + const sliceRow = getSlice(mid, sid); + isSketch = sliceRow?.is_sketch === 1; + } catch { + /* DB unavailable or column missing on pre-migration installs — fall through */ + return null; + } + if (!isSketch) return null; + return { + action: "dispatch", + unitType: "refine-slice", + unitId: `${mid}/${sid}`, + prompt: await buildRefineSlicePrompt( + mid, + midTitle, + sid, + sTitle, + basePath, + ), + }; + }, + }, { name: "planning → plan-slice", match: async ({ state, mid, midTitle, basePath }) => {