mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-25 16:22:30 +00:00
The Read tool returns at most 2000 lines per call and reports no error when it
truncates, so a subagent that read only the head of a long file still writes a
well-formed chunk. Step B3 treated "chunk exists and parses" as success, and
because build_merge replaces a re-extracted file's nodes, the unread tail was
not merely missed: it was deleted, the run reported success, and the manifest
recorded the file as done so no later --update retried it. On a 4165-line file
that left 103 nodes covering 31% of the document, with 90 of 131 entries gone
and the manifest treating it as finished.
Three changes, in the fragments, so all thirteen disk-dispatching hosts pick
them up from one edit:
The extraction spec (verbose and compact) now states the cap and forbids
emitting nodes for a partly-read file, and asks the subagent to report a
top-level "coverage" map of file -> lines read. A short count is recoverable; a
missing entry is indistinguishable from a complete read, which is why the
machine check needs the prompt to ask.
Step B1 bounds a chunk by total lines as well as file count (20-25 files AND
~6000 lines), and gives any file past 2000 lines a chunk to itself. File count
alone was only half the mechanism: a subagent holding 25 files also dilutes
attention, which is how a 590-line plan came back with zero nodes.
Step B3.5 compares each reported coverage against the file's real length, names
what was short, and hands the list to the two places that decide whether a file
is ever looked at again: save_semantic_cache(partial_source_files=...) stamps
those entries partial, which load_cached treats as a miss, and
_stamped_manifest_files(partial_source_files=...) leaves them unstamped so
detect_incremental re-queues them. Both are the existing contract the library
path already uses for a truncated LLM response (#933, #1950), so the skill and
library paths now recover the same way. Nodes already found for those files are
kept; the next run's replace-on-re-extract swaps them for a complete set.
The check is fail-safe in the direction that loses nothing: no coverage reported
means no file is flagged, and a flagged file is re-read rather than trusted.
Codex collects subagent results in memory with no per-chunk files on disk, so it
gets the prompt-layer fix but not the Step B3.5 gate; its inline extraction has
no chunk file to compare against.
Tests execute the emitted python instead of matching its source (the #2126/#2641
convention), including a round trip through the real semantic cache: the
partly-read file must come back as uncached, while the fully-read file still
hits. Reverting the fix makes that round trip return an empty uncached list -
the file served from cache forever, which is the bug.
Fixes #2225
(cherry picked from commit 0b6a1868af)