fix: detect stale and incomplete graph state - #2858
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. No changes could be formally verified in this run.
Graphify review — findings
Adds a full graph-state and build-locking layer to graphify/detect.py: GraphState/StalenessReason enums, GraphStateResult, and cross-platform lock primitives (acquire_build_lock, _mutex_lock, PID-liveness/create-time checks, lock-file parse/write) plus ephemeral vs. persistent sidecar classification. Introduces is_scan_ignored/resolves_under_root detection helpers and watch-side is_remote_source, write_build_config, and storedsourcepaths_normalize. Regenerates the skill fragments and expected skillgen outputs across all agents (codex, amp, kilo, kiro, windows, droid, etc.) and adds tests covering graph-state freshness/staleness, persistent-sidecar lock handling, and installer/hook wiring.
Worth a look
- Linux create_time uses wrong stat field index due to comm parentheses —
graphify/detect.py· 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.
- Permission-denied PID probe is treated as dead —
graphify/detect.py· 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.
- PID stored in the build lock is the transient python -c process, not the actual long-running build —
graphify/skill-agents.md· 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.
- Repo-controlled .graphify_python is executed via shell word splitting —
graphify/skill-claw.md:71· 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.
- Build lock is acquired by a short-lived subprocess and is not held during the build —
graphify/skill-claw.md:143· 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 — 2582 functions depend on the 1298 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 468 callers, 41 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
detect()— 107 callers, 15 callees - new:
save_manifest()— 34 callers, 11 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
inspect_graph_state()— 32 callers, 9 callees - new:
extract_corpus_parallel()— 26 callers, 10 callees - …and 29 more — each is listed as a finding
Verification — 2582 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: 2219 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_rebuild\_lock.
The verifier did not have enough to check \_rebuild\_lock, 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 `out_dir` is annotated `Path` — outside the synthesizable primitive/collection set
· 3 grounded finding(s) anchored inline below; 34 more finding(s) on lines outside this diff (see the check run).
| lock_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | ||
|
|
||
|
|
||
| def acquire_build_lock( |
There was a problem hiding this comment.
acquire_build_lock()
fans out to 7 callees (efferent coupling); 23 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| ) | ||
|
|
||
|
|
||
| def release_build_lock( |
There was a problem hiding this comment.
release_build_lock()
7 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return sorted(dirty_files) | ||
|
|
||
|
|
||
| def inspect_graph_state( |
There was a problem hiding this comment.
inspect_graph_state()
fans out to 9 callees (efferent coupling); 32 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
080c30f to
076c967
Compare
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. No changes could be formally verified in this run.
Graphify review — findings
Adds a GraphState/BuildLockStatus layer to graphify/detect.py: introduces GraphState/StalenessReason enums, GraphStateResult, and cross-platform build-lock primitives (acquire_build_lock, _mutex_lock, PID create-time verification for recycled-PID detection, lock-file parse/write helpers) plus ephemeral/persistent sidecar classification. Threads the new state and locking through graphify/watch.py (stored-source anchoring, gitignore-gated eviction, AST manifest rebuild) and regenerates all graphify_skill_* skill files and their tools/skillgen expected fixtures. Adds test coverage for graph-state transitions, concurrent stale lock takeover, corrupt-graph overwrite refusal, and skill/install regeneration.
Worth a look
- Permission-denied PIDs are treated as dead —
graphify/detect.py· 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.
- PermissionError is treated as dead PID —
graphify/detect.py· 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.
- release_build_lock deletes token_file even when it fails to release the lock —
graphify/detect.py· 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.
- release_build_lock deletes token_file even when lock is owned by another token —
graphify/detect.py· 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.
- Project-controlled .graphify_python is executed as a command —
graphify/skill-agents.md:69· 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 — 2730 functions depend on the 1565 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 468 callers, 41 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
detect()— 107 callers, 15 callees - new:
save_manifest()— 34 callers, 11 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
inspect_graph_state()— 32 callers, 9 callees - new:
extract_corpus_parallel()— 26 callers, 10 callees - …and 29 more — each is listed as a finding
Verification — 2730 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: 2367 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_rebuild\_lock.
The verifier did not have enough to check \_rebuild\_lock, 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 `out_dir` is annotated `Path` — outside the synthesizable primitive/collection set
· 3 grounded finding(s) anchored inline below; 34 more finding(s) on lines outside this diff (see the check run).
| lock_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | ||
|
|
||
|
|
||
| def acquire_build_lock( |
There was a problem hiding this comment.
acquire_build_lock()
fans out to 7 callees (efferent coupling); 23 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| ) | ||
|
|
||
|
|
||
| def release_build_lock( |
There was a problem hiding this comment.
release_build_lock()
7 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return sorted(dirty_files) | ||
|
|
||
|
|
||
| def inspect_graph_state( |
There was a problem hiding this comment.
inspect_graph_state()
fans out to 9 callees (efferent coupling); 32 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
076c967 to
de359f5
Compare
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. No changes could be formally verified in this run.
Graphify review — findings
Adds a graph-state and build-lock layer to graphify/detect.py: introduces GraphState/StalenessReason enums, GraphStateResult/BuildLockStatus dataclasses, and cross-platform helpers (_get_process_create_time, _mutex_lock, lock-file parse/write, acquire_build_lock) that detect stale/recycled-PID locks and classify ephemeral vs. persistent sidecar artifacts. Regenerates the per-host skillgen expected fixtures and rationale snapshots to match, and expands watch/install/graph-state/skillgen tests (six-value enum unification, Windows PowerShell fast-path, per-host audit, Kilo/Gemini install artifacts).
Worth a look
- Build lock never released on early exit / build failure —
graphify/skill-agents.md· 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.
- Repo-controlled .graphify_python is executed as a shell command —
graphify/skill-agents.md:75· 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.
- Fast-path Python import is shadowable by the target repository —
graphify/skill-agents.md:77· 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.
- Skill acquires build lock but only releases it on the success path —
graphify/skill-agents.md:668· 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.
- Cached interpreter path is executed via unquoted shell expansion —
graphify/skill-amp.md:69· 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 — 2739 functions depend on the 1574 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 468 callers, 41 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
detect()— 107 callers, 15 callees - new:
save_manifest()— 34 callers, 11 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
inspect_graph_state()— 32 callers, 9 callees - new:
extract_corpus_parallel()— 26 callers, 10 callees - …and 29 more — each is listed as a finding
Verification — 2739 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: 2376 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_rebuild\_lock.
The verifier did not have enough to check \_rebuild\_lock, 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 `out_dir` is annotated `Path` — outside the synthesizable primitive/collection set
· 3 grounded finding(s) anchored inline below; 34 more finding(s) on lines outside this diff (see the check run).
de359f5 to
20ea437
Compare
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. No changes could be formally verified in this run.
Graphify review — findings
Adds graph-state and build-lock machinery to graphify/detect.py: GraphState/StalenessReason/LockResult enums, GraphStateResult/BuildLockStatus dataclasses, cross-platform process-creation-time and advisory-mutex helpers, and acquire_build_lock with PID-liveness and recycled-PID detection plus stale-lock reclamation. Introduces ephemeral vs. persistent sidecar artifact classification for the .graphify output directory. Updates skillgen fragments/expected skills and adds watch, install, graph-state, and skillgen tests covering lock contention and dirty-graph reporting.
Worth a look
- Build lock write follows repo-controlled symlink —
graphify/detect.py:2586· 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.
- Cached interpreter path is executed from the repository without validation —
graphify/skill-agents.md:72· 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.
- Dirty extensionless shebang sources are ignored when deciding graph staleness —
graphify/detect.py· Escalate · medium- 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.
- Windows _is_pid_alive false-positive on unrelated exit codes —
graphify/detect.py· Escalate · medium- 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.
- _parse_porcelain_z requires literal space at status offset, breaking rename/copy entries with non-space codes —
graphify/detect.py· Escalate · medium- 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 — 2859 functions depend on the 1694 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 468 callers, 41 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
detect()— 109 callers, 15 callees - new:
save_manifest()— 34 callers, 11 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
inspect_graph_state()— 33 callers, 9 callees - new:
extract_corpus_parallel()— 26 callers, 10 callees - …and 33 more — each is listed as a finding
Verification — 2859 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: 2496 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_rebuild\_lock.
The verifier did not have enough to check \_rebuild\_lock, 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 `out_dir` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_translate\_bash\_block.
The verifier did not have enough to check \_translate\_bash\_block, 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
· 3 grounded finding(s) anchored inline below; 38 more finding(s) on lines outside this diff (see the check run).
| lock_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | ||
|
|
||
|
|
||
| def acquire_build_lock( |
There was a problem hiding this comment.
acquire_build_lock()
fans out to 7 callees (efferent coupling); 28 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| ) | ||
|
|
||
|
|
||
| def release_build_lock( |
There was a problem hiding this comment.
release_build_lock()
14 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return sorted(dirty_files) | ||
|
|
||
|
|
||
| def inspect_graph_state( |
There was a problem hiding this comment.
inspect_graph_state()
fans out to 9 callees (efferent coupling); 33 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
20ea437 to
0bf03b7
Compare
0bf03b7 to
ec9ebe7
Compare
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. No changes could be formally verified in this run.
Graphify review — findings
Adds a graph freshness/state model and cross-platform build-lock mechanism to graphify/detect.py: introduces GraphState/StalenessReason/GraphStateResult, LockResult/BuildLockStatus, and acquire_build_lock backed by an OS advisory mutex (_mutex_lock), PID-liveness plus process-create-time validation for stale-lock reclamation, symlink-safe atomic writes, and ephemeral-vs-persistent artifact classification. Regenerates the graphify skill templates across all agent variants (agents/codex/claw/kilo/trae/windows/opencode) and their skillgen expectations for the new install/extract/cluster/health-check steps. Adds test coverage in test_graph_state.py, test_watch.py, test_skillgen.py, and test_install.py for lock reclamation, stale-on-head-mismatch detection, incremental rebuild preservation, and skill body wording.
Worth a look
- Runs
git statuswith repository-controlled hooks/config enabled —graphify/detect.py· 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.
- Fast-path executes repo-controlled .graphify_python —
graphify/skill-agents.md:69· 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.
- Linux create_time uses field index 21 which is fragile for process names containing spaces/parens —
graphify/detect.py· Escalate · medium- 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.
- Linux process start time parsing breaks when /proc stat comm contains spaces —
graphify/detect.py:2470· Escalate · medium- 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.
- Mutex lock follows attacker-controlled symlink —
graphify/detect.py:2513· Escalate · medium- 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 — 2877 functions depend on the 1712 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 468 callers, 41 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
detect()— 109 callers, 15 callees - new:
save_manifest()— 34 callers, 11 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
inspect_graph_state()— 34 callers, 9 callees - new:
extract_corpus_parallel()— 26 callers, 10 callees - …and 33 more — each is listed as a finding
Verification — 2877 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: 2514 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_rebuild\_lock.
The verifier did not have enough to check \_rebuild\_lock, 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 `out_dir` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_translate\_bash\_block.
The verifier did not have enough to check \_translate\_bash\_block, 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
· 3 grounded finding(s) anchored inline below; 38 more finding(s) on lines outside this diff (see the check run).
| _safe_write_file(lock_path, "\n".join(lines) + "\n") | ||
|
|
||
|
|
||
| def acquire_build_lock( |
There was a problem hiding this comment.
acquire_build_lock()
fans out to 8 callees (efferent coupling); 29 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| ) | ||
|
|
||
|
|
||
| def release_build_lock( |
There was a problem hiding this comment.
release_build_lock()
14 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return sorted(dirty_files) | ||
|
|
||
|
|
||
| def inspect_graph_state( |
There was a problem hiding this comment.
inspect_graph_state()
fans out to 9 callees (efferent coupling); 34 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
Summary
Fixes #2841.
Graphify previously treated the presence of
graphify-out/graph.jsonassufficient to take the existing-graph fast path. This could cause stale graphs
or partially completed builds to be silently trusted.
This change introduces explicit graph-state inspection and integrates it into
the skill build lifecycle.
What changed
Graph state inspection
Added
inspect_graph_state()with six states:ABSENT— no existing graphBUILDING— another Graphify build is currently activeINCOMPLETE— intermediate artifacts from an interrupted build remainSTALE— the graph no longer represents the current corpusUNVERIFIABLE— freshness cannot be establishedFRESH— graph provenance and working tree are currentFor git-tracked corpora, freshness considers:
graph.json'sbuilt_at_commitvs currentHEADGraphify's own output directory and unrelated unsupported files are excluded
from dirty-tree detection.
Interrupted-build detection
Known ephemeral artifacts are detected and surfaced as
INCOMPLETE, including:.graphify_extract.json.graphify_ast.json.graphify_semantic.json.graphify_chunk_*.json.graphify_analysis.json.graphify_detect.jsonPersistent sidecars are explicitly excluded.
Unified build locking
The skill workflow and existing rebuild paths now share the same build-lock
protocol.
The lock provides:
The implementation supports POSIX and Windows locking primitives.
Skill integration
The existing-graph fast path now inspects graph state before deciding whether
to query or rebuild.
The build workflow also:
Generated platform skills and skillgen snapshots were updated accordingly.
Tests
Added comprehensive graph-state/build-lock coverage including:
Validation:
49graph-state tests passed66skillgen tests passed115watch tests passed locally, with9skipped and2pre-existing Windows-specific failures4693 passed, 30 skipped, 19 pre-existing Windows/environment failuresgit diff --cached --checkcleanThe existing watch-lock tests were also updated to validate the new
backward-compatible lock format: PID remains on line 1 while ownership
metadata is stored on subsequent lines.