Fix two latent bugs: merge-chunks output and manifest data loss

1. graphify merge-chunks dumped the entire node list to the terminal
   instead of the node count. __main__.py concatenated merged['nodes']
   (a list of dicts) into an f-string where it clearly meant
   len(merged['nodes']) -- the other two values in the same line use
   len() correctly.

2. global_graph._load_manifest silently returned a fresh empty manifest
   on any JSON parse error. That is reachable through normal interrupted
   writes (the manifest is rewritten in full on every global_add /
   global_remove, with no fsync or atomic rename), and the failure mode
   is total data loss: every tracked repo disappears from
   ~/.graphify/global-manifest.json on the next read.

   Back the corrupt file up to <path>.corrupt.<unix_ts> and print to
   stderr before returning the empty default. Users can then recover
   manually and the failure is visible rather than silent.
This commit is contained in:
nucleusjay
2026-06-09 03:36:19 -04:00
parent 137bc2d951
commit 524289fb66
2 changed files with 22 additions and 3 deletions
+1 -1
View File
@@ -4520,7 +4520,7 @@ def main() -> None:
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(merged, ensure_ascii=False), encoding="utf-8")
print(
f"Merged {len(chunk_files)} chunks: {merged['nodes']} nodes, {len(merged['edges'])} edges, "
f"Merged {len(chunk_files)} chunks: {len(merged['nodes'])} nodes, {len(merged['edges'])} edges, "
f"{merged['input_tokens']:,} in / {merged['output_tokens']:,} out tokens"
)
+21 -2
View File
@@ -16,8 +16,27 @@ def _load_manifest() -> dict:
if _GLOBAL_MANIFEST.exists():
try:
return json.loads(_GLOBAL_MANIFEST.read_text(encoding="utf-8"))
except Exception:
pass
except Exception as exc:
# Don't silently wipe the user's manifest on a parse error: that
# deletes every tracked repo. Back the bad file up and surface the
# error so the user can recover or report it.
backup = _GLOBAL_MANIFEST.with_suffix(
_GLOBAL_MANIFEST.suffix + f".corrupt.{int(datetime.now(timezone.utc).timestamp())}"
)
try:
_GLOBAL_MANIFEST.rename(backup)
print(
f"[graphify global] manifest at {_GLOBAL_MANIFEST} failed to parse ({exc}); "
f"moved to {backup} and starting fresh. Restore from the backup if this was "
f"unexpected.",
file=sys.stderr,
)
except Exception as rename_exc:
print(
f"[graphify global] manifest at {_GLOBAL_MANIFEST} failed to parse ({exc}) "
f"and could not be backed up ({rename_exc}). Starting fresh.",
file=sys.stderr,
)
return {"version": 1, "repos": {}}