Write a portable manifest.json from the skill runbooks (#1417)
The skill runbooks called save_manifest(...) with no root=, so manifest keys were stored as absolute paths (e.g. /Users/.../main.go). Cloning or moving the repo then broke `graphify --update`: detect_incremental matched none of the cached keys, so the entire corpus re-extracted (and the report showed ghost nodes). The native `graphify extract`/`update` CLI already passed root=target; the agent-executed runbooks did not. All four runbook call sites now pass root='INPUT_PATH', relativizing manifest keys to the scan root (portable forward-slash form, per save_manifest's #777 support): the lean-core skill.md Step 9, the shared --update reference, and the Aider/Devin monoliths. The monolith edit is registered as a new sanctioned change-class (_is_manifest_root_fix_line) in the round-trip guard, mirroring how the #1392 runbook fixes were sanctioned. Regenerated + blessed all artifacts; added a regression test asserting every shipped runbook threads root= into save_manifest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d168de999a
commit
1f42a2da2b
@@ -4,6 +4,7 @@ Full release notes with details on each version: [GitHub Releases](https://githu
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Fix: the skill runbooks now write a portable `manifest.json`. Step 9 (full build) and the `--update` reference called `save_manifest(...)` without `root=`, so manifest keys were stored as absolute paths; cloning or moving the repo then broke `graphify --update` — every cached file missed and the whole corpus re-extracted. All four runbook call sites (the lean-core `skill.md`, the Aider/Devin monoliths, and the shared `--update` reference) now pass `root='INPUT_PATH'`, relativizing keys to the scan root to match the native `graphify update` path. The monolith change is registered as a new sanctioned change-class in the round-trip guard (#1417).
|
||||
- Fix: hyperedge `source_file` is now relativized to the scan root like nodes and edges. `build_from_json(root=...)` relativized `source_file` on `nodes[]` and `links[]`, but stored `graph.hyperedges[]` verbatim, so a semantic subagent's absolute path (e.g. `/Users/.../CLAUDE.md`) leaked into `graph.json`. The fix lives in `build_from_json` (not `to_json`, which has no `root` to relativize against) and mirrors the existing node/edge handling (#1418).
|
||||
- Fix: the `GRAPHIFY_OUT` override is now honoured everywhere instead of a hardcoded `"graphify-out"` literal. The name is consolidated into a single `graphify.paths` module (was duplicated across `__main__`, `cache`, and `watch`); `security.validate_graph_path`'s `base=None` discovery + fallback, `callflow_html`'s project-root resolution, and the post-commit/post-checkout hook bodies (which now read the env var at hook-run time) all use it. Previously a renamed output dir validated against the wrong base or made the hook miss `.graphify_root` (#1423).
|
||||
|
||||
|
||||
@@ -674,7 +674,7 @@ from graphify.detect import save_manifest
|
||||
|
||||
# Save manifest for --update
|
||||
detect = json.loads(Path('.graphify_detect.json').read_text())
|
||||
save_manifest(detect['files'])
|
||||
save_manifest(detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('.graphify_extract.json').read_text())
|
||||
|
||||
@@ -522,7 +522,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -553,6 +556,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -522,7 +522,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -553,6 +556,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -792,7 +792,7 @@ from graphify.detect import save_manifest
|
||||
|
||||
# Save manifest for --update
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text())
|
||||
save_manifest(detect['files'])
|
||||
save_manifest(detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text())
|
||||
|
||||
@@ -522,7 +522,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -553,6 +556,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -517,7 +517,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -548,6 +551,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -523,7 +523,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -554,6 +557,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -521,7 +521,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -552,6 +555,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -547,7 +547,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -578,6 +581,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
+6
-1
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -503,6 +503,32 @@ def test_monoliths_carry_the_1392_runbook_fixes():
|
||||
assert "if not wrote:" in body
|
||||
|
||||
|
||||
def test_generated_runbooks_pass_root_to_save_manifest():
|
||||
"""#1417: every save_manifest call in a shipped runbook threads root=.
|
||||
|
||||
Without root=, save_manifest stores absolute path keys, so a clone or move
|
||||
breaks --update (every cached file misses and the whole corpus re-extracts).
|
||||
The full-build (skill.md / monoliths) and the --update reference all relativize
|
||||
the manifest to the scan root via root='INPUT_PATH'. This guards the actual
|
||||
shipped artifacts; --check keeps them in sync with the fragments.
|
||||
"""
|
||||
targets = [
|
||||
REPO_ROOT / "graphify" / "skill.md",
|
||||
REPO_ROOT / "graphify" / "skill-aider.md",
|
||||
REPO_ROOT / "graphify" / "skill-devin.md",
|
||||
]
|
||||
targets += sorted((REPO_ROOT / "graphify" / "skills").glob("*/references/update.md"))
|
||||
checked = 0
|
||||
for path in targets:
|
||||
for ln in path.read_text(encoding="utf-8").splitlines():
|
||||
if "save_manifest(" in ln and "import" not in ln:
|
||||
checked += 1
|
||||
assert "root=" in ln, (
|
||||
f"{path.relative_to(REPO_ROOT)}: save_manifest without root= (#1417): {ln.strip()!r}"
|
||||
)
|
||||
assert checked >= 4, f"expected save_manifest calls across the runbooks, found {checked}"
|
||||
|
||||
|
||||
def test_devin_keeps_its_multi_field_frontmatter():
|
||||
"""devin renders inline, so its 4+-field frontmatter is preserved verbatim."""
|
||||
platforms = gen.load_platforms()
|
||||
|
||||
@@ -674,7 +674,7 @@ from graphify.detect import save_manifest
|
||||
|
||||
# Save manifest for --update
|
||||
detect = json.loads(Path('.graphify_detect.json').read_text())
|
||||
save_manifest(detect['files'])
|
||||
save_manifest(detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('.graphify_extract.json').read_text())
|
||||
|
||||
@@ -522,7 +522,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -553,6 +556,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -522,7 +522,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -553,6 +556,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -792,7 +792,7 @@ from graphify.detect import save_manifest
|
||||
|
||||
# Save manifest for --update
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text())
|
||||
save_manifest(detect['files'])
|
||||
save_manifest(detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text())
|
||||
|
||||
@@ -522,7 +522,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -553,6 +556,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -517,7 +517,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -548,6 +551,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -523,7 +523,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -554,6 +557,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -521,7 +521,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -552,6 +555,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -547,7 +547,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -578,6 +581,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -525,7 +525,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -556,6 +559,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -674,7 +674,7 @@ from graphify.detect import save_manifest
|
||||
|
||||
# Save manifest for --update
|
||||
detect = json.loads(Path('.graphify_detect.json').read_text())
|
||||
save_manifest(detect['files'])
|
||||
save_manifest(detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('.graphify_extract.json').read_text())
|
||||
|
||||
@@ -460,7 +460,10 @@ from graphify.detect import save_manifest
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
|
||||
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
|
||||
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
|
||||
save_manifest(detect.get('all_files') or detect['files'])
|
||||
# root= relativizes the manifest keys to the scan root (same base as the build),
|
||||
# so the on-disk manifest is portable across clones/machines and a later --update
|
||||
# matches cached files instead of missing every one (#1417).
|
||||
save_manifest(detect.get('all_files') or detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
|
||||
@@ -491,6 +494,8 @@ find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
|
||||
rm -f graphify-out/.needs_update 2>/dev/null || true
|
||||
```
|
||||
|
||||
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
|
||||
|
||||
Tell the user (omit the obsidian line unless --obsidian was given):
|
||||
```
|
||||
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
|
||||
|
||||
@@ -792,7 +792,7 @@ from graphify.detect import save_manifest
|
||||
|
||||
# Save manifest for --update
|
||||
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text())
|
||||
save_manifest(detect['files'])
|
||||
save_manifest(detect['files'], root='INPUT_PATH')
|
||||
|
||||
# Update cumulative cost tracker
|
||||
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text())
|
||||
|
||||
@@ -139,7 +139,10 @@ print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])
|
||||
|
||||
# Save manifest so next --update diffs against today's state, not the
|
||||
# prior run's baseline (prevents ghost-node reports on subsequent updates).
|
||||
save_manifest(incremental['files'])
|
||||
# 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')
|
||||
print('[graphify update] Manifest saved.')
|
||||
"
|
||||
```
|
||||
|
||||
@@ -798,6 +798,21 @@ def _is_zero_node_guard_fix_line(line: str) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _is_manifest_root_fix_line(line: str) -> bool:
|
||||
"""Whether a line is part of the manifest-portability fix (#1417).
|
||||
|
||||
The monolith Step 9 called ``save_manifest(detect['files'])`` with no
|
||||
``root=``, so the manifest stored absolute path keys and a clone or move
|
||||
broke ``--update`` — every cached file missed and the whole corpus
|
||||
re-extracted. The call now threads ``root='INPUT_PATH'`` so keys are
|
||||
relativized to the scan root, matching the native ``graphify update`` path.
|
||||
Both the old bare call (removed) and the new rooted call (added) match here;
|
||||
the ``import`` guard avoids matching the ``from graphify.detect import
|
||||
save_manifest`` line.
|
||||
"""
|
||||
return "save_manifest(" in line and "import" not in line
|
||||
|
||||
|
||||
# Every line that may differ between a rendered monolith and its pristine v8
|
||||
# baseline. Each predicate documents one sanctioned change-class; a blank line is
|
||||
# allowed because the multi-line fix blocks insert spacing. Anything else failing
|
||||
@@ -810,6 +825,7 @@ _SANCTIONED_MONOLITH_DIFFS = (
|
||||
_is_content_scope_fix_line,
|
||||
_is_cache_unlink_fix_line,
|
||||
_is_zero_node_guard_fix_line,
|
||||
_is_manifest_root_fix_line,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user