Repository navigation
manifest.json uses absolute paths as keys — --update always triggers full rebuild on CI or different clone path #1964
Description
Activity
Tried to reproduce this against current v8 (post-0.9.19) to help answer the version question above — results support the overlap-with-#777/#1125 theory:
Fresh manifest, current code: built at path A (
save_manifest(..., root=target), the shape every current caller uses — cli extract, watch, and the current skill runbook), keys come out relative (src/main.py). Copied the whole clone (includinggraphify-out/) to a different path and randetect_incremental: 0 new, 0 deleted, all unchanged. Portable as designed.Legacy absolute-keyed manifest (what pre-0.8.31 — or an outdated installed skill runbook that never passed
root=— wrote) evaluated at a new clone path: every file reported new, and on a machine where the old absolute paths don't exist they'd all additionally report deleted — which matches the "193 deleted + 618 new" signature exactly.So the manifest itself looks healthy on current code, and the symptom points to a
graphify-out/whose manifest predates the relativization (upgrading the package doesn't rewrite it until the next successful save —save_manifestdoes migrate seeded legacy rows to relative keys on its next run withroot=).One part of the report that IS still true on v8:
cache/stat-index.jsonkeys are absolute (abs_key = str(resolved)incache.py), so a clone move silently loses the stat fastpath (full re-hash, though not re-extraction) and dead rows for the old path accumulate. That might be worth splitting into its own focused issue.- added a commit that references this issue
on Aug 1, 2026 - added a commit that references this issue
on Aug 13, 2026 Looks fixed on current
v8, flagging with a caveat below.When a
rootis passed,manifest.jsonkeys are now rewritten to be relative to that root before the atomic write (graphify/detect.py,save_manifest), with an inline comment explicitly citing "the #777/#1964 portability guarantee". So a manifest built at one absolute path and then read after a repo move/clone-to-a-different-path should resolve by relative key instead of forcing a full rebuild.Caveat: I verified this at the manifest-write layer (the code path is unambiguous) but didn't complete a full CLI-level before/after repro (build at path A, move to path B, confirm
graphify update .reuses the cache instead of rebuilding everything). If anyone can still reproduce a full rebuild after a plain repo move on currentv8, please reopen with the exact repro — that would point at a different layer still using absolute keys.Related but distinct symptom on the same root cause (manifest path-key inconsistency), found 2026-08-09 on
graphifyy 0.8.31— noting it here rather than as a separate issue since the underlying inconsistency (some code paths write absolute keys, others relative, without normalizing) looks like the same class of bug as this one, just observed a different way.Our case:
graphify-out/manifest.jsonhad 641 entries for ~308 real files — 307 in absolute-path form, 334 in relative-path form, many duplicating the same file under both formats with different data (e.g. the relative entry forCLAUDE.mdhad a stalemtimeand an emptysemantic_hash; the absolute entry had a currentmtimeand a real hash). Unlike this issue's symptom (cross-machine/CI clone → always full rebuild, safe-but-wasteful), ours was the opposite failure mode:detect_incremental()sometimes reads the stale duplicate and under-reports changes — it told us "2 files changed" when 146 were actually missing from the semantic cache, i.e. a false negative that could let genuinely-changed content silently go unindexed rather than trigger an unnecessary rebuild.Workaround used: don't trust
detect_incremental()when the manifest might be mixed-format; instead run a fulldetect()+check_semantic_cache(all_files)(ingraphify.cache), which normalizes each path to absolute before lookup and so is immune to the format-mixing — treat that as ground truth for what actually needs (re-)extraction.Not re-verified against the current release (this was on 0.8.31) — flagging as a data point in case it's useful alongside the CI/clone-path fix proposed here, since a fix that normalizes manifest keys to relative-from-root (Option A in this issue) would very likely also close the gap we hit, if the write path that currently produces absolute keys is the same one responsible for this duplication.
@cabgab-dev thanks, this pointed at a real live gap I'd missed when I checked back in on the original symptom. Opened #3781: both
load_manifestandsave_manifest's relativize step already collapse an absolute+relative duplicate key down to one entry, but were keeping whichever raw key happened to iterate last in the on-disk JSON rather than whichever was actually current — exactly the "stale duplicate wins, under-reports changes" shape you described. Fixed by breaking the tie on each entry's ownseentimestamp instead of iteration order.Fixed in v0.9.69 (now on PyPI) by #3781 — thanks @ayushcodes10.
Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.69
Summary
manifest.jsonandcache/stat-index.jsonstore absolute file paths as keys. This means--update(incremental re-extraction) always falls back to a full rebuild when the repo is cloned to a different path — including CI environments (GitHub Actions, etc.).Reproduction
Confirmed by copying a 193-file corpus repo from
/Users/.../device-managing-portalto/private/tmp/dmp-test:Root cause
manifest.jsonkeys are absolute paths:{ "/Users/Varuna_1/git/device-managing-portal/backend/apps/__init__.py": { "mtime": 1783744253.177, "ast_hash": "d41d8cd98f00b204e9800998ecf8427e", "semantic_hash": "d41d8cd98f00b204e9800998ecf8427e" } }cache/stat-index.jsonhas the same structure — absolute path as key, content hash as value — but the hash is never used as a fallback lookup when the path key doesn't match.Impact
/home/runner/work/<repo>/— never matching a developer's local path. Every CI run does a full rebuild.graphify-out/, developer B clones and runs--update— full rebuild every time.graph.jsonitself is fine: allsource_filevalues are already relative paths. Only the incremental bookkeeping files are affected.Expected behavior
detect_incremental()should match files by relative path from the repo root (or by content hash as a fallback), not by absolute path. This would makegraphify-out/manifest.jsonportable across machines and clone paths.Suggested fix
Option A — store relative paths as manifest keys:
Option B — hash-first lookup: when an absolute path key misses, fall back to matching by
ast_hash/semantic_hashon the file content. This is more robust (survives renames too) but requires an inverted index.Option A is simpler and fixes the CI use case completely.
Environment
pip show graphifyy→ installed 2026-07-17