Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion graphify/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,13 @@ def detect_incremental(
semantically.
kind="ast": a file is "changed" when its ast_hash is missing or its
content has changed. Use this for `graphify update`.
kind="auto": per-file-type — code files compare against ast_hash,
everything else (docs/papers/images/videos) against semantic_hash.
Use this for the skill's `--update` runbook (#2033): code never
receives semantic extraction, so its missing semantic_hash is not a
pending obligation and must not re-queue an AST-built corpus, while
docs downgraded by an AST-only rebuild still get their semantic
catch-up.

Fast path: mtime unchanged + hash matches → unchanged (free, no disk IO
beyond stat). Slow path: mtime bumped → compare MD5 against the relevant
Expand Down Expand Up @@ -1765,7 +1772,10 @@ def detect_incremental(
# Normalise legacy {mtime, hash} to new schema
if "hash" in stored and "ast_hash" not in stored:
stored = {"mtime": stored.get("mtime", 0), "ast_hash": stored["hash"], "semantic_hash": ""}
hash_key = "semantic_hash" if kind == "semantic" else "ast_hash"
if kind == "auto":
hash_key = "ast_hash" if ftype == "code" else "semantic_hash"
else:
hash_key = "semantic_hash" if kind == "semantic" else "ast_hash"
stored_hash = stored.get(hash_key, "")
# Missing semantic_hash means update ran but extract hasn't — always re-extract
if not stored_hash:
Expand Down
5 changes: 4 additions & 1 deletion graphify/skill-aider.md
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,10 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto': ast_hash for code, semantic_hash for docs/papers/images. The
# semantic default would report every code file of an AST-built corpus as
# changed and re-extract the whole corpus (#2033).
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2))
Path('.graphify_incremental.json').write_text(json.dumps(result))
Expand Down
5 changes: 4 additions & 1 deletion graphify/skill-devin.md
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,10 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto': ast_hash for code, semantic_hash for docs/papers/images. The
# semantic default would report every code file of an AST-built corpus as
# changed and re-extract the whole corpus (#2033).
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result))
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/agents/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/amp/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/claude/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/claw/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/codex/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/copilot/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
21 changes: 19 additions & 2 deletions graphify/skills/droid/references/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path

result = detect_incremental(Path('INPUT_PATH'))
# kind='auto' compares code files against ast_hash and docs/papers/images
# against semantic_hash. The default (kind='semantic') reports every code file
# of an AST-built corpus as changed — code never gets a semantic_hash — turning
# a 12-file delta into a full-corpus re-extraction (#2033); plain kind='ast'
# would instead skip the semantic catch-up for docs an AST-only rebuild
# downgraded (#2014). Per-type is the only mode that matches this pipeline.
result = detect_incremental(Path('INPUT_PATH'), kind='auto')
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
Expand Down Expand Up @@ -142,7 +148,18 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
save_manifest(incremental['files'], root='INPUT_PATH')
# Two stamps, not one kind='both' pass (#2033): 'both' would forge a
# semantic_hash for every file in the corpus, including docs this run never
# semantically extracted, silently cancelling their future catch-up.
# 1) kind='ast' over the full corpus: refreshes ast_hash; an untouched file
# keeps its semantic_hash, a changed one has it cleared.
# 2) kind='semantic' over just the files the semantic pass covered this run —
# the changed docs/papers/images (and transcribed videos). On a code-only
# update that set is empty and the second stamp is skipped.
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
sem_files = {t: incremental.get('new_files', {}).get(t, []) for t in ('document', 'paper', 'image', 'video')}
if any(sem_files.values()):
save_manifest(sem_files, root='INPUT_PATH', kind='semantic')
print('[graphify update] Manifest saved.')
"
```
Expand Down
Loading