feat(workflows): prefix-cache prompt layout + warm-first fan-out (V3, #2493) - #2510
feat(workflows): prefix-cache prompt layout + warm-first fan-out (V3, #2493)#2510flora131 wants to merge 3 commits into
Conversation
| return head.candidates | ||
| .map((candidate, index) => { | ||
| if (readPaths === undefined) { | ||
| return `<candidate index="${index + 1}">\n${candidate.body}\n</candidate>`; |
There was a problem hiding this comment.
Candidate content can override judge instructions
candidate.body is concatenated directly into the XML-like scoring prompt. A model-generated body containing </candidate> and </candidates> can close the enclosing data section and place a controlled <criterion> or <output_format> before the legitimate judge instructions. Encode candidate text or use an injection-resistant representation before interpolation, and add a regression case with closing tags plus injected ranking directives.
Artifacts
Adversarial prompt-boundary reproduction source
- This source imports the prompt builder and asserts that a candidate-controlled closing tag and ranking instruction escape the candidate section, proving the boundary condition under test.
Parent commit adversarial prompt-boundary result
- This executed capture runs the reproduction against the parent implementation and shows the candidate section escapes and injected criterion precedes the legitimate tail, confirming the pre-existing behavior.
Current PR adversarial prompt-boundary result
- This executed capture runs the reproduction against the current PR implementation and shows the same boundary escape and instruction ordering, confirming the claimed bug remains.
Focused verification prompt layout test results
- This captured Bun test run reports all seven existing verification prompt layout tests passing, showing normal layout coverage does not reject the adversarial boundary escape.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/workflows/builtin/verification-prompts.ts
Line: 80
Comment:
**Candidate content can override judge instructions**
`candidate.body` is concatenated directly into the XML-like scoring prompt. A model-generated body containing `</candidate>` and `</candidates>` can close the enclosing data section and place a controlled `<criterion>` or `<output_format>` before the legitimate judge instructions. Encode candidate text or use an injection-resistant representation before interpolation, and add a regression case with closing tags plus injected ranking directives.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary
Slice V3 of the LLM-as-a-Verifier adoption program (
specs/2026-08-17-verification-criteria-module.md§5.3): the prefix-cache prompt layout and warm-first fan-out scheduling layer.packages/workflows/builtin/verification-prompts.ts(new):build_scoring_prompt(head, criterion)enforcing the SHARED HEAD ‖ VARYING TAIL invariant — task, ground-truth note, candidate bodies, and scale anchors in a byte-identical head; only the criterion + output instruction in the tail. Candidates inline under the namedMAX_INLINE_CANDIDATE_BYTES(32 KiB, UTF-8); one oversized candidate flips the whole family toreadsso sibling heads stay byte-identical.warm_first_fan_out(ctx, steps, prefixKeyOf, options): one step per distinct prefix key completes first (populating the provider cache), then the rest flood at full concurrency; a warm-phase failure still releases its group.build_scoring_prompt.Base:
verifier/criteria-module(V1, #2504). Stack: V1 → V3 → V4 → V5 → V6.Evidence
Produced by an implement→review→repair goal run (2 turns, approved by completion/evidence/risk reviewers on the final checkout):
npm run check— green (includes the coding-agent tsgoerasableSyntaxOnlypass)npx vitest --run --project unit -t "prompt-layout"— green: byte-identical sibling heads (order indices verified), exact 32,768-byte boundary kept inline, one oversized UTF-8 body flips the family to reads with ordered duplicate reads preserved, deterministic warm/rest partition, warm-failure releases its grouppackages/**; tests uncapped: 519 total insertions)build_scoring_promptreturns a raw stringpackages/workflows/CHANGELOG.mdunder## [Unreleased]### AddedSpec contract:
specs/2026-08-17-verification-criteria-module.md§5.3 (Q3: 32 KiB inline bound; Q4: G&F rider included).Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Greptile Summary
This change adds prefix-cache-aware scoring prompt layouts, warm-first verifier scheduling, and adopts the scoring layout for generate-and-filter judging. Execution confirmed that model-generated response text can break out of the intended prompt data section and place replacement ranking instructions ahead of the legitimate judge criteria, allowing shortlist selection to be influenced by untrusted content.
Confidence Score: 3/5
Do not merge until model-generated response text is represented as data that cannot terminate or extend the judge prompt's instruction structure.
An executable reproduction exercised the affected prompt builder with adversarial closing tags and ranking directives, directly observing those directives before the legitimate judge prompt tail. The focused normal-layout tests passed but do not cover hostile response content.
Files Needing Attention: packages/workflows/builtin/verification-prompts.ts needs an injection-resistant encoding or transport for inline response bodies, along with adversarial prompt-boundary coverage.
Security Review
The scoring prompt builder has a prompt-injection integrity issue. Untrusted model-generated response text can close XML-like prompt sections and add ranking or output directives before the intended judge instructions.
What T-Rex did
Comments Outside Diff (1)
General comment
packages/workflows/builtin/verification-prompts.ts:80, an inline model-generated candidate body is inserted verbatim between<candidate>tags. A body containing</candidate>,</candidates>, and a new<criterion>/<output_format>closes the enclosing candidate region and places injected judge instructions before the legitimate criterion tail.candidate.bodyis concatenated directly into an XML-like prompt format without escaping, encoding, or an injection-resistant representation.Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(workflows): honor V3 fan-out and rea..." | Re-trigger Greptile