fix: eliminate ghost objects — confirm guard, decompress bugs, repair script
Root cause investigation 2026-05-08: three bugs created phantom DB rows whose bytes were absent from storage, causing push/stream Phase-C rejections.
Layer 1 — decompress_objects.py (deploy/decompress_objects.py): Bug 1: bare_oid was only assigned inside the hash-mismatch branch but used on the success path → NameError silently caught → no object ever actually decompressed. Bug 2: even if defined, bare_oid is bare hex, so backend.put(bare_oid, …) stored at the wrong R2 key (objects/<hex> vs objects/sha256:<hex>). Fix: split_id(oid) moved above the hash check; backend.put uses canonical oid.
Layer 2 — wire_push_confirm (musehub/services/musehub_wire.py): Bug: confirm registered DB rows without verifying the client's R2 PUT succeeded. A silent PUT failure (expired presigned URL, network blip) left a DB row with no bytes → ghost. Fix: backend.exists(oid, repo_root=…) gate added; absent objects are rejected (logged + counted); returns confirmed/rejected counts.
Layer 3 — Phase-C distinction (confirmed correct, tested): P3.1 truly-missing → 'not in push bundle or storage' P3.2 ghost (DB row, no bytes) → 'found in DB but missing from storage' P3.3 inline bundle object → passes without DB pre-existence
Layer 4 — repair_objects.py (deploy/repair_objects.py): Added repo join (MusehubObjectRef → MusehubRepo) so LocalBackend gets owner/slug for repo_root resolution; no-op for S3/R2. Operational: docker exec musehub-blue python3 /app/deploy/repair_objects.py --dry-run
22 new tests across three files: D1-D7, C1-C7, P3.1-P3.3, R4.1-R4.5.
0 comments
muse hub commit comment sha256:f1534b4412ad996d45cc3fbbc13fe61e6595f511ef02a9ccfa38ea9adb9c9bc2 --body "your comment"
No comments yet. Be the first to start the discussion.