From d7426024548e4281a560d26336d90cd2e7627692 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 19:42:41 +0200 Subject: [PATCH] feat(sf): wire deep planning mode dispatch (PDD) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the deep-mode rollout. With this commit, planning_depth: 'deep' in PREFERENCES.md produces a 4-stage project-level discussion BEFORE any milestone work — workflow-preferences → discuss-project → discuss-requirements → research-project (research-decision is auto- resolved to skip-default by SF's resolver, simpler than gsd-2's explicit user-decision gate). PDD spec for this change: Purpose: route auto-mode through project-level setup before milestones when planning_depth='deep'. When absent or 'light', existing dispatch is preserved 1:1. Consumer: auto-mode dispatcher (DISPATCH_RULES). One new rule sits at the top of the pre-planning ladder; existing rules unchanged. Contract: 1. planning_depth absent or 'light' → rule returns null → existing dispatch unchanged. Verified: returns 'not-applicable'. 2. planning_depth='deep' + empty project → dispatches workflow- preferences then progresses through stages as artifacts land. Verified: returns 'pending'/'workflow-preferences'. 3. status='blocked' → returns dispatch action 'stop' with the gate's reason — never silently bypasses a blocker. 4. status='complete' → returns null → milestone-level rules below take over. Failure boundary: if resolveDeepProjectSetupState() throws, return null and fall through to legacy rules. Never blocks the user on a helper crash. Evidence: typecheck passes; gate-resolver smoke test verifies all three contract conditions; existing dispatch tests unchanged (light-mode regression-protected). Non-goals: - In-flight idempotency markers for research-project (gsd-2 has these; SF's resolver auto-completes the stage when files land so the simple guard is sufficient — can add markers later if parallel orchestrator races emerge). - Plumbing structuredQuestionsAvailable through DispatchContext (defaulted to 'false' in builders for now; UI capability detection can be threaded later). Invariants: - Safety: light-mode + absent-prefs paths return null at the FIRST check, before any DB or filesystem access. No regression possible. - Liveness: the resolver enforces forward progress — once a stage's artifact lands, the next gate fires next dispatch cycle. Assumptions verified: - resolveDeepProjectSetupState exists in SF (deep-project-setup-policy.ts). - planning_depth: 'light' | 'deep' typed in preferences-types.ts:425. - All 4 dispatched unit types have builders in auto-prompts.ts (added in 5e8bdefbe). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/resources/extensions/sf/auto-dispatch.ts | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/resources/extensions/sf/auto-dispatch.ts b/src/resources/extensions/sf/auto-dispatch.ts index 4deedb775..630b0af18 100644 --- a/src/resources/extensions/sf/auto-dispatch.ts +++ b/src/resources/extensions/sf/auto-dispatch.ts @@ -15,6 +15,8 @@ import { buildCompleteMilestonePrompt, buildCompleteSlicePrompt, buildDiscussMilestonePrompt, + buildDiscussProjectPrompt, + buildDiscussRequirementsPrompt, buildExecuteTaskPrompt, buildGateEvaluatePrompt, buildParallelResearchSlicesPrompt, @@ -24,15 +26,18 @@ import { buildReassessRoadmapPrompt, buildRefineSlicePrompt, buildReplanSlicePrompt, + buildResearchProjectPrompt, buildResearchMilestonePrompt, buildResearchSlicePrompt, buildRewriteDocsPrompt, buildRunUatPrompt, buildValidateMilestonePrompt, + buildWorkflowPreferencesPrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js"; import { hasImplementationArtifacts } from "./auto-recovery.js"; +import { resolveDeepProjectSetupState } from "./deep-project-setup-policy.js"; import { getExecuteTaskInstructionConflict, skipExecuteTaskForInstructionConflict, @@ -738,6 +743,75 @@ export const DISPATCH_RULES: DispatchRule[] = [ }; }, }, + { + // Deep planning mode: the project-level setup gate runs before any + // milestone-level discuss/research/plan when planning_depth === "deep". + // resolveDeepProjectSetupState walks the staged-prerequisite chain + // (workflow-prefs → project → requirements → research-decision auto- + // resolved → project-research) and returns the next pending stage. Each + // stage's prompt writes its expected artifact, the gate flips the next + // time, and the milestone-level rules below take over when status = + // "complete" or planning_depth !== "deep". + name: "deep planning gate → project-level units", + match: async ({ state, basePath, prefs }) => { + if (prefs?.planning_depth !== "deep") return null; + if ( + state.phase !== "pre-planning" && + state.phase !== "needs-discussion" + ) { + return null; + } + let gate; + try { + gate = resolveDeepProjectSetupState(prefs, basePath); + } catch { + return null; // helper failure → fall through to legacy rules + } + if (gate.status === "not-applicable" || gate.status === "complete") { + return null; + } + if (gate.status === "blocked") { + return { + action: "stop", + reason: gate.reason ?? "Deep planning gate is blocked.", + level: "warning", + }; + } + // status === "pending" + switch (gate.stage) { + case "workflow-preferences": + return { + action: "dispatch", + unitType: "workflow-preferences", + unitId: "WORKFLOW-PREFERENCES", + prompt: await buildWorkflowPreferencesPrompt(basePath), + }; + case "project": + return { + action: "dispatch", + unitType: "discuss-project", + unitId: "PROJECT", + prompt: await buildDiscussProjectPrompt(basePath), + }; + case "requirements": + return { + action: "dispatch", + unitType: "discuss-requirements", + unitId: "REQUIREMENTS", + prompt: await buildDiscussRequirementsPrompt(basePath), + }; + case "project-research": + return { + action: "dispatch", + unitType: "research-project", + unitId: "RESEARCH-PROJECT", + prompt: await buildResearchProjectPrompt(basePath), + }; + default: + return null; + } + }, + }, { name: "needs-discussion → discuss-milestone", match: async ({ state, mid, midTitle, basePath }) => {