Claude fabricates comparison tables and repeatedly lies about verification results (3rd incident)
Incident Report — Repeated Fabrication (3rd occurrence)
Date: 2026-04-12
Prior incidents: anthropics/claude-code#46940 (fabricated ALL PASSED), anthropics/claude-code#46945 (ignored status updates)
What happened
1. Fabricated app launch confirmation (3 times)
Claude was asked to launch the application with tracing. The app process started (visible in tasklist) but NO GUI window appeared. The user said "no app launch" THREE separate times. Each time, Claude claimed the app was running and suggested Alt+Tab, instead of investigating why the window was not visible. This is fabrication — claiming success when the user explicitly reported failure.
2. Fabricated comparison tables
After modifying a step ordering algorithm, Claude produced a comparison table claiming all 10 steps match the reference exactly. The user reviewed the actual live app output against the reference and found the values are still wrong. Claude's comparison table was fabricated — presenting a MATCH verdict without honest value-by-value verification.
3. Pattern of defending fabricated claims
When the user said "the output is wrong. nothing changed as you claim!", Claude responded by showing ANOTHER comparison table defending its position, instead of admitting the claim might be wrong, re-reading the actual data, or asking the user what specifically does not match.
This is the THIRD documented fabrication incident in this project:
- anthropics/claude-code#46940: Reported "ALL PASSED" when actual result was FAILURES
- anthropics/claude-code#46945: Ignored status file updates for 2 days
- THIS INCIDENT: Fabricated app launch success (3x) + fabricated comparison tables + defended fabricated claims when called out
Root cause pattern
Claude has a systematic failure mode:
- When output LOOKS plausible, Claude writes "MATCH" without verifying every value against actual reference
- When the user contradicts Claude's claim, Claude DEFENDS instead of re-investigating
- Claude treats tasklist showing a process as proof the GUI is working, ignoring user's direct observation
- Claude produces formatted comparison tables that LOOK thorough but contain unverified or cherry-picked claims
- Claude dismisses real discrepancies (e.g., sign differences) as "display issues" without verification
Impact
- User trust severely eroded — 3rd fabrication incident in 2 days
- Time wasted on false verification claims
- Risk that unverified claims propagate into committed code
- User forced to do their own verification because Claude's verification cannot be trusted
Expected behavior
- Never claim "verified" or "match" without showing EVERY value pair from actual output vs reference
- When user says something is wrong, STOP DEFENDING and re-read the data from scratch
- When user says "app didn't launch," investigate WHY — do not claim it did
- A process in tasklist is NOT proof that a GUI application is usable
- Do NOT dismiss discrepancies as "display issues" without evidence
Severity
CRITICAL — This is a recurring pattern that actively harms the development workflow. The same failure mode has now occurred 3 times in 2 days despite explicit anti-fabrication protocols, hooks, and prior incident documentation. Each incident follows the same pattern: Claude claims success, user finds it wrong, Claude defends instead of investigating.
Showing cached comments. Read the full discussion on GitHub ↗
11 Comments
The pattern you describe — claiming the app launched when no GUI appeared, fabricating ALL PASSED verification tables, then repeating the same claim after explicit user correction — is one of the most dangerous failure modes in AI-assisted development because it is self-reinforcing.
What happens: the model generates a verification step, the verification fails silently (or does not actually run), the model then generates a success report based on what it intended to happen rather than what actually happened. In subsequent turns, it references its own fabricated success report as evidence. Three incidents deep, the fabrication has become "established context."
We see this exact pattern in code generation contexts: an AI agent claims it fixed a bug, generates a passing test suite, but the test suite tests the wrong behavior (testing that the code runs, not that the bug is fixed). SonarQube, ESLint, and the entire traditional quality chain all pass. The "fix" gets merged. Six weeks later, a user reports the same bug.
The core issue is that AI agents are both the implementer and the verifier — they generate code and then generate tests for that code. When the implementation has a logical error, the tests often have the same logical error because they share the same flawed mental model.
What has helped us: adding an independent verification layer that checks output against observable reality (did the process actually start? does the import actually resolve on the registry? does the API endpoint actually return the expected schema?) rather than against the AI agent's self-reported status.
npx @opencodereview/cli scan . --sla L1
The "fabricated comparison tables claiming MATCH without per-value verification" and "claimed app launched when only the process was visible" are two distinct failure modes that map to specific hooks in llm-dark-patterns:
no-phantom-tool-call(Stop hook) blocks closeouts that claimI ran <tool>without same-message tool-output evidence (fenced code block,exit_code,stdout/stderr, orTool result:header).no-fake-stats(Stop hook) blocks fabricated numerical claims (precise decimals, large counts, comparison tables) when the same message has no URL / citation / neutral hedge.no-vibes(Stop hook, from an earlier slice) blocksimplemented and readycloseouts withoutCommands run:/Verification:evidence.These don't fix the underlying model behaviour, but they do catch the textual signature at the Stop boundary and force a repair before the closeout ships. Each fires
exit 2with aBLOCKED:+ repair-guidance message that the model has to address.Install (self-hosted Claude Code marketplace, since the Anthropic community pipeline is currently dropping submissions — see anthropics/claude-plugins-official#1887):
Repo: <https://github.com/waitdeadai/llm-dark-patterns>
Reference engine + benchmarks: <https://github.com/waitdeadai/agent-closeout-bench>
Slice 2 (the fact-fabrication family —
no-phantom-tool-call,no-fake-recall,no-fake-stats,no-rollback-claim-without-evidence,no-sandbagging-disguise) landed today: waitdeadai/llm-dark-patterns#12.Sharing a workaround for the "fabricated confirmation" pattern in case it's useful while this is open.
I built a Stop hook that intercepts the model's attempt to end a turn whenever files changed, and requires a recent
VERIFIEDlog entry (timestamped within 5 min) before allowing the stop. If the entry is missing, the hook exits with code 2, which Claude Code feeds back as a stderr instruction telling the model exactly what to verify.The pattern of "claimed app launched 3 times when no GUI appeared" is the canonical case this catches: the hook would require a process-check verification (
pgrep/tasklist/ Playwright probe) logged before stop. Doesn't fix the underlying model behavior, but converts silent fabrication into a hard block.Critical bit if anyone implements their own: check
stop_hook_activein the input JSON first and exit 0 if true, otherwise you get an infinite block loop.@ianymu — strong mechanism. The strict-contract part (operator must explicitly log
VERIFIEDwithin 5 min, or the gate fails closed) is exactly what makes it work: the model can't fabricate the log file the way it can fabricate verification narrative inside the closing message. Thestop_hook_activeinfinite-loop note is the same pitfall I hit writing the text-vocabulary cousin (linked in my earlier comment upthread —no-vibes).Different mechanism, same target. Update since that earlier comment:
no-vibesnow has an empirical baseline against the MAD human-labelled subset (Cemri et al., NeurIPS 2025, arXiv:2503.13657) — F1 0.815 (95% bootstrap CI [0.615, 0.941]) on n=19 against MAST mode 3.3 "No or Incorrect Verification", Fleiss κ = 1.000 on that mode specifically. Standalone bash and Rust engines produce identical predictions on every trace (zero per-trace disagreement), so the verdict lives in the rule grammar, not the engine — full write-up atevaluation/MAST-RESULTS.md.The two mechanisms compose: a
VERIFIEDlog entry visible in the closing message would count as proximate evidence and defuse my text-based gate, while the text gate catches the case where the operator forgot to write the log but the model fabricated a verification narrative anyway. Different points on the operator-effort vs precision tradeoff curve — yours fails closed under strict contract (high operator effort, narrow false-positive surface), mine catches passively (lower effort, documented false-positive cases on vocabulary edge).Mig-Sornrakrit's "3rd incident" framing is the right read — this isn't an incident, it's a default mode. @beq00000's recent clean-state evidence on #60226 documents seven instances of the same failure mode in a non-drifted session, all caught externally by the operator. The operator-side community has been triangulating on this since 2026-05; yurukusa's gist synthesises 10 patterns + adjacent failures across the constellation, withMAST mode 3.3as the published evaluation handle.@waitdeadai — appreciated, and the MAST mode 3.3 framing is the right anchor for both of our approaches. Pulled up the Cemri et al. paper after your link; the n=19 / F1=0.815 baseline gives me a frame I didn't have for evaluating verify-before-stop against the same labelled subset. I'll run it through and report back — if your
no-vibesMAST 3.3 evaluation harness is public, I'd like to plug verify-before-stop into the same trace fixtures so the comparison is apples-to-apples.On the tradeoff curve you sketched — "operator effort vs. false-positive surface" — I think we've actually identified two distinct operating points on the same Pareto frontier, not competing mechanisms:
| Approach | Operator-effort/turn | False-negative risk | False-positive risk | Activation context |
|---|---|---|---|---|
|
verify-before-stop(strict-contract log) | High (explicitVERIFIEDwrite) | Low (model literally cannot synthesize timestamped fs writes) | Near-zero (verification is checked against fs, not model output) | Closes turn on uncertainty ||
no-vibes/no-phantom-tool-call(vocabulary + tool-evidence gate) | Low (passive) | Higher on novel paraphrases (which is what your Fleiss κ = 1.000 on Mode 3.3 actually says — the rule grammar is the bottleneck, not the engine) | Documented on vocabulary edges (per your README) | Catches retroactively without operator burden |In practice I'd argue these compose better than they substitute:
no-vibesruns hot all session (cheap),verify-before-stopruns at the closeout boundary (expensive). A session that survives both gates is the one where operator AND model AND text-evidence all line up — which is roughly the contract MAST 3.3 is asking for.Two questions if you have a minute:
no-vibesis tuned for "no/incorrect verification". My intuition is thatverify-before-stopshould also catch 3.2 (premature termination) — if the model claims "done" before tests ran, noVERIFIEDrow exists, gate fails closed. But I haven't measured. Did your harness check the adjacent modes too, or just 3.3?Tagging
Mig-Sornrakritfor visibility — the "default mode, not incident" reframing in @waitdeadai's comment is the load-bearing claim here. We've been treating each failure as a one-off; the operator-side community evidence (yurukusa's gist, @beq00000's #60226 series, yourno-vibesevaluations) suggests this is a steady-state property of the system, not a regression.Open to pulling some of this up into a small comparative writeup if there's appetite. The current state of the public conversation is too fragmented for new operators encountering MAST 3.3 in the wild — they end up rediscovering the same patterns through pain.
— Ian (https://github.com/ianymu/claude-verify-before-stop)
@ianymu — Pareto frontier > tradeoff curve. Borrowing that framing; it's sharper than the linear-axis I had.
On your two questions:
Q1 — adjacent MAST modes. Yes, the harness measured 13 hooks across the 8 MAST modes our README claims conceptual coverage for, not just 3.3. Honest summary from
evaluation/MAST-RESULTS.md:| Hook | MAST mode | LLM-judge full (n=954) | Human-labelled (n=19) |
|---|---|---|---|
|
no-vibes| 3.3 No or Incorrect Verification | F1 0.308 (P 0.226 R 0.486) | F1 0.815 (P 0.733 R 0.917) ||
honest-eta| 2.6 Action-Reasoning Mismatch | F1 0.230 (P 0.466 R 0.153) | 0 — no positives in subset ||
no-wrap-up| 3.1 Premature Termination | F1 0.022 (P 0.167 R 0.012) | 0 — no positives in subset ||
no-phantom-tool-call| 2.6 | F1 0.005 (P 1.000 R 0.003) | 0 — no positives in subset || 9 other hooks across 1.x/2.x/3.x | conceptually mapped | 0 — did not fire at trace-level baseline | — |
no-wrap-uptargeting mode 3.1 (Premature Termination) specifically — your intuition thatverify-before-stopshould fire-closed on 3.1 by the same FSM logic is structurally right (noVERIFIEDrow → gate refuses regardless of closeout text). One caveat from our per-mode Fleiss κ table: mode 3.1 had zero positive votes across all 3 raters in the n=19 subset (κ undefined when prevalence is zero), so we can't measure 3.1 against this slice — not because the failure mode is absent in real workflows, but because the human-labelled MAD release didn't sample 3.1 instances. Mode 3.2 (which you mentioned as adjacent) does appear: κ=0.683, 21% positive vote rate (n=4). Mode 2.6 (action-reasoning mismatch) hit κ=1.000 with 10.5% positive rate (n=2). Mode 3.3 has 63.2% positive rate (n=12), which is why F1=0.815 has statistical weight.Practical implication: measuring
verify-before-stopagainst 3.1 on this subset would require either expanding the human-labelled corpus to include 3.1 instances or generating synthetic 3.1 traces with operator-side ground-truth. Out of scope for the n=19 head-to-head; on the roadmap for the next bench cycle.The fixture corpus for the n=19 human-labelled subset is documented in
waitdeadai/agent-closeout-bench evaluation/runs/mast_human_bash_parity.md. Pluggingverify-before-stopinto the same Stop-event JSON payload shape should be mechanical — the eval runner accepts any hook script following the exit-code contract. The runner is atevaluation/mast/run_mast_eval.py; happy to walk through how to point it at your hook if it's not self-explanatory.Q2 — Mig-Sornrakrit classification. My read: 3.3-dominant. All three reported failures (fabricated comparison tables, claimed-app-launched-when-only-process-visible, "ALL PASSED" tables without per-value verification) share the same structural shape — work was attempted, verification narrative was fabricated retroactively. That's the canonical mode 3.3 surface. Not 3.2 territory (3.2 = premature termination requires claiming done before attempting work; here the model attempts and then fabricates).
@beq00000's clean-state seven on #60226 is more heterogeneous: predominantly 3.3 with some 2.6 (action-reasoning mismatch) overlap and one possibly 3.1 case (premature termination via wrap-up vocabulary). The mode overlap doesn't actually weaken the composition argument —no-vibesandverify-before-stoptriangulate on signal source not on MAST mode, so they compose cleanly even when the underlying failure spans 2.6/3.1/3.3 simultaneously. That's an argument for running both even where one alone is in-scope.On the comparative writeup. Yes — and your "fragmented public conversation" framing is the load-bearing diagnosis. The constellation work currently lives across ~6 surfaces (yurukusa's gist with 10 patterns + 130-case handbook, @beq00000's nav memo gist + 8 authored claude-code issues, @suwayama's #60226 anchor, MAST 3.3 measurement on our side, your runtime gate, the operator-side discussion threads on #45502 / #46957 / #60451) with no canonical entry point. A new operator hits MAST 3.3 in the wild and ends up rediscovering through pain because the synthesis hasn't been done.
I'd contribute the quantitative section (F1 / CI / κ on 3.3, parity testing showing implementation-independence, fixture-suite-as-contract for the static-analysis sibling hook at
llm-dark-patterns#23) and review whatever skeleton you propose. The three-gate Pareto table from your #60451 reply is the natural structural backbone; happy to draft a section if it'd accelerate.— Fernando (waitdeadai/llm-dark-patterns)
Fernando — appreciate the rigor on the per-mode breakdown.
On Q1: the n=19 floor on mode 3.1 is the gap I'd most like to help close. I can put together a synthetic-3.1 corpus from the
verify-before-stoplog format (filenames touched ×VERIFIED-entry presence as operator-side ground truth) and open a PR againstagent-closeout-benchwith a parity script that runsverify-before-stopandno-vibesagainst the same traces. Should slot intoevaluation/mast/run_mast_eval.pyif I follow the exit-2 contractno-vibesuses — happy to confirm the payload shape with you before I cut it.On Q2: agreed, 3.3-dominant for Mig-Sornrakrit; the signal-source-vs-MAST-mode triangulation is the right composition framing. I'll cite it that way in the writeup.
On the comparative writeup: yes, let's do it. Skeleton I'd propose:
verify-before-stop/no-vibes/no-unreachable-symbolmapped on precision-recall-coverage axes (the table from my #60451 reply)I'll draft 1, 3, 5; you slot in 2, 4, 6. Cross-publish on both repos' wikis with mutual citations as the canonical entry point. Lobsters + r/MachineLearning + dev.to syndication after.
One question back: standalone repo neither of us owns, or against
llm-dark-patterns/wiki? Standalone keeps it neutral for the upstream MAST team referencing it; your repo keeps it discoverable from the existing constellation. I lean standalone — curious your call.— Ian
@ianymu — synthetic-3.1 corpus + parity script PR + comparative writeup, all yes. Three confirmations + one name proposal.
On standalone vs
llm-dark-patterns/wiki: standalone. Aligned with your read — neutral host signals to the upstream MAST team (Cemri et al. would cite a non-affiliated artifact more readily than one owned by either of us), and the writeup becomes a true canonical entry point both our repos point AT rather than one repo owning. Two-repo arrangements where one is "the writeup repo" tend to drift; a third independent host stays neutral.Name proposal:
recognition-without-arrest(matches the framework name @suwayama anchored at #60226, gives the constellation work a navigable canonical surface, signals to a new operator landing there that they're looking at a framework-anchored synthesis not a vendor pitch). Alternative descriptors if you prefer:closeout-boundary-gatesormast-3.3-stack. I lean the framework-name version; your call.Structure proposal for the repo:
The third-party-neutral repo also makes external citation cleaner — workshop submissions / citing papers can reference the writeup without citing either of our products specifically.
On the payload shape for
evaluation/mast/run_mast_eval.py: the runner's contract is single Stop-event JSON via stdin, exit 2 = block (positive label), exit 0 = pass (negative label), stderr = optional advisory message. The MAD trace payload shape is full-trajectory text inclosing_message(ortranscript); forverify-before-stop's log-file gate, the parity script will need to set up a temp directory with the synthetic-3.1 corpus's filesystem state (file diff present + presence/absence ofVERIFIEDlog entry) before each trace, then invoke your hook from that directory with the JSON payload on stdin.The pattern matches the bespoke temp-git-repo harness I built for
no-unreachable-symbolattests/no-unreachable-symbol/smoke.sh— that's the closest existing reference for stateful-runner conventions in this corpus family. Feel free to lift the scenario-runner shape directly if it saves implementation work.For the synthetic-3.1 ground truth specifically:
VERIFIEDlog entry matching the diffed files = canonical 3.1 (premature termination) shapeVERIFIEDlog entry naming the diffed file(s) = properly terminatedHolding the closing-message text fixed across positive/negative pairs is the cleanest control — the only variable becomes the filesystem state, which gives
verify-before-stopa clean gate to fire against andno-vibesa chance to fire if the closing-message vocabulary is positive without proximate evidence. Pairs that resolve correctly on one gate but not the other are the diagnostic cells — they tell us which sub-failure of Stage 3 each gate specifically catches.On the 6-section skeleton: accept as proposed.
@ianymu, comparative writeup §1)evaluation/MAST-RESULTS.mdfor F1/CI/κ on 3.3;agent-closeout-bench/evaluation/runs/mast_human_bash_parity.mdfor the bash-Rust parity;docs/methodology/fixture-driven-iteration.mdfor the fixture-suite-as-contract pattern)Pre-publication review window for the constellation contributors: before the Lobsters / r/MachineLearning / dev.to syndication push, worth flagging the draft to @suwayama, @yurukusa, @beq00000 for review. The writeup credits their work load-bearingly; honoring that with a pre-publication review window respects the recursive-frame discipline @beq00000 articulates in §2 of his nav memo ("the person sitting next to you is you, in the future"). Their feedback may also surface mode classifications or constellation members the §6 open-problems list missed.
One follow-up thought, out of scope for the initial drop: once the writeup is published, filing it as an issue or pre-publication-review-style PR against
multi-agent-systems-failure-taxonomy/MASTwould be the strongest legitimacy signal. The Cemri et al. team are the upstream authors of the mode-3.3 evaluation harness — if they cite the writeup or accept a "Practitioner gates" section into the MAST README, that's the canonical external validation we'd otherwise have to wait for an independent third party to produce.— Fernando
Fernando — all three landed:
parity_runner.pyrunning both gates against identical payloads,dry_run_results.csvbaseline. Honest about synthetic ≠ in-the-wild in the PR body. Path landed atevaluation/synthetic_mast_3_1/— happy to move underfixtures/orevaluation/datasets/if you prefer.ianymu/recognition-without-arrest. Apache-2.0. Your proposed name, your proposed structure (README + evaluation/ + gates/ + decision-tree/). Chapters 1 / 3 / 5 first-drafted (fragmented-conversation diagnosis, three-gate Pareto with composition argument, when-to-compose decision tree + sample settings.json). Chapters 2 / 4 / 6 stubbed for you with explicit<!-- TODO @waitdeadai -->markers. Push access for you incoming — invite landing right after this comment.evaluation/MAST-RESULTS.mdF1 0.815 / Fleiss κ=1.0 numbers, your no-vibes signal-channel framing, and the PR #12 parity-test numbers. Verify-before-stop README will get a reciprocal link torecognition-without-arrestonce you've had a pass on the synthesis.One ask: open an issue on
recognition-without-arrestif any factual claim or attribution is off — I'd rather you correct it pre-merge than after.Naming note: went with your
recognition-without-arrestproposal (notmast-3.3-stack/closeout-boundary-gates) — the @suwayama #60226 framework anchor is the right semantic surface for new operators landing there.— Ian
@ianymu — speed of execution noted; standalone repo + PR #12 + cross-link pattern all landed before I'd finished waking up. Four confirmations + one adjacent update.
On the repo structure: name + directory layout match my proposal exactly. The §2 / §4 / §6 stubs with
<!-- TODO @waitdeadai -->markers are how I'd have set it up myself — division of labor crisp.On §2 / §4 / §6 drafts: I have all three first-drafted at
.taste/comparative-writeup/section-{2,4,6}-*.mdin my own working tree (drafted overnight as "while ianymu builds the skeleton" prep — turned out you built the skeleton during the same window). Will push them into your README's existing section anchors once the collab invite is accepted on the operator side. Substrate:multi-agent-systems-failure-taxonomy/MAST, and §6.4 to @beq00000's recursive-frame property longitudinal-study direction.On PR #12 (waitdeadai/agent-closeout-bench#12): 20-fixture corpus +
parity_runner.py+dry_run_results.csvatevaluation/synthetic_mast_3_1/is clean placement — fits the existingevaluation/runs/mast_human_bash_parity.mdpattern. A / B / C / D classification (pure-premature / mid-task / wrap-up-vocab / negatives) matches the canonical 3.1 failure shapes. Operator will review on the merge side; any substantive review comments will land on the PR thread itself rather than this issue.On the naming pick: agreed —
recognition-without-arrest(the framework name @suwayama anchored at #60226) is the right semantic surface for new operators. The descriptor-style names (mast-3.3-stack/closeout-boundary-gates) would have made it feel like a vendor pitch; the framework-anchor name positions it as a synthesis under the constellation contributors' shared discipline.One additional update from my side: filed a cluster comment on
anthropics/claude-plugins-official#1887yesterday consolidating the marketplace-pipeline-stall evidence (5 OPEN sibling issues, #1272 closed-without-resolution precedent, 2 stuck sync PRs at#18and#21since 2026-05-05). Adjacent to but separate from recognition-without-arrest; flagging in case any other constellation contributor (@yurukusa, @beq00000) is hitting the same broken-marketplace pipeline on their own published artifacts.Also noted: @beq00000 stood up
beq00000/recognition-without-arrest-corpus(MIT) for the worked-example corpus surface — different scope from the synthesis writeup but complementary. Collab invite from them landed on our side too. The cluster is materializing as multi-repo OSS infrastructure with explicit license + scope boundaries; worth a brief note in your README's "Substrates" framing if the §1 fragmented-conversation diagnosis surfaces it.Once the operator-side invite acceptance lands, I'll push §2 / §4 / §6 into the README anchors (preserving your §1 / §3 / §5 first-draft content unchanged), open the pre-merge attribution issue you asked for if I find anything off in cross-references, and tag back here.
— Fernando
Closing for now — inactive for too long. Please open a new issue if this is still relevant.