From aa5c3085e080c7c95c56867491c5ff49f1d6258e Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 29 Mar 2026 05:42:11 -0500 Subject: [PATCH 1/3] feat(dispatch): parallel research slices and parallel milestone validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dispatch rule that detects when multiple slices need research simultaneously and dispatches them in parallel via subagents. Also rewrite validate-milestone prompt to use 3 parallel reviewers (requirements, integration, UAT) for faster validation. - New dispatch rule: planning → parallel-research-slices (2+ ready) - buildParallelResearchSlicesPrompt with per-slice subagent prompts - Parallel research slices prompt template - Validate-milestone rewritten for 3 parallel reviewers --- src/resources/extensions/gsd/auto-dispatch.ts | 49 ++++++++++++ src/resources/extensions/gsd/auto-prompts.ts | 30 +++++++ .../gsd/prompts/parallel-research-slices.md | 23 ++++++ .../gsd/prompts/validate-milestone.md | 79 +++++++++++++------ 4 files changed, 158 insertions(+), 23 deletions(-) create mode 100644 src/resources/extensions/gsd/prompts/parallel-research-slices.md diff --git a/src/resources/extensions/gsd/auto-dispatch.ts b/src/resources/extensions/gsd/auto-dispatch.ts index 91918938f..36c50fff9 100644 --- a/src/resources/extensions/gsd/auto-dispatch.ts +++ b/src/resources/extensions/gsd/auto-dispatch.ts @@ -27,6 +27,7 @@ import { buildMilestoneFileName, buildSliceFileName, } from "./paths.js"; +import { parseRoadmap } from "./parsers-legacy.js"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { hasImplementationArtifacts } from "./auto-recovery.js"; @@ -46,6 +47,7 @@ import { buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, + buildParallelResearchSlicesPrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js"; @@ -350,6 +352,53 @@ export const DISPATCH_RULES: DispatchRule[] = [ }; }, }, + { + name: "planning (multiple slices need research) → parallel-research-slices", + match: async ({ state, mid, midTitle, basePath, prefs }) => { + if (state.phase !== "planning") return null; + if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research) return null; + + // Load roadmap to find all slices + const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP"); + const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; + if (!roadmapContent) return null; + const roadmap = parseRoadmap(roadmapContent); + + // Find slices that need research (no RESEARCH file, dependencies done) + const milestoneResearchFile = resolveMilestoneFile(basePath, mid, "RESEARCH"); + const researchReadySlices: Array<{ id: string; title: string }> = []; + + for (const slice of roadmap.slices) { + if (slice.done) continue; + // Skip S01 when milestone research exists + if (milestoneResearchFile && slice.id === "S01") continue; + // Skip if already has research + if (resolveSliceFile(basePath, mid, slice.id, "RESEARCH")) continue; + // Skip if dependencies aren't done (check for SUMMARY files) + const depsComplete = (slice.depends ?? []).every((depId) => + !!resolveSliceFile(basePath, mid, depId, "SUMMARY"), + ); + if (!depsComplete) continue; + + researchReadySlices.push({ id: slice.id, title: slice.title }); + } + + // Only dispatch parallel if 2+ slices are ready + if (researchReadySlices.length < 2) return null; + + return { + action: "dispatch", + unitType: "research-slice", + unitId: `${mid}/parallel-research`, + prompt: await buildParallelResearchSlicesPrompt( + mid, + midTitle, + researchReadySlices, + basePath, + ), + }; + }, + }, { name: "planning → plan-slice", match: async ({ state, mid, midTitle, basePath }) => { diff --git a/src/resources/extensions/gsd/auto-prompts.ts b/src/resources/extensions/gsd/auto-prompts.ts index 1ea0e3366..ea6a1863b 100644 --- a/src/resources/extensions/gsd/auto-prompts.ts +++ b/src/resources/extensions/gsd/auto-prompts.ts @@ -1749,6 +1749,36 @@ const GATE_QUESTIONS: Record = { }, }; +export async function buildParallelResearchSlicesPrompt( + mid: string, + midTitle: string, + slices: Array<{ id: string; title: string }>, + basePath: string, +): Promise { + // Build individual research-slice prompts for each slice + const subagentSections: string[] = []; + for (const slice of slices) { + const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath); + subagentSections.push([ + `### ${slice.id}: ${slice.title}`, + "", + "Use this as the prompt for a `subagent` call (agent: `gsd-executor` or the default agent):", + "", + "```", + slicePrompt, + "```", + ].join("\n")); + } + + return loadPrompt("parallel-research-slices", { + mid, + midTitle, + sliceCount: String(slices.length), + sliceList: slices.map((s) => `- **${s.id}**: ${s.title}`).join("\n"), + subagentPrompts: subagentSections.join("\n\n---\n\n"), + }); +} + export async function buildGateEvaluatePrompt( mid: string, midTitle: string, sid: string, sTitle: string, base: string, diff --git a/src/resources/extensions/gsd/prompts/parallel-research-slices.md b/src/resources/extensions/gsd/prompts/parallel-research-slices.md new file mode 100644 index 000000000..22c18d9f6 --- /dev/null +++ b/src/resources/extensions/gsd/prompts/parallel-research-slices.md @@ -0,0 +1,23 @@ +# Parallel Slice Research + +You are dispatching parallel research agents for **{{sliceCount}} slices** in milestone **{{mid}} — {{midTitle}}**. + +## Slices to Research + +{{sliceList}} + +## Mission + +Dispatch ALL slices simultaneously using the `subagent` tool in **parallel mode**. Each subagent will independently research its slice and write a RESEARCH file. + +## Execution Protocol + +1. Call `subagent` with `tasks: [...]` containing one entry per slice below +2. Wait for ALL subagents to complete +3. Verify each slice's RESEARCH file was written (check the `.gsd/{{mid}}/` directory) +4. If any subagent failed to write its RESEARCH file, re-run it individually +5. Report which slices completed research and which (if any) failed + +## Subagent Prompts + +{{subagentPrompts}} diff --git a/src/resources/extensions/gsd/prompts/validate-milestone.md b/src/resources/extensions/gsd/prompts/validate-milestone.md index 9653118a3..528959c66 100644 --- a/src/resources/extensions/gsd/prompts/validate-milestone.md +++ b/src/resources/extensions/gsd/prompts/validate-milestone.md @@ -1,46 +1,79 @@ -You are executing GSD auto-mode. +# Milestone Validation — Parallel Review -## UNIT: Validate Milestone {{milestoneId}} ("{{milestoneTitle}}") +You are the validation orchestrator for **{{milestoneId}} — {{milestoneTitle}}**. ## Working Directory Your working directory is `{{workingDirectory}}`. All file reads, writes, and shell commands MUST operate relative to this directory. Do NOT `cd` to any other directory. -## Your Role in the Pipeline +## Mission -All slices are done. Before the milestone can be completed, you must validate that the planned work was delivered as specified. Compare the roadmap's success criteria and slice definitions against the actual slice summaries and UAT results. This is a reconciliation gate — catch gaps, regressions, or missing deliverables before the milestone is sealed. +Dispatch 3 independent parallel reviewers, then synthesize their findings into the final VALIDATION verdict. This is remediation round {{remediationRound}}. If this is round 0, this is the first validation pass. If > 0, prior validation found issues and remediation slices were added and executed — verify those remediation slices resolved the issues. +## Context + All relevant context has been preloaded below — the roadmap, all slice summaries, UAT results, requirements, decisions, and project context are inlined. Start working immediately without re-reading these files. {{inlinedContext}} -{{skillActivation}} +## Execution Protocol -## Validation Steps +### Step 1 — Dispatch Parallel Reviewers -1. For each **success criterion** in `{{roadmapPath}}`, check whether slice summaries and UAT results provide evidence that it was met. Record pass/fail per criterion. -2. For each **slice** in the roadmap, verify its demo/deliverable claim against its summary. Flag any slice whose summary does not substantiate its claimed output. -3. Check **cross-slice integration points** — do boundary map entries (produces/consumes) align with what was actually built? -4. Check **requirement coverage** — are all active requirements addressed by at least one slice? -5. If **Verification Classes** are provided in the inlined context above, check each non-empty class: - - For each verification class (Contract, Integration, Operational, UAT), determine whether slice summaries, UAT results, or observable behavior provide evidence that this verification tier was addressed. - - Document the compliance status of each class in a dedicated verification classes section. - - If `Operational` verification is non-empty and no evidence of operational verification exists, flag this explicitly — it means planned operational checks (migrations, deployments, runtime verification) were not proven. - - A milestone with unaddressed verification classes may still pass if the gaps are minor, but the gaps MUST be documented in the Deferred Work Inventory. -6. Determine a verdict: - - `pass` — all criteria met, all slices delivered, no gaps - - `needs-attention` — minor gaps that do not block completion (document them) - - `needs-remediation` — material gaps found; remediation slices must be added to the roadmap +Call `subagent` with `tasks: [...]` containing ALL THREE reviewers simultaneously: -## Persist Validation +**Reviewer A — Requirements Coverage** +Prompt: "Review milestone {{milestoneId}} requirements coverage. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/REQUIREMENTS.md` (or equivalent requirements file). For each requirement, check the slice SUMMARY files in `.gsd/{{milestoneId}}/` to determine if it is: COVERED (clearly demonstrated), PARTIAL (mentioned but not fully demonstrated), or MISSING (no evidence). Output a markdown table with columns: Requirement | Status | Evidence. End with a one-line verdict: PASS if all covered, NEEDS-ATTENTION if partials exist, FAIL if any missing." -**Persist validation results through `gsd_validate_milestone`.** Call it with: `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verificationClasses` (when non-empty), `verdictRationale`, and `remediationPlan` (if verdict is `needs-remediation`). The tool writes the validation to the DB and renders VALIDATION.md to disk. +**Reviewer B — Cross-Slice Integration** +Prompt: "Review milestone {{milestoneId}} cross-slice integration. Working directory: {{workingDirectory}}. Read `{{roadmapPath}}` and find the boundary map (produces/consumes contracts). For each boundary, check that the producing slice's SUMMARY confirms it produced the artifact, and the consuming slice's SUMMARY confirms it consumed it. Output a markdown table: Boundary | Producer Summary | Consumer Summary | Status. End with a one-line verdict: PASS if all boundaries honored, NEEDS-ATTENTION if any gaps." + +**Reviewer C — UAT & Acceptance Criteria** +Prompt: "Review milestone {{milestoneId}} UAT and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/CONTEXT.md` for acceptance criteria. Check for UAT-RESULT files in each slice directory. Verify each acceptance criterion maps to either a passing UAT result or clear SUMMARY evidence. Output a checklist: [ ] Criterion | Evidence. End with a one-line verdict: PASS if all criteria met, NEEDS-ATTENTION if gaps exist." + +### Step 2 — Synthesize Findings + +After all reviewers complete, aggregate their verdicts: +- If ALL reviewers say PASS → overall verdict: `pass` +- If any reviewer says NEEDS-ATTENTION → overall verdict: `needs-attention` +- If any reviewer says FAIL → overall verdict: `needs-remediation` + +### Step 3 — Write VALIDATION File + +Write to `{{validationPath}}`: + +```markdown +--- +verdict: +remediation_round: {{remediationRound}} +reviewers: 3 +--- + +# Milestone Validation: {{milestoneId}} + +## Reviewer A — Requirements Coverage + + +## Reviewer B — Cross-Slice Integration + + +## Reviewer C — UAT & Acceptance Criteria + + +## Synthesis +<2-3 sentences summarizing overall findings and verdict rationale> + +## Remediation Plan + +``` If verdict is `needs-remediation`: -- After calling `gsd_validate_milestone`, use `gsd_reassess_roadmap` to add remediation slices. Pass `milestoneId`, a synthetic `completedSliceId` (e.g. "VALIDATION"), `verdict: "roadmap-adjusted"`, `assessment` text, and `sliceChanges` with the new slices in the `added` array. The tool persists the changes to the DB and re-renders ROADMAP.md. -- These remediation slices will be planned and executed before validation re-runs. +- Add new slices to `{{roadmapPath}}` with unchecked `[ ]` status +- These slices will be planned and executed before validation re-runs + +**You MUST write `{{validationPath}}` before finishing.** **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories. From db8a8b4ffb6e359733d052769f463b7262100442 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 29 Mar 2026 05:58:01 -0500 Subject: [PATCH 2/3] test(dispatch): add parallel research slices dispatch tests Structural tests verifying dispatch rule, prompt builder, template variables, and parallel milestone validation reviewers. --- .../tests/parallel-research-dispatch.test.ts | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts diff --git a/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts b/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts new file mode 100644 index 000000000..593488df9 --- /dev/null +++ b/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts @@ -0,0 +1,77 @@ +/** + * Parallel research slices dispatch — structural tests. + * + * Verifies the dispatch rule and prompt builder exist with correct structure. + */ + +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const dispatchSrc = readFileSync(join(__dirname, "..", "auto-dispatch.ts"), "utf-8"); +const promptsSrc = readFileSync(join(__dirname, "..", "auto-prompts.ts"), "utf-8"); +const templatePath = join(__dirname, "..", "prompts", "parallel-research-slices.md"); +const templateSrc = readFileSync(templatePath, "utf-8"); + +// ─── Dispatch rule ──────────────────────────────────────────────────────── + +test("dispatch: parallel-research-slices rule exists", () => { + assert.ok( + dispatchSrc.includes("parallel-research-slices"), + "dispatch table should have parallel-research-slices rule", + ); +}); + +test("dispatch: parallel-research-slices requires 2+ slices", () => { + assert.ok( + dispatchSrc.includes("researchReadySlices.length < 2"), + "rule should require at least 2 slices for parallel dispatch", + ); +}); + +test("dispatch: parallel-research-slices respects skip_research", () => { + const ruleIdx = dispatchSrc.indexOf("parallel-research-slices"); + const ruleBlock = dispatchSrc.slice(ruleIdx, ruleIdx + 500); + assert.ok( + ruleBlock.includes("skip_research") || dispatchSrc.slice(ruleIdx - 300, ruleIdx).includes("skip_research"), + "rule should check skip_research preference", + ); +}); + +// ─── Prompt builder ─────────────────────────────────────────────────────── + +test("prompt: buildParallelResearchSlicesPrompt exported", () => { + assert.ok( + promptsSrc.includes("export async function buildParallelResearchSlicesPrompt"), + "buildParallelResearchSlicesPrompt should be exported", + ); +}); + +test("prompt: builds per-slice subagent prompts", () => { + assert.ok( + promptsSrc.includes("buildResearchSlicePrompt"), + "parallel prompt builder should delegate to per-slice research prompts", + ); +}); + +// ─── Template ───────────────────────────────────────────────────────────── + +test("template: parallel-research-slices.md has required variables", () => { + assert.ok(templateSrc.includes("{{sliceCount}}"), "template should use sliceCount"); + assert.ok(templateSrc.includes("{{mid}}"), "template should use mid"); + assert.ok(templateSrc.includes("{{subagentPrompts}}"), "template should use subagentPrompts"); +}); + +// ─── Validate milestone prompt ──────────────────────────────────────────── + +test("template: validate-milestone uses parallel reviewers", () => { + const validateSrc = readFileSync(join(__dirname, "..", "prompts", "validate-milestone.md"), "utf-8"); + assert.ok( + validateSrc.includes("Reviewer A") && validateSrc.includes("Reviewer B") && validateSrc.includes("Reviewer C"), + "validate-milestone should dispatch 3 parallel reviewers", + ); +}); From d7e850c509e1f2d5cf4c42115275c106d5c7db3a Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 29 Mar 2026 06:02:13 -0500 Subject: [PATCH 3/3] test(prompts): update validate-milestone test for parallel reviewer structure The validate-milestone prompt was rewritten to use 3 parallel reviewers. Update the prompt contract test to verify the new structure instead of the old gsd_validate_milestone tool approach. --- .../extensions/gsd/tests/prompt-contracts.test.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/resources/extensions/gsd/tests/prompt-contracts.test.ts b/src/resources/extensions/gsd/tests/prompt-contracts.test.ts index 2c52a1da5..5b9c099d2 100644 --- a/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +++ b/src/resources/extensions/gsd/tests/prompt-contracts.test.ts @@ -181,11 +181,14 @@ test("reassess-roadmap prompt references gsd_reassess_roadmap tool", () => { assert.match(prompt, /gsd_reassess_roadmap/); }); -test("validate-milestone prompt persists verification classes through gsd_validate_milestone", () => { +test("validate-milestone prompt dispatches parallel reviewers", () => { const prompt = readPrompt("validate-milestone"); - assert.match(prompt, /verification classes section/i); - assert.match(prompt, /verificationClasses/); - assert.match(prompt, /gsd_validate_milestone/); + assert.match(prompt, /Reviewer A/); + assert.match(prompt, /Reviewer B/); + assert.match(prompt, /Reviewer C/); + assert.match(prompt, /Requirements Coverage/); + assert.match(prompt, /Cross-Slice Integration/); + assert.match(prompt, /UAT/); }); // ─── Prompt migration: replan-slice → gsd_replan_slice ────────────────