Skip to content

onboarding: project-bootstrap repair effect has three pre-existing edge-case gaps #531

Description

@qnbs

Summary

Found by CodeAnt AI review while extracting the project-bootstrap effect into hooks/useProjectBootstrapEffect.ts (PR #530, fixing issue #527's race). Both are confirmed pre-existing — the extraction is behavior-preserving aside from adding the isInitialLoad guard; the original inline effect in App.tsx had the identical if (!isI18nReady || isPortalActive || !project) return; guard and identical repairProjectI18nFields call, verified via direct diff against origin/main. Neither gap was introduced by #530 or by v1.28.2.

Gap 1 — bootstrap effect is permanently blocked if the translation bundle fails to load

The effect's guard requires isI18nReady to be true. If the active locale bundle fails to fetch (offline, transient network failure), isI18nReady may never become true even though t() itself still works via English fallback (per useTranslation's own bootstrap/fallback design). Consequence: on an offline or transiently-failed first start, a blank project's raw-i18n-key fields never get repaired and a brand-new project never gets seeded — the effect just never runs.

Suggested direction (not implemented, needs its own design pass): allow the fallback-translation state to satisfy this gate (i.e., gate on "translations are usable," not literally "the network fetch for the active locale bundle succeeded"), or add a retry/timeout path so a failed bundle load doesn't block bootstrap forever.

Gap 2 — repair can silently overwrite a user's intentionally-cleared title/logline

repairProjectI18nFields treats any falsy (empty-string) title or logline as "needs repair" and replaces it with the localized initialProject.title/initialProject.logline default. This doesn't distinguish "this field was never set / holds a raw i18n key" (genuinely needs repair) from "the user deliberately cleared this field" (should stay empty, that's their choice). A returning user who intentionally blanks either field could have it silently replaced with the default on the next app start.

Suggested direction (not implemented, needs its own design pass): distinguish a raw-persisted-i18n-key value (e.g. literally "initialProject.title" stored as the title string — a real corruption case) from a genuinely-empty user-cleared value, and only auto-repair the former. Possibly restrict the repair path to newly-created projects specifically, if that's a state that can be tracked, rather than running on every app start for every project.

Gap 3 — startup repair dispatches multiple separate undo-history entries instead of one atomic update

Found by CodeAnt AI. repairProjectI18nFields's result is applied via up to three separate dispatch() calls (updateTitle, updateLogline, setManuscript), each landing as its own entry in project's redux-undo history (100-step history, per this repo's own architecture docs) instead of one atomic change. A user hitting Undo once after a startup repair only partially reverts it (e.g. undoes the seeded manuscript but leaves the repaired title/logline in place), which is confusing. Also pre-existing and byte-identical to the original App.tsx effect — verified via diff.

Suggested direction (not implemented, needs its own design pass): add a single reducer/action that atomically applies a ProjectI18nRepair payload (whichever of title/logline/manuscript are present) in one dispatch, replacing the three separate calls. resetProject is not a drop-in replacement — it also resets characters/worlds/binderNodes, which a repair must not touch.

Release relevance

Non-blocking, pre-existing on main before v1.28.2 and before #530. Not fixed here — #530 is scoped narrowly to closing the isPortalActive/isInitialLoad race (per explicit review-loop guidance to keep that fix minimal and not expand into a broader onboarding refactor). Tracked here for its own bounded design + fix pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions