feat(sf): wire deep planning mode dispatch (PDD)
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) <noreply@anthropic.com>
This commit is contained in:
parent
5e8bdefbea
commit
d742602454
1 changed files with 74 additions and 0 deletions
|
|
@ -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 }) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue