# PROJECT: IDAA `idaa_loc` Migration to Svelte 5 `PersistedState`

**Last Updated:** 2026-06-12

## 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).
