Merge pull request #3539 from Tibsfox/fix/inject-slice-context-into-prompts

fix(gsd): inject S##-CONTEXT.md from slice discussion into all prompt builders
This commit is contained in:
Jeremy McSpadden 2026-04-05 08:07:19 -05:00 committed by GitHub
commit 5c7e5efcf4
2 changed files with 71 additions and 0 deletions

View file

@ -994,10 +994,15 @@ export async function buildResearchSlicePrompt(
const milestoneResearchPath = resolveMilestoneFile(base, mid, "RESEARCH");
const milestoneResearchRel = relMilestoneFile(base, mid, "RESEARCH");
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
const inlined: string[] = [];
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
if (contextInline) inlined.push(contextInline);
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
if (sliceCtxInline) inlined.push(sliceCtxInline);
const researchInline = await inlineFileOptional(milestoneResearchPath, milestoneResearchRel, "Milestone Research");
if (researchInline) inlined.push(researchInline);
const decisionsInline = await inlineDecisionsFromDb(base, mid);
@ -1045,6 +1050,8 @@ export async function buildPlanSlicePrompt(
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
const researchRel = relSliceFile(base, mid, sid, "RESEARCH");
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
const inlined: string[] = [];
@ -1053,6 +1060,8 @@ export async function buildPlanSlicePrompt(
if (researchSliceAnchor) inlined.push(formatAnchorForPrompt(researchSliceAnchor));
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
if (sliceCtxInline) inlined.push(sliceCtxInline);
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
if (researchInline) inlined.push(researchInline);
if (inlineLevel !== "minimal") {
@ -1253,9 +1262,13 @@ export async function buildCompleteSlicePrompt(
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
const inlined: string[] = [];
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
if (sliceCtxInline) inlined.push(sliceCtxInline);
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
if (inlineLevel !== "minimal") {
const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
@ -1510,9 +1523,13 @@ export async function buildReplanSlicePrompt(
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
const inlined: string[] = [];
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
if (sliceCtxInline) inlined.push(sliceCtxInline);
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Current Slice Plan"));
// Find the blocker task summary — the completed task with blocker_discovered: true
@ -1627,9 +1644,13 @@ export async function buildReassessRoadmapPrompt(
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
const summaryRel = relSliceFile(base, mid, completedSliceId, "SUMMARY");
const sliceContextPath = resolveSliceFile(base, mid, completedSliceId, "CONTEXT");
const sliceContextRel = relSliceFile(base, mid, completedSliceId, "CONTEXT");
const inlined: string[] = [];
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
if (sliceCtxInline) inlined.push(sliceCtxInline);
inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
if (inlineLevel !== "minimal") {
const projectInline = await inlineProjectFromDb(base);

View file

@ -0,0 +1,50 @@
/**
* Regression test: S##-CONTEXT.md from slice discussion must be
* injected into all 5 downstream prompt builders (#3452).
*
* Scans auto-prompts.ts for the 5 builder functions and verifies
* each one resolves and inlines the slice-level CONTEXT file.
*/
import { describe, 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 autoPromptsPath = join(__dirname, "..", "auto-prompts.ts");
const source = readFileSync(autoPromptsPath, "utf-8");
const BUILDERS = [
"buildResearchSlicePrompt",
"buildPlanSlicePrompt",
"buildCompleteSlicePrompt",
"buildReplanSlicePrompt",
"buildReassessRoadmapPrompt",
];
describe("slice CONTEXT.md injection into prompt builders (#3452)", () => {
for (const builder of BUILDERS) {
test(`${builder} resolves slice CONTEXT file`, () => {
// Find the function body
const fnStart = source.indexOf(`export async function ${builder}`);
assert.ok(fnStart !== -1, `${builder} should exist in auto-prompts.ts`);
// Get a reasonable chunk after the function start (enough to cover the inlining section)
const chunk = source.slice(fnStart, fnStart + 3000);
// Must resolve the slice CONTEXT path
assert.ok(
chunk.includes('resolveSliceFile(base, mid,') && chunk.includes('"CONTEXT"'),
`${builder} should call resolveSliceFile with "CONTEXT"`,
);
// Must inline it with inlineFileOptional
assert.ok(
chunk.includes('Slice Context'),
`${builder} should inline slice CONTEXT with a "Slice Context" label`,
);
});
}
});