fix(cache): retry zero-node semantic results - #2931
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Rejects zero-node/zero-hyperedge semantic extractions (edge-only or empty results) in save_semantic_cache and load_cached, so such files are treated as cache misses and re-dispatched (#2927). Stops stamping the manifest for edge-only semantic output in _stamped_manifest_files (drops edges from the counted collections), and adds _zero_node_stamped_semantic_sources plus dispatch_command wiring to re-queue doc/paper/image files that pre-#2927 manifests already stamped despite having no nodes or hyperedges in graph.json. Adds tests/test_cache.py coverage for edge-only rejection, node/hyperedge-only acceptance, and legacy poisoned-entry healing.
Worth a look
- Edge-only semantic results silently dropped from cache and manifest, discarding valid edges —
graphify/cache.py:999· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Semantic edge-only cache entries now load as misses —
graphify/cache.py:1005· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Semantic heal ignores nodes stored under graph.nodes —
graphify/cli.py:454· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1468 functions depend on the 381 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 474 callers, 42 callees - new:
_rebuild_code()— 98 callers, 50 callees - new:
detect()— 108 callers, 15 callees - new:
save_semantic_cache()— 58 callers, 11 callees - new:
load_cached()— 48 callers, 7 callees - new:
file_hash()— 48 callers, 6 callees - new:
extract_corpus_parallel()— 26 callers, 11 callees - new:
dispatch_command()— 2 callers, 120 callees - …and 19 more — each is listed as a finding
Verification — 1468 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: 1152 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify load\_cached.
The verifier did not have enough to check load\_cached, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify save\_semantic\_cache.
The verifier did not have enough to check save\_semantic\_cache, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
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)
Could not verify: Could not verify \_stamped\_manifest\_files.
The verifier did not have enough to check \_stamped\_manifest\_files, 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: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
· 27 more finding(s) on lines outside this diff (see the check run).
Closes #2927
Summary
Semantic extraction results containing edges but no nodes can currently be
cached as successful results. Because the cache is keyed by content hash, the
file is then treated as unchanged on subsequent incremental runs and is never
redispatched.
This can permanently leave the file without its semantic nodes.
Root Cause
During incremental extraction:
nodes=[],edges=[...].save_semantic_cache()writes the result to the semantic cache.nodes.
rebuild is performed.
Fix
A semantic extraction is now considered valid for caching and manifest stamping
when it contains:
Edge-only results are therefore treated as incomplete and remain retryable.
The fix:
extraction,
Tests
Added regression coverage for:
All #2927-focused and adjacent tests pass.