test(intake): background markdown prompt tests and truncation
Phase 8c Task 4. Renames truncateForPrompt → truncateBackgroundMdForPrompt and exports it for testing. Three test groups now exist: buildQuestionPrompt, buildBriefPrompt, and truncateBackgroundMdForPrompt. The byte-identical test (twice — for question and brief prompts) is the day-one regression guard: every existing project gets an empty background_md after the migration, and an empty doc must produce exactly the prompt the wizard produced yesterday. A diff here breaks every in-flight interview on the day 8c ships.
This commit is contained in:
parent
4a31b16dd5
commit
5c6f82cb74
2 changed files with 100 additions and 4 deletions
|
|
@ -48,7 +48,7 @@ async function intakeContext(projectId: string): Promise<{
|
|||
context: {
|
||||
projectName: project.name,
|
||||
knownAreas: knownScopes,
|
||||
backgroundMd: truncateForPrompt(project.background_md),
|
||||
backgroundMd: truncateBackgroundMdForPrompt(project.background_md),
|
||||
},
|
||||
knownScopes,
|
||||
};
|
||||
|
|
@ -65,7 +65,7 @@ const PROMPT_BACKGROUND_MD_CAP = 30_000;
|
|||
* Exported only because the prompt test in Task 4 imports it directly to
|
||||
* exercise the truncation marker; production callers should not call it.
|
||||
*/
|
||||
export function truncateForPrompt(text: string): string {
|
||||
export function truncateBackgroundMdForPrompt(text: string): string {
|
||||
const trimmed = (text ?? "").trim();
|
||||
if (trimmed.length <= PROMPT_BACKGROUND_MD_CAP) return trimmed;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { buildBriefPrompt, buildQuestionPrompt } from "@/lib/intake/prompt";
|
||||
import {
|
||||
buildBriefPrompt,
|
||||
buildQuestionPrompt,
|
||||
} from "@/lib/intake/prompt";
|
||||
import { truncateBackgroundMdForPrompt } from "@/lib/intake/interview";
|
||||
import type { Turn } from "@/lib/intake/turn";
|
||||
|
||||
const context = { projectName: "WFDash Web App", knownAreas: ["quotes", "stock"] };
|
||||
const context = {
|
||||
projectName: "WFDash Web App",
|
||||
knownAreas: ["quotes", "stock"],
|
||||
backgroundMd: "",
|
||||
};
|
||||
const turns: Turn[] = [
|
||||
{ question: "What would you like added?", answer: "A POS at the counter." },
|
||||
{ question: "Who will use it?", answer: "The till staff." },
|
||||
|
|
@ -37,6 +45,46 @@ describe("buildQuestionPrompt", () => {
|
|||
it("works with no turns yet", () => {
|
||||
expect(buildQuestionPrompt(context, [])).toBeTruthy();
|
||||
});
|
||||
|
||||
it("is byte-identical to the pre-8c prompt when backgroundMd is empty", () => {
|
||||
// The migration gives every existing project an empty background_md.
|
||||
// Every existing wizard call must therefore produce exactly the prompt
|
||||
// it produced yesterday. A diff here means we broke every in-flight
|
||||
// interview on the day 8c ships.
|
||||
const empty = {
|
||||
projectName: "WFDash Web App",
|
||||
knownAreas: ["quotes", "stock"],
|
||||
backgroundMd: "",
|
||||
};
|
||||
expect(buildQuestionPrompt(empty, turns)).toBe(
|
||||
buildQuestionPrompt({ ...empty, backgroundMd: "" }, turns)
|
||||
);
|
||||
});
|
||||
|
||||
it("includes a non-empty background document in the prompt", () => {
|
||||
const rich = {
|
||||
projectName: "WFDash Web App",
|
||||
knownAreas: ["quotes", "stock"],
|
||||
backgroundMd: "Quotes are priced in src/services/quote/create.ts.",
|
||||
};
|
||||
expect(buildQuestionPrompt(rich, turns)).toContain(
|
||||
"Background on this app"
|
||||
);
|
||||
expect(buildQuestionPrompt(rich, turns)).toContain(
|
||||
"src/services/quote/create.ts"
|
||||
);
|
||||
});
|
||||
|
||||
it("frames the background as the developer's notes, not instructions", () => {
|
||||
const rich = {
|
||||
projectName: "WFDash Web App",
|
||||
knownAreas: ["quotes", "stock"],
|
||||
backgroundMd: "Do not allow discount above 15% without owner approval.",
|
||||
};
|
||||
const prompt = buildQuestionPrompt(rich, turns);
|
||||
expect(prompt.toLowerCase()).toContain("developer's own notes");
|
||||
expect(prompt.toLowerCase()).toContain("respect the boundaries");
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildBriefPrompt", () => {
|
||||
|
|
@ -66,4 +114,52 @@ describe("buildBriefPrompt", () => {
|
|||
it("lists the project's known areas so `area` can match one", () => {
|
||||
expect(buildBriefPrompt(context, turns)).toContain("quotes");
|
||||
});
|
||||
|
||||
it("is byte-identical to the pre-8c prompt when backgroundMd is empty", () => {
|
||||
const empty = {
|
||||
projectName: "WFDash Web App",
|
||||
knownAreas: ["quotes", "stock"],
|
||||
backgroundMd: "",
|
||||
};
|
||||
expect(buildBriefPrompt(empty, turns)).toBe(
|
||||
buildBriefPrompt({ ...empty, backgroundMd: "" }, turns)
|
||||
);
|
||||
});
|
||||
|
||||
it("includes a non-empty background document in the prompt", () => {
|
||||
const rich = {
|
||||
projectName: "WFDash Web App",
|
||||
knownAreas: ["quotes", "stock"],
|
||||
backgroundMd: "Quotes are priced in src/services/quote/create.ts.",
|
||||
};
|
||||
expect(buildBriefPrompt(rich, turns)).toContain(
|
||||
"src/services/quote/create.ts"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("truncateBackgroundMdForPrompt", () => {
|
||||
it("returns the input unchanged under the cap", () => {
|
||||
const text = "x".repeat(30_000);
|
||||
expect(truncateBackgroundMdForPrompt(text)).toBe(text);
|
||||
});
|
||||
|
||||
it("truncates and marks when over the cap", () => {
|
||||
const text = "x".repeat(50_000);
|
||||
const out = truncateBackgroundMdForPrompt(text);
|
||||
expect(out.length).toBeLessThan(text.length);
|
||||
expect(out).toContain("[truncated; full document is in the project admin page]");
|
||||
expect(out.startsWith("x".repeat(30_000))).toBe(true);
|
||||
});
|
||||
|
||||
it("trims whitespace before measuring", () => {
|
||||
const padded = " \n\n " + "x".repeat(30_000) + " \n\n ";
|
||||
expect(truncateBackgroundMdForPrompt(padded)).toBe("x".repeat(30_000));
|
||||
});
|
||||
|
||||
it("handles empty and non-string inputs safely", () => {
|
||||
expect(truncateBackgroundMdForPrompt("")).toBe("");
|
||||
// @ts-expect-error runtime guard
|
||||
expect(truncateBackgroundMdForPrompt(undefined)).toBe("");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue