Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions src/forker/context-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,24 @@ export class ContextBuilder {
}

/**
* Return a summary of all prior decisions for context injection.
* Returns empty string if there are no prior decisions.
* Return a compact summary of all prior decisions for context injection.
* Format: "Prior decisions: Q1=A1; Q2=A2; Q3=A3"
* Empty string if there are no prior decisions.
*
* Compact form saves 12-21% tokens vs the previous numbered-list prose
* while keeping the "Prior decisions:" anchor that helps the LLM
* recognise this as resolved state, not as constraints to negotiate.
*/
static buildDecisionContext(decisionPath: DecisionStep[]): string {
if (decisionPath.length === 0) {
return "";
}

const lines = decisionPath.map(
(step, i) => `${i + 1}. ${step.question}: ${step.answer}`,
const pairs = decisionPath.map(
(step) => `${step.question}=${step.answer}`,
);

return `Previously decided:\n${lines.join("\n")}`;
return `Prior decisions: ${pairs.join("; ")}.`;
}

/**
Expand All @@ -78,6 +83,6 @@ export class ContextBuilder {
return task;
}

return `${context}\n\nTask: ${task}`;
return `${context} Task: ${task}`;
}
}
36 changes: 26 additions & 10 deletions test/unit/forker/context-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ describe("ContextBuilder", () => {

const context = ContextBuilder.buildDecisionContext(path);

expect(context).toContain("Previously decided:");
expect(context).toContain("1. Auth method: JWT");
expect(context).toContain("Prior decisions:");
expect(context).toContain("Auth method=JWT");
});

it("with multiple decisions formats correctly", () => {
Expand All @@ -91,10 +91,26 @@ describe("ContextBuilder", () => {

const context = ContextBuilder.buildDecisionContext(path);

expect(context).toContain("Previously decided:");
expect(context).toContain("1. Auth method: JWT");
expect(context).toContain("2. Signing algorithm: RS256");
expect(context).toContain("3. Token expiry: 15 minutes");
expect(context).toContain("Prior decisions:");
expect(context).toContain("Auth method=JWT");
expect(context).toContain("Signing algorithm=RS256");
expect(context).toContain("Token expiry=15 minutes");
expect(context.split(";").length).toBe(3);
});

it("is shorter than the original prose format", () => {
const path: DecisionStep[] = [
{ question: "Auth method", answer: "JWT" },
{ question: "Signing algorithm", answer: "RS256" },
{ question: "Token expiry", answer: "15 minutes" },
];

const compact = ContextBuilder.buildDecisionContext(path);
const prose = `Previously decided:\n${path
.map((s, i) => `${i + 1}. ${s.question}: ${s.answer}`)
.join("\n")}`;

expect(compact.length).toBeLessThan(prose.length);
});
});

Expand All @@ -115,9 +131,9 @@ describe("ContextBuilder", () => {
decisions,
);

expect(result).toContain("Previously decided:");
expect(result).toContain("1. Framework: Express");
expect(result).toContain("2. Database: PostgreSQL");
expect(result).toContain("Prior decisions:");
expect(result).toContain("Framework=Express");
expect(result).toContain("Database=PostgreSQL");
expect(result).toContain("Task: Build a REST API");
});

Expand All @@ -128,7 +144,7 @@ describe("ContextBuilder", () => {

const result = ContextBuilder.buildFullPrompt("Do the thing", decisions);

const contextIndex = result.indexOf("Previously decided:");
const contextIndex = result.indexOf("Prior decisions:");
const taskIndex = result.indexOf("Task:");
expect(contextIndex).toBeLessThan(taskIndex);
});
Expand Down
Loading