# PROJECT: IDAA `idaa_loc` Migration to Svelte 5 `PersistedState` ## Objective Migrate IDAA persisted local state from legacy `svelte-persisted-store` (`$idaa_loc`) to Svelte 5 `PersistedState` (`idaa_loc.current`) without behavior regressions, auth leaks, or broken page flows. Primary target store: - `src/lib/stores/ae_idaa_stores__idaa_loc.svelte.ts` ← new store (created, not yet wired in) Legacy source currently used by routes: - `src/lib/stores/ae_idaa_stores.ts` ← remove `idaa_loc` export after migration ## Why This Matters - Removes coarse-grained reactivity side effects from legacy persisted store access. - Aligns IDAA with the completed Events store migration pattern. - Reduces risk of auth-state corruption from broad re-renders triggered by unrelated writes to the same store key. ## Scope In scope: - Replace all `idaa_loc` imports from `ae_idaa_stores.ts` with imports from `ae_idaa_stores__idaa_loc.svelte.ts`. - Replace all `$idaa_loc.*` reads/writes with `idaa_loc.current.*`. - Remove `idaa_loc` export and its `persisted()` definition from `ae_idaa_stores.ts` after all consumers are migrated. - Keep `store_versions.ts` wipe call for `ae_idaa_loc` — this cleans old data from browsers of returning users. Keep it for at least one year post-migration (same rule as `ae_events_loc`). Out of scope: - Migrating `idaa_sess`, `idaa_slct`, `idaa_trig`, `idaa_prom` — in-memory writables, no coarse-reactivity problem. - Backend/API changes. - `ae_loc` migration (separate project). ## Consumer File Inventory ### Files requiring import + `$idaa_loc` → `idaa_loc.current` changes (29 files) #### IDAA module root layouts (auth-critical — do these first and last) | File | `$idaa_loc` hits | Notes | | --- | --- | --- | | `src/routes/idaa/+layout.svelte` | 3 | Top-level IDAA layout; `/idaa/clear-caches` lives outside this | | `src/routes/idaa/(idaa)/+layout.svelte` | 40 | Most complex. Novi verification loop, auth escalation, admin/trusted list writes | #### Bulletin Board (BB) | File | `$idaa_loc` hits | | --- | --- | | `src/routes/idaa/(idaa)/bb/+layout.svelte` | 3 | | `src/routes/idaa/(idaa)/bb/+page.svelte` | 11 | | `src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte` | 6 | | `src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_li.svelte` | 4 | | `src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_edit.svelte` | 11 | | `src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_view.svelte` | low | | `src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_comment_obj_id_edit.svelte` | 7 | | `src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_options.svelte` | 18 | #### Archives | File | `$idaa_loc` hits | | --- | --- | | `src/routes/idaa/(idaa)/archives/+layout.svelte` | low | | `src/routes/idaa/(idaa)/archives/+page.svelte` | 6 | | `src/routes/idaa/(idaa)/archives/ae_idaa_comp__media_player.svelte` | low | | `src/routes/idaa/(idaa)/archives/[archive_id]/+page.svelte` | 11 | | `src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_obj_id_edit.svelte` | 4 | | `src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_obj_id_view.svelte` | 16 | | `src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte` | 4 | | `src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_li.svelte` | low | | `src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__modal_media_player.svelte` | low | #### Recovery Meetings | File | `$idaa_loc` hits | | --- | --- | | `src/routes/idaa/(idaa)/recovery_meetings/+layout.svelte` | low | | `src/routes/idaa/(idaa)/recovery_meetings/+page.svelte` | 37 | | `src/routes/idaa/(idaa)/recovery_meetings/[event_id]/+page.svelte` | 10 | | `src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte` | 41 | | `src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte` | 8 | | `src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte` | 12 | | `src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_view.svelte` | low | #### Other IDAA | File | `$idaa_loc` hits | | --- | --- | | `src/routes/idaa/(idaa)/jitsi_reports/+page.svelte` | low | | `src/routes/idaa/(idaa)/video_conferences/+page.svelte` | 12 | ### Files that reference `ae_idaa_loc` as a raw string only — NO changes needed - `src/routes/events/+layout.svelte` — `localStorage.removeItem('ae_idaa_loc')` (sign-out) - `src/routes/events/[event_id]/(launcher)/cfg_components/launcher_cfg_local_actions.svelte` — same - `src/lib/stores/store_versions.ts` — `_check_and_wipe('ae_idaa_loc', ...)` — keep as-is ## Safety Rules 1. Do this as one atomic migration — no split old/new `idaa_loc` consumers in the same session. 2. Do not loosen any IDAA auth gating — IDAA is private data, always authenticated. 3. Do not move IDAA private data loads into `+page.ts` / `+layout.ts` where prefetch can run. 4. Keep route behavior and permissions exactly as current production behavior. 5. The `(idaa)/+layout.svelte` auth gate is the most sensitive file. Review it by hand after the mechanical pass — do not rely solely on `svelte-check` to catch auth logic regressions. ## Key Syntax Changes | Before | After | | --- | --- | | `import { idaa_loc } from '$lib/stores/ae_idaa_stores'` | `import { idaa_loc } from '$lib/stores/ae_idaa_stores__idaa_loc.svelte'` | | `$idaa_loc.novi_uuid` | `idaa_loc.current.novi_uuid` | | `$idaa_loc.bb.qry__hidden = 'not_hidden'` | `idaa_loc.current.bb.qry__hidden = 'not_hidden'` | | `$idaa_loc.recovery_meetings.qry__favorites_only` | `idaa_loc.current.recovery_meetings.qry__favorites_only` | Notes: - Keep other imports from `ae_idaa_stores` (`idaa_sess`, `idaa_slct`, `idaa_trig`, `idaa_prom`) unchanged — they stay in that file. - No `$` sigil — access via `.current` property, not Svelte store subscription. ## Execution Plan ### Phase 0: Baseline Checkpoint - Ensure clean compile baseline: `npx svelte-check` → 0/0. - Confirm new store file committed: `ae_idaa_stores__idaa_loc.svelte.ts` ✅ (done 2026-06-11) Exit criteria: - `svelte-check` 0 errors / 0 warnings. ### Phase 1: Store Consumer Conversion (Mechanical) Recommended order — least to most auth-critical, so layouts are done fresh at the end: #### 1a. Sub-module components (BB, Archives, Recovery Meetings) All `ae_idaa_comp__*` component files — pure consumers, no auth logic. #### 1b. Sub-module pages and layouts (BB, Archives, Recovery Meetings) Page and layout files for the three sub-modules. Sub-layouts (`bb/+layout.svelte`, `archives/+layout.svelte`, `recovery_meetings/+layout.svelte`) check auth indirectly but don't own the Novi verification loop. #### 1c. Jitsi reports + video conferences Low-hit, isolated pages. #### 1d. `src/routes/idaa/+layout.svelte` (top-level) Reads `$idaa_loc` for display only; writes happen in the (idaa) inner layout. #### 1e. `src/routes/idaa/(idaa)/+layout.svelte` (innermost — do last) Most complex file (40 hits). Owns the Novi verification loop and all auth escalation writes. The `$idaa_loc.bb.qry__hidden`, `$idaa_loc.novi_admin_li`, etc. writes all live here. Review by hand after mechanical pass. For each file: 1. Update import — change `idaa_loc` source, keep `idaa_sess` / `idaa_slct` / etc. from `ae_idaa_stores`. 2. Replace `$idaa_loc.` → `idaa_loc.current.` (global find-replace within file). 3. No other logic changes — mechanical pass only. Exit criteria: - No remaining `$idaa_loc.` usages in `src/routes/idaa/`. - No `idaa_loc` imports pointing to `ae_idaa_stores` in route files. ### Phase 2: Store File Cleanup After all consumers are migrated: - Remove `idaa_loc` export and `persisted('ae_idaa_loc', idaa_local_data_struct)` from `ae_idaa_stores.ts`. - Remove `AE_IDAA_LOC_VERSION` import from `ae_idaa_stores.ts` (no longer needed there). - Keep `store_versions.ts` unchanged — the `_check_and_wipe` call stays to clean old data. ### Phase 3: Critical Auth Layout Validation Manually review after conversion: - `src/routes/idaa/(idaa)/+layout.svelte` — Novi verification `$effect`, auth escalation, sign-out - `src/routes/idaa/+layout.svelte` — top-level auth gate template - `src/routes/idaa/(idaa)/bb/+layout.svelte` - `src/routes/idaa/(idaa)/archives/+layout.svelte` - `src/routes/idaa/(idaa)/recovery_meetings/+layout.svelte` Checks: - Auth gate still blocks unauthenticated users on all sub-routes. - `novi_verified`, `novi_uuid`, trusted/admin flag writes work correctly. - No duplicate or skipped verification loops. - `bb.qry__hidden`, `bb.qry__enabled` reset after Novi verification still fires. Exit criteria: - Auth flow matches current production behavior exactly. ### Phase 4: Compile + Search Guards Run: ```bash npx svelte-check grep -rn '\$idaa_loc\.' src/ grep -rn "from '\$lib/stores/ae_idaa_stores'" src/routes/idaa/ ``` Exit criteria: - `svelte-check`: 0 errors / 0 warnings. - No `$idaa_loc.` references remaining in source. - No `idaa_loc` imports from `ae_idaa_stores` in route files. ### Phase 5: Test File Updates The IDAA Novi auth test (`tests/idaa_novi_auth.test.ts`) seeds `ae_idaa_loc` via `addInitScript`. After migration: - The seeded structure remains valid (same key, same shape). - Remove any `ver:` field from the seed if present — `PersistedState` stores don't use it. - Verify the full nested structure is still seeded (the `bb`, `archives`, `recovery_meetings` objects must be present — see the "Seed the Full ae_idaa_loc Structure" lesson in `tests/README.md`). ### Phase 6: Runtime Smoke Test Test flows: 1. Direct navigation to `/idaa/` — auth gate behavior correct. 2. Bulletin Board: list, post view, post edit, comment. 3. Archives: list, archive detail, media player. 4. Recovery Meetings: list, search/filter, favorites toggle, edit form. 5. Video Conferences page loads. 6. Jitsi Reports page loads. 7. Cache clear page (`/idaa/clear-caches`) still clears state and posts message to parent. 8. Sign-out clears `ae_idaa_loc` (the localStorage key name is unchanged, so this works automatically for any caller using `localStorage.removeItem('ae_idaa_loc')`). Exit criteria: - No regressions in primary user paths. ## Risk Register ### R1: Split-brain state **Risk:** Mixed old/new `idaa_loc` consumers in the same session can lead to inconsistent state. **Mitigation:** Convert all consumers in one agent pass before committing. Do not commit partial migrations. ### R2: Auth regression in `(idaa)/+layout.svelte` **Risk:** The Novi verification loop is complex (40 `$idaa_loc` hits). A subtle change to `$effect` dependency tracking between old and new store access could break or skip verification. **Mitigation:** Review this file by hand after the mechanical pass. Test auth flow explicitly. ### R3: Nested object merge gap **Risk:** The `deserialize` function does a shallow spread at the top level only: `{ ...idaa_loc_defaults, ...JSON.parse(raw) }`. If a new field is added inside `bb`, `archives`, or `recovery_meetings` after a user has stored data, that field will get `undefined` rather than its default. **Mitigation:** This is the same accepted trade-off as the events sub-stores. If a new nested field is added in the future, add a migration step or accept that the old stored value takes over wholesale. ### R4: Mechanical typo / missed reference **Risk:** High replacement count (29 files, ~300+ hits) introduces missed `$idaa_loc.` references. **Mitigation:** Run the grep guard in Phase 4 before declaring done. ## Rollback Plan If issues are found after migration: 1. `git revert` the migration commit(s). 2. Re-run `npx svelte-check`. 3. Re-attempt using smaller batches per sub-module (BB only, then Archives, then Recovery Meetings). ## Deliverables - All 29 consumer files converted from `$idaa_loc.*` → `idaa_loc.current.*`. - `idaa_loc` export removed from `ae_idaa_stores.ts`. - Passing compile check (0/0). - Smoke-tested all IDAA sub-modules. ## Definition of Done - Full `idaa_loc` migration complete. - No auth/privacy regressions. - `svelte-check` 0/0. - IDAA smoke-tested (auth gate, BB, Archives, Recovery Meetings, cache clear).