Skip to content

Restore displaced and current models when a model load fails - #257

Merged
fumitoh merged 1 commit into
mainfrom
claude/exciting-golick-e94327
Jul 19, 2026
Merged

Restore displaced and current models when a model load fails#257
fumitoh merged 1 commit into
mainfrom
claude/exciting-golick-e94327

Conversation

@fumitoh

@fumitoh fumitoh commented Jul 19, 2026

Copy link
Copy Markdown
Owner

What changed

ModelReader.read_model (serializers 4-7) now restores the system's pre-existing models when a load fails partway:

  • Displaced model gets its name back. RenameParser runs at AT_PARSE and renames the half-built model to its saved name immediately during parsing, displacing a same-named existing model to <name>_BAK<n>. Previously, a failed read closed only the half-built model, leaving the user's model stranded under the backup name with nothing under the original name.
  • The previously current model becomes current again. parse_dir's mx.new_model() makes the half-built model current, and close_model nulls currentmodel instead of restoring the prior one — so after any failed read, mx.cur_model() was None and the next current-model-dependent call (e.g. mx.defcells, mx.new_space) silently created a brand-new model.

The fix is shared: a new modelx/serialize/reader_state.py defines SystemStateSnapshot, which records system.models and system.currentmodel before each read; the except paths of serializer 4/5/6 (7 inherits 6) call state.restore() after closing the half-built model and rolling back the IOManager journal (#256). Restoration is guarded by identity checks so it only renames back a model this read displaced, and only when the original name is free.

Successful reads are unchanged: the displaced model intentionally keeps its _BAK name (covered by the existing test_restore_model_leave_old).

Tests

New modelx/tests/serialize/test_read_error_recovery.py (16 tests, versions 4-7). The failure is produced without mocks by truncating a space file mid-statement in a copy of the compat fixtures, so the load aborts after the displacing rename — the worst-case failure point. Scenarios: displaced model restored by identity under its original name, prior cur_model() restored, no-prior-model reads left untouched, and the success path still displacing to _BAK.

Without the fix, 8 of the 16 fail (both defects, all four versions); with it, the full suite is green (1144 passed, 6 skipped).

Notes for review

🤖 Generated with Claude Code

ModelReader.read_model for serializers 4-7 builds the new model in the
live system: parse_dir creates a model (making it current) and
RenameParser renames it to its saved name immediately during parsing,
displacing a same-named existing model to <name>_BAK<n>. On a failed
read, only the half-built model was closed: the user's model stayed
stranded under its backup name, and cur_model() was left None even
though the previously current model still existed, so the next
current-model-dependent API call silently created a brand-new model.

Snapshot the system's models and current model before reading and
restore both in the except path after closing the half-built model
(modelx/serialize/reader_state.py, shared by serializers 4-6;
serializer 7 inherits 6). Successful reads are unchanged: the
displaced model intentionally keeps its backup name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fumitoh
fumitoh merged commit 174de6c into main Jul 19, 2026
18 checks passed
@fumitoh
fumitoh deleted the claude/exciting-golick-e94327 branch July 19, 2026 16:07
@fumitoh fumitoh mentioned this pull request Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant