fix: Recover profiles left with no framework - #28
Merged
Conversation
Two ways a profile could end up with an empty `framework`, and no way out of either in basic mode, where no framework control is rendered at all -- the Frameworks nav is filtered out, /frameworks redirects to /profiles, and the profile dropdown is gated on advanced_mode. The launch then fails with "No framework assigned. Assign a framework to this profile.", telling the user to do something the UI does not let them do. The only repair was accidental: opening the editor fires the auto-assign effect, so saving an untouched profile writes the field back. FrameworkBootstrap now adopts framework-less profiles whenever there is nothing to choose -- no frameworks yet, or exactly one -- because the assignment it would make is the only one the user could make by hand. With two or more it still declines, but says so instead of returning silently: that empty value is the deliberate post-delete state and guessing could launch a profile against the wrong game directory. ProfileForm also shows the dropdown, in either mode, when the saved profile has no framework. The other way in is a handoff. The successor signals Adopted at the top of Main and only then runs Migration and FrameworkBootstrap, so the predecessor stays alive and message-driven right through the migration. Every repository save rewrites its whole file from an in-memory list the OLD schema parsed, so a single run counter arriving in that window drops every field the successor just added -- and frameworks.json survives, so the bootstrap never retried. DataWriteGate closes before the successor is spawned: from then on the data directory belongs to it and this process only reads. It reopens only where no successor can still be migrating (it exited without signalling, or never started); one that is alive but silent leaves the gate closed, since read-only beats two writers on one directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two ways a profile could end up with an empty
framework, and no way out of either in basic mode, where no framework control is rendered at all -- the Frameworks nav is filtered out, /frameworks redirects to /profiles, and the profile dropdown is gated on advanced_mode. The launch then fails with "No framework assigned. Assign a framework to this profile.", telling the user to do something the UI does not let them do. The only repair was accidental: opening the editor fires the auto-assign effect, so saving an untouched profile writes the field back.FrameworkBootstrap now adopts framework-less profiles whenever there is nothing to choose -- no frameworks yet, or exactly one -- because the assignment it would make is the only one the user could make by hand. With two or more it still declines, but says so instead of returning silently: that empty value is the deliberate post-delete state and guessing could launch a profile against the wrong game directory. ProfileForm also shows the dropdown, in either mode, when the saved profile has no framework.
The other way in is a handoff. The successor signals Adopted at the top of Main and only then runs Migration and FrameworkBootstrap, so the predecessor stays alive and message-driven right through the migration. Every repository save rewrites its whole file from an in-memory list the OLD schema parsed, so a single run counter arriving in that window drops every field the successor just added -- and frameworks.json survives, so the bootstrap never retried. DataWriteGate closes before the successor is spawned: from then on the data directory belongs to it and this process only reads. It reopens only where no successor can still be migrating (it exited without signalling, or never started); one that is alive but silent leaves the gate closed, since read-only beats two writers on one directory.