Skip to content

fix(extract): hollow, unparseable and omitting chunks count as incomplete so the shrink guard stays armed (#3105) - #3114

Open
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/hollow-chunks-arm-shrink-guard
Open

fix(extract): hollow, unparseable and omitting chunks count as incomplete so the shrink guard stays armed (#3105)#3114
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/hollow-chunks-arm-shrink-guard

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Closes #3105.

The problem

A full build writes with force=True, bypassing the #479 shrink guard, unless the run was classified as incomplete. _extraction_incomplete tracked hard failures only: a crashed extractor pass, a chunk that raised. A chunk that comes back hollow after every retry, or as invalid JSON, or that simply omits some of its files does not raise — it returns fewer nodes and counts as a succeeded chunk. So the reporter's two consecutive --update runs on an unchanged repo went:

run raised chunks hollow guard result
1 3 8 fired refused — 570 nodes preserved
2 0 6 never fired wrote 111 nodes over 570

Run 2 even printed 10/24 dispatched file(s) produced no nodes and are absent from the graph and still counted as complete. With an LLM backend this is the normal way an extraction silently produces a fraction of the graph — the case the guard exists for, and the one case where it was disabled.

The change

After the semantic pass, a non-empty uncovered_files list (files the model omitted — which is also where an invalid-JSON chunk's files land) or any partial/hollow file now sets _extraction_incomplete = True, with one stderr line saying how many files produced no nodes and how many came back truncated or hollow, and that --allow-partial overrides. From there the existing machinery does the rest: force=False, the shrink guard compares against the existing graph, a refusal exits non-zero and leaves the manifest unstamped so the next run retries those files.

A complete run — every dispatched file covered, nothing partial — keeps force=True exactly as before, so a legitimately shrinking full build (dedup, deleted code) is unaffected.

Tests

tests/test_hollow_chunks_arm_shrink_guard.py — 5 tests through the real CLI with a stubbed semantic pass: an omitting chunk arms the guard (force=False); a hollow chunk (files marked partial, nothing raised — the reporter's run 2) arms it; --allow-partial still overrides; a fully covered run keeps force=True with no warning; and a refused write exits non-zero without stamping the manifest. With the fix reverted, the two "arms the guard" tests fail. test_incomplete_build_guard, test_extract_cli and test_extract_code_only_cli are unchanged (60 passed); the full suite matches the v8 baseline.

…lete (Graphify-Labs#3105)

The Graphify-Labs#479 shrink guard is bypassed (force=True) on a run classified as
complete, and _extraction_incomplete tracked hard failures only: a crashed
pass, a chunk that raised. A chunk that comes back hollow after every
retry, or as invalid JSON, or that simply omits some of its files does not
raise - it returns fewer nodes and counts as a SUCCEEDED chunk. So two
consecutive --update runs on an unchanged repo: the first had 3 raised
chunks and the guard refused; the second had 0 raised and 6 hollow, read
as complete, and wrote 111 nodes over a 570-node graph without a word.

With an LLM backend that is the normal way an extraction silently
produces a fraction of the graph, so it now arms the guard exactly like a
crashed chunk does: a non-empty uncovered_files list (files the model
omitted, which is where invalid-JSON chunks land) or any partial/hollow
file marks the run incomplete, with a stderr line saying so. A complete
run keeps force=True as before; --allow-partial still overrides; a refused
write still leaves the manifest unstamped so the next run retries.
Copilot AI lite review requested due to automatic review settings August 26, 2026 12:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Treats hollow, unparseable, or file-omitting semantic chunks as an incomplete extraction: dispatch_command now sets _extraction_incomplete when fresh reports uncovered_files or partial-marked source files, so the #479 shrink guard stays armed (force=False) instead of silently overwriting a larger graph with a fraction of it (#3105). Previously these cases returned fewer nodes without raising and counted as SUCCEEDED, bypassing the guard; --allow-partial still forces the write, and an ordinary fully-covered run keeps its force-write path. Adds test_hollow_chunks_arm_shrink_guard covering the omit, hollow, allow-partial override, clean-run, and manifest-not-stamped-on-refusal cases.

No blocking issues surfaced.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 256 functions depend on the 67 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 122 callees
  • new: _stale_graph_sources() — 7 callers, 6 callees
  • new: _run_hook_guard() — 4 callers, 7 callees
  • new: test_poisoned_manifest_is_healed() — 0 callers, 6 callees

Verification — 256 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 209 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify dispatch\_command.

The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)

· 4 more finding(s) on lines outside this diff (see the check run).

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.

extract silently overwrote a 570-node graph with 111 nodes: hollow LLM responses don't count as "incomplete"

2 participants