gabriel / muse public
fix BREAKING task/224-migration-only-mutates-on-success #1 / 1
AI Agent gabriel · 5 hours ago · Sep 18, 2026 · Diff

fix: migration must never update identity.toml on a failed hub registration (musehub#221 follow-up)

Found while risk-assessing the staging/production rollout of `muse migrate hub-scoping`, before running it against anything beyond localhost. run_migration() (both hub_scoping_migration.py and domain_migration.py, which share this design) previously mutated identity_map's fingerprint/ hd_path unconditionally on a live run, even when hub_register_fn failed. That would leave identity.toml claiming a hub-scoped key the hub never actually received -- every subsequent authenticated call to that hub (push, whoami) would then fail, and worse, the entry would no longer look "pre-scoping" to a future migration run, so there'd be no automatic way to even detect the drift, let alone repair it. Not catastrophic (the old key is always re-derivable from the same mnemonic and the exact old_hd_path is in every migration result), but an unnecessary, avoidable failure mode for a command whose entire job is keeping local and remote in sync.

run_migration() now only mutates identity_map when hub_registered is True. A failed registration leaves the entry completely untouched -- still correctly detected as needing migration on the next run.

This intersected with the *intentional* `--no-register` path, which uses a result that looks identical to a failure (hub_register_fn is never called, so hub_registered is always False) but explicitly wants local state updated anyway. Added an explicit `skip_register` parameter to both run_migration() functions rather than overloading hub_registered's meaning -- `skip_register=True` mutates unconditionally (deliberate, requested), a real failure does not (unrequested, must not silently diverge from remote). migrate_cmd.py now passes `skip_register` through explicitly instead of relying on a `MagicMock(return_value=False)` sentinel to fake a "failure" for the skip case.

Updated the two tests that asserted the old (now-wrong) "mutate even on failure" behavior to assert the entry is left untouched instead, and added explicit tests for the skip_register contract in both migration modules.

Tests: 116/116 in the hub-scoping + domain-integers files, 240/240 across the full migrate/rotate/register/hdkeys targeted set.

sha256:08c083095bcaffb4c43ce947668fac93bf5d261e13d321776170c4019dd1d77d sha
+3 ~8 −1 symbols
1195 changed · 1195 in snapshot files
sha256:6b5d4d72e835c726f06244844b88addeb44c0336536b071538000e522377b4d8 snapshot
+3
symbols added
~8
symbols modified
−1
symbol removed
1195
files changed
1195
files in snapshot
0
dead code introduced
Semantic Changes 12 symbols
~ muse/core/domain_migration.py .py 1 symbol modified
~ tests/test_migrate_domain_integers.py .py 2 symbols added, 1 symbol removed, 2 symbols modified
test_hd_path_updated_even_when_registration_fails method method test_hd_path_updated_even_when_registration_fails L816–837
+ test_hd_path_untouched_when_registration_fails method method test_hd_path_untouched_when_registration_fails L819–843
+ test_hd_path_updated_when_skip_register_explicitly_requested method method test_hd_path_updated_when_skip_register_explicitly_requested L845–871
~ tests/test_migrate_hub_scoping.py .py 1 symbol added, 2 symbols modified
+ test_skip_register_updates_local_state_without_calling_hub method method test_skip_register_updates_local_state_without_calling_hub L405–429
Files Changed
+1195
1195 in snapshot
+ .coverage .coverage
+ .museattributes .museattributes
+ .museignore .museignore
+ CLAUDE.md .md
+ README.md .md
+ dev-setup.sh .sh
+ pyproject.toml .toml
← Older Oldest on task/224-migration-only-mutates-on-success
All commits
Newer → Latest on task/224-migration-only-mutates-on-success

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:08c083095bcaffb4c43ce947668fac93bf5d261e13d321776170c4019dd1d77d --body "your comment"