Skip to content

fix(detect): clear stale semantic_hash for dispatched-but-omitted files (#1948)#1956

Closed
alphanury wants to merge 1 commit into
Graphify-Labs:v8from
alphanury:fix/1948-manifest-stale-semantic-hash
Closed

fix(detect): clear stale semantic_hash for dispatched-but-omitted files (#1948)#1956
alphanury wants to merge 1 commit into
Graphify-Labs:v8from
alphanury:fix/1948-manifest-stale-semantic-hash

Conversation

@alphanury

Copy link
Copy Markdown
Contributor

Fixes #1948.

save_manifest's seed loop copied every on-disk manifest row verbatim for files still alive on disk, with no way to distinguish "never touched this run" from "dispatched this run but omitted from the result". So a doc whose chunk the LLM dropped on a --force re-run kept its stale semantic_hash from the earlier successful run, detect_incremental(kind="semantic") reported it unchanged, and the #1890 re-run promise silently never happened — exactly the failure this issue describes, and what the manual "blank the hash by hand" workaround was compensating for.

Change

  • save_manifest() (graphify/detect.py) gains a keyword-only clear_semantic set: for any file in it, the seed loop forces semantic_hash to "" instead of inheriting the prior run's value. Defaults to None (no-op) — every existing caller and the save_manifest drops untouched files on incremental runs, forcing full re-extraction #917/save_manifest retains scan-excluded files, causing permanent false deleted_files #1908 seed/prune behaviors are unchanged (guard tests pass unmodified).
  • cli.py derives the set as semantic_files minus _stamped_manifest_files()'s result — i.e. dispatched this run but not stamped, regardless of why. semantic_files is what was actually sent to the backend (narrowed by the incremental gate and --code-only, widened by deep mode), so untouched live files are never in the set and a partial incremental run cannot blank the rest of the corpus's stamps.
  • The set is threaded through all three _save_manifest call sites.

Review note (why the derivation matters)

An earlier draft of this branch derived the set from files_by_type (the full live corpus). Adversarial review before pushing caught that this wipes semantic_hash for every unchanged file on any partial incremental run — forcing a full-corpus re-extraction on the next run, which would have been worse than the bug. The committed version derives from the dispatched subset and adds an end-to-end regression test for exactly that scenario.

Tests

  • Unit: test_save_manifest_clear_semantic_erases_stale_hash_for_omitted_file (red before the detect.py change, green after), plus the seven pre-existing seed/prune guards, unmodified.
  • End-to-end (new): test_incremental_partial_run_preserves_untouched_semantic_hash — full-scan run stamps two docs, a second incremental run dispatches only the changed one (asserted via the captured dispatch list), and the untouched doc's semantic_hash must survive in manifest.json. Red on the files_by_type derivation, green on the committed one.
  • Suites: test_extract_cli.py + test_extract_code_only_cli.py + test_detect.py + test_build.py → 226 passed. Full suite: 3269 passed; the 15 failures in test_ollama_retry_cap.py/test_skillgen.py are pre-existing on a clean v8 checkout (verified byte-identical before/after).

🤖 Generated with Claude Code

…es (Graphify-Labs#1948)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
safishamsi added a commit that referenced this pull request Jul 17, 2026
…is cleared)

Follow-up to #1956: a doc stamped complete on a prior run that truncates
(partial) this run must have its stale semantic_hash cleared so it is re-queued.
Partial files are dropped by _stamped_manifest_files and therefore land in
clear_semantic — pin that composed behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

Landed on v8 (ships in 0.9.18), cherry-picked to preserve your authorship. Thanks @alphanury — save_manifest no longer seeds a stale semantic_hash for a dispatched-but-omitted file, so the #1890 re-run actually fires. Rebased onto current v8 where it composes with #1950: a truncated (partial) file is also dropped from the stamped set and therefore cleared/re-queued. Added a regression test pinning that #1948 x #1950 interaction.

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.

save_manifest seeds stale semantic_hash from prior manifest, masking LLM-omitted docs — the re-run promised by #1890 never happens

2 participants