Skip to content

Commit d4536ef

Browse files
author
Foliant-Mobi SRL
committed
perf(prompt): stabilize review cache prefix
1 parent deffd4b commit d4536ef

3 files changed

Lines changed: 53 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 0.7.2 - Unreleased
44

5+
- Reordered review prompts so shared instructions and the JSON contract precede feature-specific context, improving provider prompt-cache reuse across feature reviews.
6+
57
## 0.7.1 - 2026-07-20
68

79
### Highlights

src/prompt.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,41 @@ import { fixtureRoot, writeFixture } from "./test-helpers.js";
1010
import type { FeatureRecord, FindingRecord, ProjectRecord } from "./types.js";
1111

1212
describe("review prompt provenance", () => {
13+
it("keeps shared review instructions ahead of feature-specific context", async () => {
14+
const root = await fixtureRoot("clawpatch-prompt-cache-prefix-");
15+
await writeFixture(root, "src/index.ts", "export const value = 1;\n");
16+
await writeFixture(root, "src/second.ts", "export const second = 2;\n");
17+
const firstFeature = feature();
18+
const secondFeature: FeatureRecord = {
19+
...feature(),
20+
featureId: "feat_second",
21+
title: "Second feature",
22+
summary: "A different semantic review unit",
23+
ownedFiles: [{ path: "src/second.ts", reason: "primary" }],
24+
contextFiles: [],
25+
tests: [],
26+
};
27+
28+
const [first, second] = await Promise.all([
29+
buildReviewPromptBundle(root, project(root), firstFeature, defaultConfig()),
30+
buildReviewPromptBundle(root, project(root), secondFeature, defaultConfig()),
31+
]);
32+
const firstFeatureIndex = first.prompt.indexOf("Feature:\n");
33+
const secondFeatureIndex = second.prompt.indexOf("Feature:\n");
34+
const firstPrefix = first.prompt.slice(0, firstFeatureIndex);
35+
const secondPrefix = second.prompt.slice(0, secondFeatureIndex);
36+
37+
expect(firstFeatureIndex).toBeGreaterThan(0);
38+
expect(secondFeatureIndex).toBeGreaterThan(0);
39+
expect(firstPrefix).toBe(secondPrefix);
40+
expect(firstPrefix).toContain("Review categories:");
41+
expect(firstPrefix).toContain("JSON shape:");
42+
expect(first.prompt.indexOf("JSON shape:")).toBeLessThan(firstFeatureIndex);
43+
expect(first.prompt.indexOf("Valid evidence paths are exactly:")).toBeGreaterThan(
44+
firstFeatureIndex,
45+
);
46+
});
47+
1348
it("records included, omitted, and truncated review prompt context", async () => {
1449
const root = await fixtureRoot("clawpatch-prompt-provenance-");
1550
await writeFixture(root, "src/index.ts", "export const value = 1;\n");

src/prompt.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,7 @@ ${customPrompt.trim()}
198198
199199
Return strict JSON only. No markdown fences.
200200
201-
Project:
202-
${JSON.stringify({ name: project.name, detected: project.detected }, null, 2)}
203-
204-
Feature:
205-
${JSON.stringify(reviewFeatureView(feature), null, 2)}
206-
207-
${customBlock}Review categories:
201+
Review categories:
208202
- correctness bugs
209203
- security issues
210204
- race/concurrency bugs
@@ -223,7 +217,7 @@ Shell and workflow review:
223217
- Focus this rule on captured or parsed values, not human-facing logging fallbacks like some_command || echo "failed".
224218
- Recommend separating the primary command capture from fallback assignment, for example if ! status="$(cmd)"; then status="fallback"; fi.
225219
226-
${reviewModeInstructions(mode)}${cudaBlock}
220+
${reviewModeInstructions(mode)}
227221
228222
${languageGuidance}
229223
@@ -235,18 +229,13 @@ issues: when the same bug pattern appears in multiple owned files, emit one find
235229
with multiple evidence refs instead of separate one-off findings.
236230
237231
Avoid speculative low-evidence findings. Evidence must point at included files.
238-
Valid evidence paths are exactly:
239-
${validEvidencePaths.map((path) => `- ${path}`).join("\n")}
240-
Feature metadata paths are not valid evidence unless listed above.
232+
Feature metadata paths are not valid evidence unless listed in the review input below.
241233
When providing evidence line ranges, use the line-number gutter in the Files section.
242234
Do not inspect files beyond the shown excerpts for evidence. If an excerpt is truncated,
243235
only cite lines that appear in the Files section.
244236
Set evidence.quote to null; line ranges are enough for validation.
245237
246-
Prompt context:
247-
${JSON.stringify(promptContext, null, 2)}
248-
249-
JSON shape:
238+
${customBlock}JSON shape:
250239
{
251240
"findings": [
252241
{
@@ -266,6 +255,18 @@ JSON shape:
266255
"inspected": {"files":["string"],"symbols":["string"],"notes":["string"]}
267256
}
268257
258+
Project:
259+
${JSON.stringify({ name: project.name, detected: project.detected }, null, 2)}
260+
261+
Feature:
262+
${JSON.stringify(reviewFeatureView(feature), null, 2)}
263+
${cudaBlock}
264+
Valid evidence paths are exactly:
265+
${validEvidencePaths.map((path) => `- ${path}`).join("\n")}
266+
267+
Prompt context:
268+
${JSON.stringify(promptContext, null, 2)}
269+
269270
Files:
270271
${fileBlocks.join("\n\n")}`;
271272
const promptBytes = Buffer.byteLength(prompt, "utf8");

0 commit comments

Comments
 (0)