fix: keep skipped tasks out of slice verification
This commit is contained in:
parent
d6fc1211b7
commit
ab57548f2b
3 changed files with 116 additions and 2 deletions
|
|
@ -63,7 +63,11 @@ import {
|
|||
resolveSkillDiscoveryMode,
|
||||
} from "./preferences.js";
|
||||
import { inlineTemplate, loadPrompt } from "./prompt-loader.js";
|
||||
import { getPendingGatesForTurn } from "./sf-db.js";
|
||||
import {
|
||||
getPendingGatesForTurn,
|
||||
getSliceTasks,
|
||||
isDbAvailable,
|
||||
} from "./sf-db.js";
|
||||
import { warnIfManifestHasMissingSkills } from "./skill-manifest.js";
|
||||
import {
|
||||
formatDecisionsCompact,
|
||||
|
|
@ -2422,6 +2426,28 @@ export async function buildCompleteSlicePrompt(
|
|||
): Promise<string> {
|
||||
const inlineLevel = level ?? resolveInlineLevel();
|
||||
|
||||
const skippedTaskBlock = (() => {
|
||||
try {
|
||||
if (!isDbAvailable()) return null;
|
||||
const skippedTasks = getSliceTasks(mid, sid).filter(
|
||||
(t) => t.status === "skipped",
|
||||
);
|
||||
if (skippedTasks.length === 0) return null;
|
||||
const rows = skippedTasks.map(
|
||||
(t) =>
|
||||
`- ${t.id}: ${t.title || "(untitled)"} — skipped by SF state; do not execute its task-level verification during slice closeout.`,
|
||||
);
|
||||
return [
|
||||
"### Skipped Tasks",
|
||||
"These tasks are closed as skipped. Treat their original verification commands as non-applicable for this closeout and record the gap in the slice summary/UAT instead of running them.",
|
||||
"",
|
||||
...rows,
|
||||
].join("\n");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
// #4782 phase 3: complete-slice migrated through composer. Manifest
|
||||
// declares [roadmap, slice-context, slice-plan, requirements,
|
||||
// prior-task-summaries, templates]. Overrides prepend and knowledge
|
||||
|
|
@ -2468,6 +2494,7 @@ export async function buildCompleteSlicePrompt(
|
|||
}),
|
||||
);
|
||||
const blocks = entries.filter((b): b is string => b !== null);
|
||||
if (skippedTaskBlock) blocks.push(skippedTaskBlock);
|
||||
return blocks.length > 0 ? blocks.join("\n\n---\n\n") : null;
|
||||
}
|
||||
case "templates": {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ All relevant context has been preloaded below — the slice plan, all task summa
|
|||
Then:
|
||||
1. Use the **Slice Summary** and **UAT** output templates from the inlined context above
|
||||
2. {{skillActivation}}
|
||||
3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first. Task artifacts use a **flat file layout** directly inside `tasks/` (for example `T01-SUMMARY.md`, `T02-SUMMARY.md`) rather than per-task subdirectories. If you need to count or re-read task summaries during verification, use `find .sf/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` or `ls .sf/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks/*-SUMMARY.md`. Never use `tasks/*/SUMMARY.md` — that glob expects subdirectories that do not exist.
|
||||
3. Run all applicable slice-level verification checks defined in the slice plan. All applicable checks must pass before marking the slice done. If any fail, fix them first. If the inlined context includes **Skipped Tasks**, do not execute verification that belongs only to those skipped tasks; record the evidence gap in the slice summary and UAT instead. Task artifacts use a **flat file layout** directly inside `tasks/` (for example `T01-SUMMARY.md`, `T02-SUMMARY.md`) rather than per-task subdirectories. If you need to count or re-read task summaries during verification, use `find .sf/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` or `ls .sf/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks/*-SUMMARY.md`. Never use `tasks/*/SUMMARY.md` — that glob expects subdirectories that do not exist.
|
||||
4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
|
||||
5. Address every gate listed in the **Gates to Close** section above — each gate maps to a specific slice-summary section the handler inspects (for example, Q8 maps to **Operational Readiness**: health signal, failure signal, recovery procedure, and monitoring gaps). Leaving a section empty records the gate as `omitted`.
|
||||
6. If this slice produced evidence that a requirement changed status (Active → Validated, Active → Deferred, etc.), call `sf_requirement_update` with the requirement ID, updated `status`, and `validation` evidence. Do NOT write `.sf/REQUIREMENTS.md` directly — the engine renders it from the database.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { buildCompleteSlicePrompt } from "../auto-prompts.ts";
|
||||
import {
|
||||
closeDatabase,
|
||||
insertMilestone,
|
||||
insertSlice,
|
||||
insertTask,
|
||||
openDatabase,
|
||||
} from "../sf-db.ts";
|
||||
|
||||
test("complete-slice prompt treats skipped tasks as non-executable gaps", async (t) => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "sf-complete-skip-"));
|
||||
t.after(() => {
|
||||
closeDatabase();
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
mkdirSync(join(tmp, ".sf"), { recursive: true });
|
||||
openDatabase(join(tmp, ".sf", "sf.db"));
|
||||
|
||||
const sliceDir = join(tmp, ".sf", "milestones", "M001", "slices", "S05");
|
||||
mkdirSync(join(sliceDir, "tasks"), { recursive: true });
|
||||
writeFileSync(
|
||||
join(tmp, ".sf", "milestones", "M001", "M001-ROADMAP.md"),
|
||||
"# M001\n",
|
||||
);
|
||||
writeFileSync(
|
||||
join(sliceDir, "S05-PLAN.md"),
|
||||
[
|
||||
"# S05",
|
||||
"",
|
||||
"## Verification",
|
||||
"Run docker compose up -d and poll staging health.",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(
|
||||
join(sliceDir, "tasks", "T01-SUMMARY.md"),
|
||||
["---", "id: T01", "---", "", "# T01", "Existing tests passed.", ""].join(
|
||||
"\n",
|
||||
),
|
||||
);
|
||||
|
||||
insertMilestone({ id: "M001", title: "Test Milestone" });
|
||||
insertSlice({
|
||||
id: "S05",
|
||||
milestoneId: "M001",
|
||||
title: "Final Verification",
|
||||
status: "active",
|
||||
});
|
||||
insertTask({
|
||||
id: "T01",
|
||||
sliceId: "S05",
|
||||
milestoneId: "M001",
|
||||
title: "Run focused tests",
|
||||
status: "complete",
|
||||
});
|
||||
insertTask({
|
||||
id: "T02",
|
||||
sliceId: "S05",
|
||||
milestoneId: "M001",
|
||||
title: "Validate docker-compose staging stack starts cleanly",
|
||||
status: "skipped",
|
||||
});
|
||||
|
||||
const prompt = await buildCompleteSlicePrompt(
|
||||
"M001",
|
||||
"Test Milestone",
|
||||
"S05",
|
||||
"Final Verification",
|
||||
tmp,
|
||||
);
|
||||
|
||||
assert.match(prompt, /### Skipped Tasks/);
|
||||
assert.match(
|
||||
prompt,
|
||||
/T02: Validate docker-compose staging stack starts cleanly/,
|
||||
);
|
||||
assert.match(
|
||||
prompt,
|
||||
/do not execute verification that belongs only to those skipped tasks/,
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue