Skip to content

feat(workflows): prefix-cache prompt layout + warm-first fan-out (V3, #2493) - #2510

Closed
flora131 wants to merge 3 commits into
verifier/criteria-modulefrom
verifier/prefix-cache-prompts
Closed

feat(workflows): prefix-cache prompt layout + warm-first fan-out (V3, #2493)#2510
flora131 wants to merge 3 commits into
verifier/criteria-modulefrom
verifier/prefix-cache-prompts

Conversation

@flora131

@flora131 flora131 commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

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 named MAX_INLINE_CANDIDATE_BYTES (32 KiB, UTF-8); one oversized candidate flips the whole family to reads so 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.
  • Rider (Q4): the generate-and-filter judge prompt adopts build_scoring_prompt.
  • Adversarial-verification wiring is minimal by design — the full rewire is V2 (parallel branch); reconciliation lands at V6 as planned.

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 tsgo erasableSyntaxOnly pass)
  • 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 group
  • Size cap: 288 changed source lines (< 500; cap applies to packages/**; tests uncapped: 519 total insertions)
  • Independent reviewer probe confirmed the module docstring states the ordering invariant and build_scoring_prompt returns a raw string
  • CHANGELOG: packages/workflows/CHANGELOG.md under ## [Unreleased] ### Added

Spec contract: specs/2026-08-17-verification-criteria-module.md §5.3 (Q3: 32 KiB inline bound; Q4: G&F rider included).


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with 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.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for the posted P1 finding and attached artifacts detailing adversarial prompt-boundary reproduction and focused prompt layout results.
  • T-Rex published a second finding-comment-proof validating the same P1 finding.
  • T-Rex performed a general-contract-validation-proof showing how the adversarial injection can distort the judge prompt structure and shortlist instruction ordering, and noted that reproduction sources and test outputs were uploaded.
  • T-Rex organized and linked the related artifacts to the findings so reviewers can inspect the adversarial prompt-boundary evidence.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 Candidate bodies can close the judge prompt's candidate section and inject ranking instructions

    • Bug
      • At 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.
    • Cause
      • candidate.body is concatenated directly into an XML-like prompt format without escaping, encoding, or an injection-resistant representation.
    • Fix
      • Encode candidate bodies before interpolation (for example, a robust serialized/quoted representation with explicit instruction that it is untrusted data), or use an unambiguous delimiter/structured transport that cannot be terminated by candidate-controlled text; add an adversarial regression test with closing tags and injected criterion/output instructions.

    T-Rex Ran code and verified through T-Rex

Prompt To Fix All With AI
### Issue 1
packages/workflows/builtin/verification-prompts.ts:80
**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.

Reviews (1): Last reviewed commit: "fix(workflows): honor V3 fan-out and rea..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

return head.candidates
.map((candidate, index) => {
if (readPaths === undefined) {
return `<candidate index="${index + 1}">\n${candidate.body}\n</candidate>`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security 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.

View artifacts

T-Rex 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant