Skip to content

Fix delegate_editor: writes-vs-claim verification guard (raffle-app fabricated-completion incident) - #154

Merged
allocsys merged 17 commits into
mainfrom
fix/editor-writes-vs-claim-guard
Sep 7, 2026
Merged

Fix delegate_editor: writes-vs-claim verification guard (raffle-app fabricated-completion incident)#154
allocsys merged 17 commits into
mainfrom
fix/editor-writes-vs-claim-guard

Conversation

@allocsys

@allocsys allocsys commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Incident

A delegate_editor run on allocsys/raffle-app (Telegram Stars payment confirmation) took 9 read_file steps, wrote nothing, then produced a confident, detailed completion report — specific functions and files claimed updated, none of which existed in the diff. Root cause: every step was served by the fallback model gemini-3.5-flash-lite (primary Gemini unavailable), and the loop's completion path trusted the model's own final text with zero cross-check against writtenFiles.

Fix

  • Writes-vs-claim guard: before trusting a draft final answer, checks it against writtenFiles and any tool-result text already gathered this run.
  • Ported from agent_delegate.js: extractMechanicalClaims/findUnverifiedClaims (provider-agnostic, proven on Gemini) and the single-fire pendingVerification pattern (tools re-enabled, not withheld, for the corrective round).
  • Covers the final-step boundary case: a fabricated completion claim landing on the run's actual last step (tools withheld, no loop budget left) — the shape most likely to match the original incident — now gets an inline no-tools corrective call instead of skipping the guard entirely.
  • Never silently trusts a survived fabrication: if a zero-write completion claim still holds after verification (or the model just repeats the same claim on its one retry), the returned answer is prefixed with UNVERIFIED_COMPLETION_CLAIM: rather than passed through clean.
  • fallbackModelUsed surfaced on the final result (and on checkpoint resumes), not just buried in the transcript.

Explicitly out of scope: detectToolCallLeakage, step/char caps, reasoningEffort override — all bai-specific, not applicable to this Gemini failure mode.

Testing

  • Self-correction during verification (model writes for real)
  • Single-fire retry, now flagged rather than silently trusted
  • Fabrication on the actual final step (previously bypassed the guard)
  • Honest zero-write answer not falsely flagged
  • fallbackModelUsed surfaced correctly

CI (verify) is green on this branch.

…or the fabricated-completion-report failure mode)
…inal result, not just buried in the transcript
… final answer, and surface fallbackModelUsed on the persisted/returned result
…s -- a successful draft answer now gets one extra verification round-trip (tools re-enabled) before being trusted, same pattern as agent_delegate.js's own pendingVerification
…es a fabricated completion report (the raffle-app incident's exact failure shape), lets the model self-correct by actually writing during the verification round, stays single-fire when the model insists, and surfaces fallbackModelUsed on the final result
…aim heuristic

The prompt used to unconditionally tell the model "you still have tool
access this turn" even when it might not (the final step / stuck-loop
force withholds tools). If the verification round itself lands on a
tools-withheld step and the model believes the prompt and tries to
write_file anyway, the loop discards the whole run as failed. Now the
prompt's wording matches whether tools are actually available.

Also adds a small heuristic to detect a completion-sounding answer, used
next to gate a no-tools fallback verification path and a final safety
check on the way out.
…o skip entirely

Previously the guard only fired when `!withholdTools && step < cappedSteps`
-- meaning a fabricated completion claim landing on the run's actual last
step (tools withheld, no budget for another loop iteration) bypassed the
guard completely. That's the most likely trigger shape (model burns its
whole step budget reading, then has to answer with no tools left) and
matches the original raffle-app incident if its fabricated summary landed
on the final allowed step.

Now: when the normal tools-enabled corrective round isn't possible, but
writtenFiles is empty and the answer looks like a completion claim, run
one inline no-tools corrective call instead of silently trusting the
draft. And regardless of which path was taken (or none, if pendingVerification
was already true and the model just repeats the same claim), a final
safety check flags -- rather than silently accepts -- a still-completion-
claiming, zero-write answer on the way out.
…w flagged, not silently accepted

Also add coverage for the two gaps found in review: a fabricated
completion claim landing on the run's actual final step (previously
skipped the guard entirely), and an honest "nothing needed" answer with
zero writes correctly NOT being flagged.
@allocsys
allocsys merged commit 36fe8d8 into main Sep 7, 2026
2 checks passed
@allocsys
allocsys deleted the fix/editor-writes-vs-claim-guard branch September 7, 2026 02:26
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