Summary
#2879 ("Deliberately-skipped data JSON (#1224) is classified as failed extraction, so it is re-queued on every incremental run") was fixed in v0.9.48 and closed. On v0.9.64 the same user-visible symptom is back, but through a different code path, so the original issue is still correctly closed — its fix is working.
The recurrence comes from the #2543 heal, _zero_node_stamped_code_sources in cli.py. The two mechanisms now hold contradictory beliefs about the same state:
Both ship in 0.9.64. The heal wins, and the file is re-extracted on every run, forever.
As with #2879, correctness is unaffected — the cost is unbounded repeated work plus a permanent, misleading "prior failed extraction" warning that never clears.
Evidence that this is not the #2879 path
#2879's signature was a blank ast_hash in the manifest. That is fixed: on 0.9.64 there are zero blank-ast_hash entries in the manifest, yet ten .json files are still re-queued on every run.
data JSONs with no nodes in graph.json: 10
ast_hash=5d5902a3 .claude/settings.json
ast_hash=0587f2cb .claude/settings.local.json
ast_hash=960a5833 .vite/vitest/results.json
ast_hash=cd310488 evals/clustering.json
ast_hash=7aa4a4ae evals/metadata-tagging.json
ast_hash=b4e67073 src/lib/citation-integrity-baseline.json
ast_hash=9b7aa817 src/lib/entity-index-baseline.json
ast_hash=110b35c9 src/lib/source-comment-citation-baseline.json
ast_hash=16f676af tsconfig.e2e.json
ast_hash=a5e11940 tsconfig.json
total manifest entries with BLANK ast_hash: 0
Every one is data-shaped JSON — editor config, tsconfig, test-result output, and committed baseline fixtures. All are correctly stamped. All are re-queued anyway.
Mechanism
cli.py, _zero_node_stamped_code_sources:
"""Manifest-stamped code files with a registered extractor but ZERO nodes
in the existing graph.json (#2543 heal).
...
Bounded by the same no-wedge property: if it fails again this run it is now
left unstamped, and if it succeeds its nodes enter graph.json so the next
scan stops re-queuing it.
"""
The stated no-wedge property does not hold for a file whose extractor correctly produces nothing. Such a file can never "succeed" by the heal's definition, because success is defined as nodes entering graph.json. A data JSON has no symbols to contribute, so the exit condition is unreachable and the heal re-queues it on every run indefinitely.
The predicate is the same pair of conditions #2879 identified as indistinguishable — zero nodes and an extractor is registered — just evaluated against graph.json rather than against the extraction result.
Reproduction
Two consecutive identical runs on a repo containing data-shaped JSON. The second run should be a no-op; it is not, and the output is byte-identical to the first:
$ graphify extract . --code-only
[graphify extract] re-queuing 10 manifest-stamped code file(s) with no nodes in graph.json (prior failed extraction, #2543)
[graphify extract] 10 code, 0 docs, 0 papers, 0 images changed; 443 unchanged; 0 deleted
[graphify extract] wrote graphify-out/graph.json: 5005 nodes, 12371 edges, 214 communities
[graphify extract] incremental summary: 443 files cached/unchanged, 10 re-extracted, 0 deleted
$ graphify extract . --code-only
[graphify extract] re-queuing 10 manifest-stamped code file(s) with no nodes in graph.json (prior failed extraction, #2543)
[graphify extract] 10 code, 0 docs, 0 papers, 0 images changed; 443 unchanged; 0 deleted
[graphify extract] wrote graphify-out/graph.json: 5005 nodes, 12371 edges, 214 communities
[graphify extract] incremental summary: 443 files cached/unchanged, 10 re-extracted, 0 deleted
Identical node and edge counts confirm the re-extraction produces no change — it cannot, because these files have no symbols.
Suggested fix
The heal needs the same distinction #2879 introduced: declined is not failed. If the reason a file contributes no nodes is that its extractor deliberately declined it (the #1224 data-JSON path), exclude it from _zero_node_stamped_code_sources rather than treating it as a poisoned manifest entry. Reusing whatever marker the v0.9.48 fix already computes would keep the two mechanisms consistent.
Related
Environment
graphify 0.9.64 · macOS 22.6.0 · Python 3.12 · installed via uv tool install graphifyy
Summary
#2879 ("Deliberately-skipped data JSON (#1224) is classified as failed extraction, so it is re-queued on every incremental run") was fixed in v0.9.48 and closed. On v0.9.64 the same user-visible symptom is back, but through a different code path, so the original issue is still correctly closed — its fix is working.
The recurrence comes from the #2543 heal,
_zero_node_stamped_code_sourcesincli.py. The two mechanisms now hold contradictory beliefs about the same state:graph.jsonis a prior failed extraction, and re-queues it.Both ship in 0.9.64. The heal wins, and the file is re-extracted on every run, forever.
As with #2879, correctness is unaffected — the cost is unbounded repeated work plus a permanent, misleading "prior failed extraction" warning that never clears.
Evidence that this is not the #2879 path
#2879's signature was a blank
ast_hashin the manifest. That is fixed: on 0.9.64 there are zero blank-ast_hashentries in the manifest, yet ten.jsonfiles are still re-queued on every run.Every one is data-shaped JSON — editor config,
tsconfig, test-result output, and committed baseline fixtures. All are correctly stamped. All are re-queued anyway.Mechanism
cli.py,_zero_node_stamped_code_sources:The stated no-wedge property does not hold for a file whose extractor correctly produces nothing. Such a file can never "succeed" by the heal's definition, because success is defined as nodes entering
graph.json. A data JSON has no symbols to contribute, so the exit condition is unreachable and the heal re-queues it on every run indefinitely.The predicate is the same pair of conditions #2879 identified as indistinguishable — zero nodes and an extractor is registered — just evaluated against
graph.jsonrather than against the extraction result.Reproduction
Two consecutive identical runs on a repo containing data-shaped JSON. The second run should be a no-op; it is not, and the output is byte-identical to the first:
Identical node and edge counts confirm the re-extraction produces no change — it cannot, because these files have no symbols.
Suggested fix
The heal needs the same distinction #2879 introduced: declined is not failed. If the reason a file contributes no nodes is that its extractor deliberately declined it (the #1224 data-JSON path), exclude it from
_zero_node_stamped_code_sourcesrather than treating it as a poisoned manifest entry. Reusing whatever marker the v0.9.48 fix already computes would keep the two mechanisms consistent.Related
Environment
graphify 0.9.64 · macOS 22.6.0 · Python 3.12 · installed via
uv tool install graphifyy