diff --git a/src/resources/extensions/gsd/bootstrap/db-tools.ts b/src/resources/extensions/gsd/bootstrap/db-tools.ts index be4aa072e..8e6e490d2 100644 --- a/src/resources/extensions/gsd/bootstrap/db-tools.ts +++ b/src/resources/extensions/gsd/bootstrap/db-tools.ts @@ -924,7 +924,7 @@ export function registerDbTools(pi: ExtensionAPI): void { promptSnippet: "Validate a GSD milestone (DB write + VALIDATION.md render)", promptGuidelines: [ "Use gsd_validate_milestone when all slices are done and the milestone needs validation before completion.", - "Parameters: milestoneId, verdict, remediationRound, successCriteriaChecklist, sliceDeliveryAudit, crossSliceIntegration, requirementCoverage, verdictRationale, remediationPlan (optional).", + "Parameters: milestoneId, verdict, remediationRound, successCriteriaChecklist, sliceDeliveryAudit, crossSliceIntegration, requirementCoverage, verificationClasses (optional), verdictRationale, remediationPlan (optional).", "If verdict is 'needs-remediation', also provide remediationPlan and use gsd_reassess_roadmap to add remediation slices to the roadmap.", "On success, returns validationPath where VALIDATION.md was written.", ], @@ -936,6 +936,7 @@ export function registerDbTools(pi: ExtensionAPI): void { sliceDeliveryAudit: Type.String({ description: "Markdown table auditing each slice's claimed vs delivered output" }), crossSliceIntegration: Type.String({ description: "Markdown describing any cross-slice boundary mismatches" }), requirementCoverage: Type.String({ description: "Markdown describing any unaddressed requirements" }), + verificationClasses: Type.Optional(Type.String({ description: "Markdown describing verification class compliance and gaps" })), verdictRationale: Type.String({ description: "Why this verdict was chosen" }), remediationPlan: Type.Optional(Type.String({ description: "Remediation plan (required if verdict is needs-remediation)" })), }), diff --git a/src/resources/extensions/gsd/prompts/validate-milestone.md b/src/resources/extensions/gsd/prompts/validate-milestone.md index 82059091b..9653118a3 100644 --- a/src/resources/extensions/gsd/prompts/validate-milestone.md +++ b/src/resources/extensions/gsd/prompts/validate-milestone.md @@ -26,7 +26,7 @@ All relevant context has been preloaded below — the roadmap, all slice summari 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 your verdict rationale. + - 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: @@ -36,7 +36,7 @@ All relevant context has been preloaded below — the roadmap, all slice summari ## Persist Validation -**Persist validation results through `gsd_validate_milestone`.** Call it with: `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verdictRationale`, and `remediationPlan` (if verdict is `needs-remediation`). The tool writes the validation to the DB and renders VALIDATION.md to disk. +**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. 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. diff --git a/src/resources/extensions/gsd/tests/prompt-contracts.test.ts b/src/resources/extensions/gsd/tests/prompt-contracts.test.ts index aef179b77..2c52a1da5 100644 --- a/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +++ b/src/resources/extensions/gsd/tests/prompt-contracts.test.ts @@ -181,6 +181,13 @@ 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", () => { + const prompt = readPrompt("validate-milestone"); + assert.match(prompt, /verification classes section/i); + assert.match(prompt, /verificationClasses/); + assert.match(prompt, /gsd_validate_milestone/); +}); + // ─── Prompt migration: replan-slice → gsd_replan_slice ──────────────── test("replan-slice prompt names gsd_replan_slice as the tool to use", () => { diff --git a/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts b/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts index f78879e15..120751b60 100644 --- a/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +++ b/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts @@ -1,6 +1,6 @@ import { describe, it, afterEach } from "node:test"; import assert from "node:assert/strict"; -import { mkdirSync, existsSync, rmSync, writeFileSync } from "node:fs"; +import { mkdirSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { randomUUID } from "node:crypto"; @@ -24,6 +24,7 @@ const VALID_PARAMS = { sliceDeliveryAudit: "| S01 | delivered |", crossSliceIntegration: "No issues", requirementCoverage: "All covered", + verificationClasses: "- Contract: covered\n- Integration: covered\n- Operational: gap noted", verdictRationale: "Everything checks out", }; @@ -59,6 +60,27 @@ describe("handleValidateMilestone write ordering (#2725)", () => { // Disk file exists const filePath = join(base, ".gsd", "milestones", "M001", "M001-VALIDATION.md"); assert.ok(existsSync(filePath), "VALIDATION.md should exist on disk"); + const validationMd = readFileSync(filePath, "utf-8"); + assert.match(validationMd, /## Verification Class Compliance/); + assert.match(validationMd, /- Contract: covered/); + assert.match(validationMd, /## Verdict Rationale/); + }); + + it("omits verification class section when no verification classes are supplied", async () => { + base = makeTmpBase(); + const dbPath = join(base, ".gsd", "gsd.db"); + openDatabase(dbPath); + insertMilestone({ id: "M001" }); + + const result = await handleValidateMilestone( + { ...VALID_PARAMS, verificationClasses: undefined }, + base, + ); + assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`); + + const filePath = join(base, ".gsd", "milestones", "M001", "M001-VALIDATION.md"); + const validationMd = readFileSync(filePath, "utf-8"); + assert.doesNotMatch(validationMd, /## Verification Class Compliance/); }); it("rolls back DB row when disk write fails", async () => { diff --git a/src/resources/extensions/gsd/tools/validate-milestone.ts b/src/resources/extensions/gsd/tools/validate-milestone.ts index d38d8cf16..305b75c06 100644 --- a/src/resources/extensions/gsd/tools/validate-milestone.ts +++ b/src/resources/extensions/gsd/tools/validate-milestone.ts @@ -25,6 +25,7 @@ export interface ValidateMilestoneParams { sliceDeliveryAudit: string; crossSliceIntegration: string; requirementCoverage: string; + verificationClasses?: string; verdictRationale: string; remediationPlan?: string; } @@ -55,6 +56,10 @@ ${params.crossSliceIntegration} ## Requirement Coverage ${params.requirementCoverage} +${params.verificationClasses ? `## Verification Class Compliance +${params.verificationClasses} + +` : ""} ## Verdict Rationale ${params.verdictRationale} `;