docs: update IDAA idaa_loc migration project doc
Rewrote PROJECT__IDAA_Stores_Svelte5_Migration_2026.md with complete detail: full 29-file consumer inventory with hit counts per module, exact files that only reference the localStorage key string (no changes needed), store_versions.ts wipe note, explicit Phase 1 ordering, test seed guidance, and updated Risk Register including R3 nested-object merge gap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
265
documentation/PROJECT__IDAA_Stores_Svelte5_Migration_2026.md
Normal file
265
documentation/PROJECT__IDAA_Stores_Svelte5_Migration_2026.md
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
# 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).
|
||||||
Reference in New Issue
Block a user