gabriel / musehub public
fix BREAKING phase8 task/issue-7-phase-8 #1 / 1
AI Agent gabriel · 149 days ago · Apr 27, 2026 · Diff

fix(phase8): atomic _write() + ingest_push parent validation with regression tests

Gap 1 — ingest_push() parent existence validation - New commits in a push whose parent_id is neither in the bundle nor already in the DB are now rejected with ValueError('missing_parent_commits: …'). - Mirrors the same guard in wire_push_stream() (musehub_wire.py) so both ingest paths are equally safe. - 4 new tests in test_data_integrity.py cover: bogus parent rejected, parent-in-bundle accepted, parent-already-in-DB accepted, genesis accepted.

Gap 2 — LocalBackend._write() atomic crash safety - Replaced path.write_bytes(data) with mkstemp → os.write → os.fsync → os.close → os.replace. Readers see either the old file or the fully-written new file — never a partial write. - Temp file is unlinked on any failure (fsync error, replace error). - Final chmod 0o444 makes objects immutable after storage. - 7 new tests in TestAtomicWrite cover: complete file left, no orphan temp, immutable permissions, fsync-failure cleanup, idempotent skip, repair- corrupted, concurrent safety.

sha256:403c3d613fb4c6c7b0730424673516368301bb1ccad7d6c71fe403ec14d31a8b sha
+12 ~3 symbols
sha256:e7929482ef5dc45339eace4d1885db8fed57530838ce03fa6746d6591488d195 snapshot
+12
symbols added
~3
symbols modified
0
dead code introduced
Semantic Changes 15 symbols
~ musehub/services/musehub_sync.py .py 1 symbol modified
~ musehub/storage/backends.py .py 2 symbols modified
~ tests/test_data_integrity.py .py 4 symbols added
+ test_ingest_push_accepts_parent_already_in_db function async_function test_ingest_push_accepts_parent_already_in_db L214–275
+ test_ingest_push_accepts_parent_in_same_bundle function async_function test_ingest_push_accepts_parent_in_same_bundle L163–211
+ test_ingest_push_genesis_commit_no_parent_accepted function async_function test_ingest_push_genesis_commit_no_parent_accepted L278–312
+ test_ingest_push_rejects_missing_external_parent function async_function test_ingest_push_rejects_missing_external_parent L117–160
~ tests/test_storage_backends.py .py 8 symbols added
+ TestAtomicWrite class class TestAtomicWrite L636–722
+ test_write_concurrent_same_object_safe method async_method test_write_concurrent_same_object_safe L715–722
+ test_write_file_is_immutable_after_put method async_method test_write_file_is_immutable_after_put L656–663
+ test_write_idempotent_identical_bytes_skips_io method async_method test_write_idempotent_identical_bytes_skips_io L686–697
+ test_write_leaves_complete_file method async_method test_write_leaves_complete_file L639–646
+ test_write_no_tmp_file_remains_after_success method async_method test_write_no_tmp_file_remains_after_success L648–654
+ test_write_repairs_corrupted_file_atomically method async_method test_write_repairs_corrupted_file_atomically L699–713
+ test_write_tmp_file_cleaned_up_on_fsync_failure method async_method test_write_tmp_file_cleaned_up_on_fsync_failure L665–684
← Older Oldest on task/issue-7-phase-8
All commits
Newer → Latest on task/issue-7-phase-8

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:403c3d613fb4c6c7b0730424673516368301bb1ccad7d6c71fe403ec14d31a8b --body "your comment"