feat: link plans to context workspaces
This commit is contained in:
parent
6a54bccbb0
commit
ecca80f584
|
|
@ -44,6 +44,31 @@ describe("plan receipt", () => {
|
|||
expect(receipt.reasons).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
it("can cite a ready context workspace receipt", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "plan-context-"));
|
||||
const workspace = path.join(dir, "context-workspace.json");
|
||||
fs.writeFileSync(workspace, `${JSON.stringify({ schema: "fable.context_workspace.receipt.v1", decision: "ready" })}
|
||||
`);
|
||||
|
||||
const receipt = createPlanReceipt({ task: "Add feature", contextWorkspaceReceipt: workspace, phases: [phase] });
|
||||
|
||||
expect(receipt.decision).toBe("ready");
|
||||
expect(receipt.contextWorkspaceReceipt).toBe(workspace);
|
||||
});
|
||||
|
||||
it("blocks plans that cite blocked context workspaces", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "plan-context-blocked-"));
|
||||
const workspace = path.join(dir, "context-workspace.json");
|
||||
fs.writeFileSync(workspace, `${JSON.stringify({ schema: "fable.context_workspace.receipt.v1", decision: "blocked" })}
|
||||
`);
|
||||
|
||||
const receipt = createPlanReceipt({ task: "Add feature", contextWorkspaceReceipt: workspace, phases: [phase] });
|
||||
|
||||
expect(receipt.decision).toBe("blocked");
|
||||
expect(receipt.reasons).toContain("context workspace receipt is blocked");
|
||||
});
|
||||
|
||||
it("writes a JSON receipt", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "plan-receipt-"));
|
||||
const file = path.join(dir, "receipt.json");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export interface PlanReceiptOptions {
|
|||
requirements?: string[];
|
||||
questions?: string[];
|
||||
phases: PlanPhase[];
|
||||
contextWorkspaceReceipt?: string;
|
||||
source?: string;
|
||||
now?: Date;
|
||||
}
|
||||
|
|
@ -25,6 +26,7 @@ export interface PlanReceipt {
|
|||
requirements: string[];
|
||||
questions: string[];
|
||||
phases: PlanPhase[];
|
||||
contextWorkspaceReceipt?: string;
|
||||
decision: "ready" | "needs_clarification" | "blocked";
|
||||
reasons: string[];
|
||||
}
|
||||
|
|
@ -37,6 +39,8 @@ export function createPlanReceipt(opts: PlanReceiptOptions): PlanReceipt {
|
|||
if (opts.phases.length === 0) reasons.push("missing phases");
|
||||
if (opts.phases.some((phase) => phase.acceptanceCriteria.length === 0)) reasons.push("each phase needs acceptance criteria");
|
||||
if (opts.phases.some((phase) => phase.verification.length === 0)) reasons.push("each phase needs verification steps");
|
||||
const workspaceReason = opts.contextWorkspaceReceipt ? invalidContextWorkspaceReceiptReason(opts.contextWorkspaceReceipt) : undefined;
|
||||
if (workspaceReason) reasons.push(workspaceReason);
|
||||
|
||||
return {
|
||||
schema: "fable.plan.receipt.v1",
|
||||
|
|
@ -46,6 +50,7 @@ export function createPlanReceipt(opts: PlanReceiptOptions): PlanReceipt {
|
|||
requirements,
|
||||
questions,
|
||||
phases: opts.phases,
|
||||
contextWorkspaceReceipt: opts.contextWorkspaceReceipt,
|
||||
decision: reasons.length ? "blocked" : questions.length ? "needs_clarification" : "ready",
|
||||
reasons,
|
||||
};
|
||||
|
|
@ -56,3 +61,15 @@ export function writePlanReceipt(file: string, receipt: PlanReceipt): string {
|
|||
fs.writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`);
|
||||
return file;
|
||||
}
|
||||
|
||||
function invalidContextWorkspaceReceiptReason(file: string): string | undefined {
|
||||
if (!fs.existsSync(file)) return "context workspace receipt missing";
|
||||
try {
|
||||
const value = JSON.parse(fs.readFileSync(file, "utf-8")) as { schema?: unknown; decision?: unknown };
|
||||
if (value.schema !== "fable.context_workspace.receipt.v1") return "context workspace receipt schema invalid";
|
||||
if (value.decision === "blocked") return "context workspace receipt is blocked";
|
||||
return undefined;
|
||||
} catch {
|
||||
return "context workspace receipt JSON invalid";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue