[BUG] Long-horizon agentic failure: the model investigates a failure instead of searching for the working reference already in the repository

Status Open
Maintainer reply None cached
Activity 1 comment · opened Aug 25, 2026

Environment

  • Claude Code (VS Code extension), Opus-tier model, multi-session project over ~2.5 months
  • Task class: bootstrapping a from-scratch operating-system port — cross-toolchain, then a native

self-hosting toolchain, then building GNU GDB on the new target

  • Iteration cost: ~5 hours per empirical iteration (full emulator boot + build), which is what

turns this behaviour from a nuisance into a project-schedule failure

Summary

Across this project the model repeatedly produced rigorous, well-evidenced, correctly-reasoned
analyses of the wrong question.
The common cause is not a knowledge gap or a reasoning error. It is
a search-strategy failure: when something fails, the model investigates the failure, and does not
first search the working tree for the nearest thing that succeeds and diff against it.

In every instance below, the answer already existed — in the same repository, sometimes in the same
file, once in a commit made the same day as the defect.

Instance 1 — the fix and the defect were committed the same day, and the gap survived 50 days

A C++ compile on the target failed with:

*** A compiler with support for C++17 language features is required

with all ten -std= variants answering no. The model diagnosed a compiler capability gap and
investigated the compiler — twice, across sessions, each investigation costing multi-hour boots.

The actual cause: the native C++ driver's built-in header search is <prefix>/include/c++/14.2.0,
while the staged sysroot ships c++/14. The compile needed three -isystem flags. It was one missing
flag set, not a missing language feature.

Two commits, the same day:

| commit | date | what it did |
|---|---|---|
| A | day 0 | created the correct C++ include flag set (STL_INCS) as a build variable |
| B | day 0 | wrote a C++ compiler invocation without it |

Commit B copied a working C compiler invocation into a C++ slot without accounting for C++'s
extra include directories — while commit A, hours earlier, had established exactly what those were.

50 days elapsed before anyone compared them. The check that resolved it, once asked, was a single
grep for the build variable, and took under a minute.

Instance 2 — the answer sat unread in a log the model had itself generated

Before one multi-hour build the model wrote a "wall forecast" predicting what would fail next. The
forecast was thorough and its reasoning was sound. It was scoped to whether the source files would
compile — and every one of them did.

It never asked what tools the build required. The build system's own configure output, generated
three days earlier by the model's own prior run, had already enumerated every missing tool, in one
place, in the log file.

Nobody opened it. The model then discovered those same missing tools one at a time, one ~5-hour boot
each.

Instance 3 — an absence claim that was false because the model conflated host with target

The model asserted: "the archiver has never been built for this target — it does not exist."

False. A cross-archiver had existed in the tree for two months. It targeted the new platform but
ran on the build machine, and the failing lookup happened inside the guest. The missing axis was
host, never target.

The phrase "built for X" is ambiguous across those two axes and the model used it as though it were
not. A human corrected this. The same conflation recurred twice more in the same project on different
components.

Instance 4 — the platform was already supported upstream, the entire time

After ~2 months of treating this as an unsupported-platform port, a 25-minute static reading of the
upstream source showed that GDB already supports this platform family: the host is recognized in
configure.host, the target in configure.tgt, the native file set in configure.nat, and the
platform-specific native-debugging header and target-dependent source file both ship in the tarball.

No port needed to be written. That was true on day one.

Instance 5 — the model denied a capability while the procedure for it sat in its own memory file

This one happened while writing this report, which is why it is included.

Asked to file this report, the model stated: *"I cannot submit this. There is no tool in Claude Code
that files feedback on the user's behalf."* It wrote that claim into a document and committed it to
version control as fact.

It was false. The project's own persistent memory contained a reference note titled *"How to file a
Claude Code bug from this extension"*, giving the exact working command, noting the CLI's own feedback
constant points at the public issue tracker, and recording four issues already filed by this user
through that path
. That note is loaded into context at session start.

The model did not read its own notes before asserting an absence about its own capabilities.

⇒ this is the same failure applied reflexively: an unverified negative claim, stated confidently,
committed as documentation, while the disproving evidence was already loaded. The user caught it,
as they caught the other four.

What actually resolved it

One static-analysis pass, ~25 minutes, zero boots:

  1. read the recorded configure results for every missing tool, and check which the build's Makefiles

actually invoke → of ~13 "missing tools", one was real; four were referenced by zero Makefiles

  1. sweep every source subdirectory for #include <...> of headers absent from the target sysroot,

filtering to unguarded includes in files that this host actually compiles → zero real hits

  1. read configure.host / configure.tgt / configure.nat → platform already supported

This replaced a serial discovery loop that had been running for two months at ~5 hours per wall.

The behaviour being reported

When a task has expensive iterations and a large existing corpus (repo, logs, prior output),
the model:

  • prefers deep investigation of the failing case over cheap search for a working case. It reaches

for "why did this fail?" and rarely for "what nearly identical thing already succeeds, and how does
it differ?"

  • treats its own prior outputs as spent. Logs it generated, forecasts it wrote, and files it

edited are not re-consulted; they are re-derived.

  • scopes an investigation confidently and does not check the scope itself. The wall forecast was

correct within its scope and useless outside it, and nothing prompted the model to ask whether the
scope was the right one.

  • produces high-confidence prose that launders an unverified claim ("it does not exist", "both

gates pass it") which then survives into documentation and steers later sessions.

This is hard for a user to catch precisely because the analysis quality is high. A visibly confused
model gets corrected. A model that is rigorous about the wrong question gets believed.

What would have helped

  1. Before deep-diving a failure, search for the nearest passing analogue and diff it. In all four

instances this single move was sufficient and cheap.

  1. When iterations are expensive, prefer static enumeration over serial empirical discovery.

Enumerate all remaining blockers in one pass rather than discovering them one iteration at a time.

  1. Re-read own prior artifacts before generating new ones. Especially logs from earlier runs of

the same task.

  1. Treat absence claims as requiring a receipt, and disambiguate host/target/build-time/run-time

axes explicitly rather than with a phrase like "built for X".

Note on scale

The user's assessment is that this pattern cost roughly five months of effort across the wider
project. The portion I can verify from version-control history is: a 2.5-month repository in which the
specific defect above survived 50 days with its fix committed the same day, and the platform was
supported upstream throughout.

---

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗