fix: prevent manifest timestamp churn on unchanged updates - #2846
fix: prevent manifest timestamp churn on unchanged updates#2846hopstreax wants to merge 1 commit into
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 5 advisory finding(s) below merit a look before merge.
Formal verification. 4 change(s) tested, no difference found (not proven).
Graphify review — findings
Reverts the unreleased 0.9.46 changes: drops Common Lisp extraction support (README tables, [commonlisp] extra) and removes several build-time fixes. Removes the specific-over-generic relation collapse guard (_GENERIC_RELATIONS) in build_from_json, drops hyperedge member rewiring on dedup in build, and removes the stranded external-import stub sweep after source pruning in build_merge. Folds the reverted CHANGELOG entries back and remarks 0.9.45 as unreleased.
Worth a look
- Generic edge relations can overwrite specific relations again —
graphify/build.py:1230· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Reverting generic-relation collapse allows specific edges to be downgraded to generic —
graphify/build.py:1233· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Removed public hyperedges keyword from deduplicate_entities —
graphify/dedup.py:467· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Hyperedge members no longer rewired to dedup survivors, losing participants —
graphify/dedup.py:782· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Common Lisp extensions no longer classified as code —
graphify/detect.py:46· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 4564 functions depend on the 2317 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 464 callers, 41 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
build_from_json()— 153 callers, 18 callees - new:
detect()— 99 callers, 15 callees - new:
deduplicate_entities()— 63 callers, 20 callees - new:
build_merge()— 43 callers, 14 callees - new:
save_semantic_cache()— 51 callers, 9 callees - new:
_extract_generic()— 18 callers, 24 callees - …and 141 more — each is listed as a finding
Verification — 4564 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: 4226 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify build.
The verifier did not have enough to check build, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_from\_json.
The verifier did not have enough to check build\_from\_json, 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 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_merge.
The verifier did not have enough to check build\_merge, 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 `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify deduplicate\_entities.
The verifier did not have enough to check deduplicate\_entities, 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 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify \_load\_dir\_own\_ignore.
The verifier did not have enough to check \_load\_dir\_own\_ignore, 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 `d` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_load\_graphifyignore.
The verifier did not have enough to check \_load\_graphifyignore, 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
No difference found (not proven): No behavior difference found in save\_manifest (not a proof).
The verifier ran both versions of save\_manifest on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, 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 \_java\_annotation\_names.
The verifier did not have enough to check \_java\_annotation\_names, 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 182 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify \_java\_collect\_type\_refs.
The verifier did not have enough to check \_java\_collect\_type\_refs, 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 `skip` is annotated `frozenset[str] | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify extract\_markdown.
The verifier did not have enough to check extract\_markdown, 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 \_resolve\_go\_type\_references.
The verifier did not have enough to check \_resolve\_go\_type\_references, 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 \_resolve\_java\_type\_references.
The verifier did not have enough to check \_resolve\_java\_type\_references, 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: non-vacuity: domain too small (only 1 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
No difference found (not proven): No behavior difference found in normalize\_id (not a proof).
The verifier ran both versions of normalize\_id on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_search\_tokens (not a proof).
The verifier ran both versions of \_search\_tokens on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_subgraph\_to\_text (not a proof).
The verifier ran both versions of \_subgraph\_to\_text on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 149 more finding(s) on lines outside this diff (see the check run).
|
Shipped in v0.9.47 via authorship-preserving cherry-pick, with added no-op-then-change and corrupt-manifest tests. Thanks @hopstreax! Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.47 |
Summary
Fixes #2838.
graphify updatewas rewritinggraphify-out/manifest.jsonon every run by updating the per-fileseentimestamp, even when the source files and graph topology were unchanged.This caused
graphify-out/to remain dirty indefinitely and made the pre-push hook create a trailing graph commit on every push.What changed
seentimestamp for genuinely unchanged manifest entries.seentimestamp when a file is new or its relevant content/hash state changes.manifest.jsonwhen the final manifest payload is identical to the existing file.Validation
Verified that:
seentimestampseentimestamp and updated hashgraphify update .runs leavemanifest.jsonbyte-identicalgraphify-out/remains clean after consecutive no-op updatesRelevant tests pass, including the manifest, incremental mtime collision, partial cache, and atomic write suites.
The remaining Windows-specific failures in the broader suite were reproduced on the parent commit and are unrelated to this change.