repo-init and sync 'content commit' use non-hash_commit ids — likely already unclonable
Summary
Discovered while implementing #195: at least two MuseHub write sites create MusehubCommit rows whose commit_id is not derived from hash_commit (the canonical formula the muse CLI client uses to verify every incoming commit's identity, unconditionally, on receipt):
musehub/services/musehub_repository.py— the wizard's "initialize repo" empty first commit usesblob_id(f"init:{repo_id}".encode()).musehub/services/musehub_sync.py— the synthetic "content commit" path usesblob_id(f"{repo_id}\x00{branch}\x00{snap_id}\x00{message}\x00{now.isoformat()}".encode()).
Why this matters
Confirmed directly (not just by inspection): recomputing either commit's id via the standard hash_commit/compute_commit_id formula from its own stored fields never matches the id actually stored. The muse CLI's _verify_commit_id (muse/core/commits.py) calls this formula unconditionally for every commit it receives, with no special-casing for these — meaning if a client ever fetches/clones one of these commits over the wire, its own integrity check should already reject it today, independent of anything in #195.
This was surfaced concretely while testing #195's fix: an attempt to add an automatic "verify a commit's wire bytes actually reproduce its id" check to the general serve path immediately broke on both of these commit shapes.
Suggested investigation
- Confirm empirically whether a fresh
initialize=Truerepo, or a repo touched by the sync "content commit" path, actually fails to clone today (does the client really reject these on receipt, or is there some other special-casing / do these commits simply never get fetched in practice because they're immediately superseded by real pushes?). - If confirmed broken: decide whether these two write sites should adopt real
hash_commit-derived ids (bringing them in line with every other commit-creation path), or whether they need a deliberately different, documented identity scheme with client-side awareness of it. - Either way, document the intended id scheme for "synthetic" (non-client-authored) commits — right now it's implicit and inconsistent between these two sites and everywhere else.
Related
- #195 — where this was found (committed_at_raw / commit identity work).
- #63 — the broader "commits should be canonical bytes, not DB-reconstructed" structural ticket; a canonical-bytes model would likely make this class of bug impossible by construction.
Source
Found by Claude Sonnet 5 while implementing #195, 2026-09-11.