fix(bags): rebuild the category cache until the profile can be trusted - #1227
Open
dfrisone wants to merge 1 commit into
Open
fix(bags): rebuild the category cache until the profile can be trusted#1227dfrisone wants to merge 1 commit into
dfrisone wants to merge 1 commit into
Conversation
Reported by @ercarp on 8.7.3: in the STANDALONE bag addon, custom categories vanish on every /reload or relog. The category list is a DERIVATION of db.profile: built once into CategoryManager._categories, rebuilt by GetCategories() only when that is nil, and read BACK by SaveState() to write the profile. The cache is therefore the source of truth for the next save, which is why categories die while sliders live -- sliders read db.profile fresh every time. EllesmereUI_Lite already documents why only standalone is affected: a file-scope NewDB there runs before the owning addon's SavedVariables load, so the db is built inside an orphan table and re-rooted in place at ADDON_LOADED. In the suite child files execute long after the parent's SVs are live. Nothing told consumers about that re-root. Bags caches a list derived from the orphan (defaults-only) profile, and the first SaveState() after any category action stamps that snapshot over the user's real data. BP() falls back to an empty table, so the early build fails silently rather than erroring, which is why it looks like the categories were never written rather than overwritten. Fix: * Lite exposes EUILite.IsDBReady() -- unconditionally true in the suite, true in standalone once the owning addon's SavedVariables are live. * InitCategories records whether the build saw a trustworthy profile, and GetCategories rebuilds until one did. * SaveState refuses to write while the db is not ready, so a future timing regression degrades to a no-op instead of data loss. PULL, not push. The first version of this registered a Lite callback from the categories file at file scope to drop the cache on re-root. That works in the suite, where Lite is guaranteed loaded first, but in a standalone build everything is one addon and that ordering is exactly what I cannot verify -- the same assumption that caused the bug. Reading readiness at GetCategories time has no such dependency, because by the time anything reads categories the framework is certainly present. A missing framework counts as not-ready, so the answer can only improve. Deliberately NOT done: moving the file-scope InitCategories() into a PLAYER_LOGIN handler. GetCategories() self-initialises, so any early caller re-caches the stale list; relocating the explicit call would move the bug rather than remove it, and would put the category module's lifecycle in the options file.
dfrisone
force-pushed
the
bags-category-reroot-cache
branch
from
August 6, 2026 20:28
03ae5c6 to
4914646
Compare
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.
The report
Reported by @ercarp on 8.7.3: in the standalone bag addon, custom categories vanish on every
/reloador relog. Simple settings like scale and columns are unaffected.Cause
The category list is a derivation of
db.profile, not a view of it:InitCategories()builds it intoCategoryManager._categoriesGetCategories()rebuilds only when that is nilSaveState()reads it back and writes it to the profileSo the cache is the source of truth for the next save. That is why categories die while sliders live: sliders read
db.profilefresh every time, categories do not.EllesmereUI_Litealready documents why only standalone is affected:The db is built inside an orphan table and re-rooted in place at
ADDON_LOADED. Nothing told consumers that had happened. Bags caches a list derived from the orphan (defaults-only) profile, and the firstSaveState()after any category action stamps that snapshot over the user's real data.BP()falls back to an empty table, so the early build fails silently instead of erroring. That is why it presents as "categories were never saved" rather than "categories were overwritten".Fix
EUILite.IsDBReady(): unconditionally true in the suite, true in standalone once the owning addon's SavedVariables are live.InitCategoriesrecords whether the build saw a trustworthy profile, andGetCategoriesrebuilds until one did.SaveStaterefuses to write while the db is not ready, so a future timing regression degrades to a no-op rather than data loss.Inert in the suite by construction:
IsDBReady()is unconditionally true there, so the rebuild condition is satisfied on the first build and the save guard never trips.Pull, not push
Worth flagging for review because I changed approach after self-review.
The first version registered a Lite callback from the categories file at file scope, to drop the cache when the db was re-rooted. That works in the suite, where Lite is guaranteed to load first. But in a standalone build everything is one addon, and that ordering is precisely what I cannot verify — it is the same assumption that caused the bug in the first place. If the categories file happened to load before the framework, the registration would silently no-op and the fix would do nothing, in the only packaging that needs it.
Reading readiness at
GetCategories()time has no such dependency: by the time anything reads categories, the framework is certainly present. A missing framework counts as not-ready, so the answer can only improve.What I deliberately did not do
The reporter also proposed a fix: remove the file-scope
InitCategories()and call it unconditionally fromEUI_Bags_Options.lua'sPLAYER_LOGIN. His diagnosis was correct and is what put me on the right track, but I did not take that shape.GetCategories()self-initialises, so removing the explicit call does not remove the hazard, it hands it to whichever caller touches categories first. And it would make the options file responsible for the category module's lifecycle, which is the same class of ordering assumption that caused the bug.