Dashboard: Users + People cards corrected to tertiary (matched menu);
Files card added between People and Data Stores using secondary (matched menu).
Files per-page options extended to 200/400/800/1000/1500/2000.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When orphan filter is active, total was summing all 50 loaded files
rather than the filtered subset shown in the table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DELETE /v3/action/event_file/{id} now handles full atomic cleanup (link
removal, physical file, hosted_file record) in one call — replaces the
multi-step Redis pre-warm workaround. orphan_scan endpoint replaces the
N+1 per-file /links fetch on the admin files page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Event file delete (Pres Mgmt):
- Re-implement cleanup using /links pre-fetch before delete_hosted_file calls.
The /links endpoint calls get_id_random() per link which populates Redis.
Without this, redis_lookup_id_random('event_file', id) raises 404 in the
delete handler → silent skip → physical file never removed.
Now mirrors the same pattern used by the /core/files/ admin page delete.
/core/files/ admin page:
- Add orphan check mode: "Check Orphans" button batch-fetches links for all
visible results in parallel (reusing links_map cache), then filters table
to show only files with zero links.
- Orphan files get a warning badge in the filename column.
- Results header toggles to show "N orphans of M" when filter is active.
- Unlink icon imported from lucide for orphan UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
event_file delete was broken since the module was first created (Oct 2024).
delete_ae_obj_id__event_file passed delete_hosted_file=true + rm_orphan=true to
the generic V3 CRUD endpoint, but delete_obj_template never handled those params —
it only did a raw sql_delete on the event_file row. The hosted_file_link record,
hosted_file DB record, and physical file on disk were never cleaned up.
Fix: call api.delete_hosted_file (the action endpoint) BEFORE deleting the
event_file record so the backend can still resolve link_to_id via Redis.
Pass link_to_type='event_file', link_to_id=event_file_id, rm_orphan=true,
method=delete. hosted_file_id is now an optional param on
delete_ae_obj_id__event_file; component passes event_file_obj.hosted_file_id.
Also fix hosted_file delete in /core/files/ admin page (same root cause):
load links first, then call delete_hosted_file for each link with correct
link_to_type/link_to_id_random and method=delete before removing the record.
Also: add clickable navigation links in the files admin link sub-row.
Direct types (event, journal, archive, post) resolve immediately; nested types
(event_session, event_location, event_presenter, journal_entry, etc.) fetch
their parent ID via the V3 CRUD endpoint and construct the correct route.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Search hosted files across all accounts (including disabled)
- Sortable columns, pagination, per-row delete, download, and SHA-256 copy
- Lazy-load file link records per row via /v3/action/hosted_file/{id}/links
- Fix delete to load links first, remove each via correct link_to_type/link_to_id_random,
then hard-delete with method=delete and rm_orphan=true
- Remove Linked To and Group columns (moved Group/ForType to filter bar only)
- SHA-256 column now visible at lg breakpoint (was xl)
- Added /core/files nav link to /core layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passcodes are no longer compared locally against cached localStorage data.
Entry now POSTs to /v3/action/auth/authenticate_passcode; on success the
returned JWT (with per-role TTL) is stored in $ae_loc.jwt. Page-load
expiry check in +layout.ts resets access_type to anonymous when the JWT
has expired, targeting only auth_type='passcode' JWTs.
- Debounce (600 ms) auto-fires the check after typing stops; Enter key
fires immediately as a secondary trigger — preserving the original UX
- Inline spinner and error message added to the passcode input
- Silent fallback to local comparison on network error or unresolved
site_id (ghost), so IDAA staff and Electron/Launcher contexts are safe
- USE_API_PASSCODE_AUTH = true (active); local fallback retained while
production is observed; site_access_code_kv cleanup deferred
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Removed invalid two-way bindings to read-only Dexie observables in the test page
- Fixed 'state_referenced_locally' warning in Field Editor by refactoring draft_value initialization
- Cleaned up empty else block in session list to resolve Vite build warning
- Verified successful production build and clean 'npm run check' output
- Completed rewrite of `element_ae_obj_field_editor.svelte` to Svelte 5 + Tailwind v4
- Set `display_modal = true`, `modal_blocking = false`, and `modal_placement = 'center'` as new defaults
- Implemented trigger-relative modal positioning with automatic viewport boundary clamping to prevent off-screen rendering
- Migrated all 12 call sites across core and events modules (Session, Presenter, Location, Exhibit, etc.)
- Removed legacy datetime-to-local manual conversion logic from views as the component now handles it natively
- Retired Skeleton-based legacy component
- Updated testing page and documentation to reflect the new standardized primitive
- Refactored core layout and dashboard to follow AE Firefly guidelines
- Added proper theme-aware backgrounds and transitioned to Skeleton v4 tokens
- Grouped navigation and cards by functional color (Teal: Infra, Indigo: Identity, Amber: Config)
- Reordered items to: Accounts, Sites, Users, People, Data Stores, Lookups, Addresses, Contacts
- Fixed color inconsistency for Activity Logs in dashboard vs navigation
- Enabled non-blocking modal mode for inline field editors in Data Stores table
- New `display_modal` prop opens the edit panel as a native <dialog> anchored
near the pencil trigger instead of shifting inline content
- `modal_placement` (center|above|below|left|right, default center) positions
the dialog relative to the trigger via getBoundingClientRect + CSS transform
- `modal_blocking` (default true) toggles showModal() vs show(); non-modal
mode adds a document pointerdown listener to close on outside click
- `cancel_edit()` now warns "Discard unsaved changes?" when draft differs from
saved value (matches data store form behaviour); skips warning after a
successful save
- Dialog background uses theme CSS vars directly (--color-surface-50/900) via
:global CSS — Skeleton tonal presets are intentionally semi-transparent and
rendered behind table content without explicit position:fixed + z-index
- Extracted edit panel to {#snippet edit_panel()} — shared by inline and
dialog paths with no duplication
- data-stores table: all three inline field editors switched to display_modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Edit button now has SquarePen icon + text per button UX standard
- Style guidelines: extract button icon+text rule into its own section 8
(was buried in Accessibility); renumber Accessibility to section 9;
add code examples and context table for the rule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Row is no longer a giant click target. Code, Name, and Type cells now use
AE_Field_Editor for inline editing (pencil in edit_mode). Edit button opens
the full modal for everything else. Preview column hidden (kept in markup).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- order_by_li was sent as an array; backend expects plain object — fixes sort
- pass enabled/hidden directly to search_ae_obj (was defaulting to 'enabled'/'not_hidden')
- account column now shows code/short_name from Dexie cache via SvelteMap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
element_data_store_form.svelte is now the single source of truth for
editing data stores — owns the modal, all form fields, change detection,
save/delete API calls, and IDB cache update.
- element_data_store.svelte: remove ~120 lines of duplicated form/handler
code; now delegates to AE_DataStore_Form via bind:open + callbacks
- data_stores/+page.svelte: open_edit/open_new are now 2 lines each;
remove all draft state, submit_status, handle_save/delete; fix label
a11y (wrapping labels on all filter + bulk-rename inputs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add original_obj prop + has_changes bindable output to element_data_store_form
- Derive has_changes by comparing each draft field to original_obj; null = always dirty (new record)
- Wire bind:has_changes + original_obj={is_new ? null : editing_obj} in management page
- Fix Save button: preset-filled-primary → preset-filled-primary-500 (matches element_data_store)
- Disable Save when !has_changes; block modal outsideclose when changes are pending
- Restore textarea rows 10 → 15 to match element_data_store.svelte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New element_data_store_form.svelte handles all form fields for creating/
editing a Data Store. Replaces the inline form in the management page modal.
Features vs old inline form:
- Help text on every label (type descriptions, constraint notes, etc.)
- Advanced section (collapsible, hidden by default): Enable, Hide, Priority,
Sort, Group, Notes — each with hint text
- For ID: editable on new, read-only on edit (with explanation why)
- show_account_field / show_for_fields props for embedded widget use later
- html_edit_mode + show_advanced are internal state, reset via {#key} on parent
Management page: drops html_edit_mode state + Code/Eye/editor imports; uses
{#key editing_obj?.id ?? 'new'} to recreate the form on each record change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- api.ts: remove get_data_store_obj_w_code (hit /data_store/code/, non-V3)
and its export. V3 get_data_store (/v3/data_store/code/) stays.
- ae_core_functions.ts: remove load_ae_obj_code__data_store (wrapper around
the removed function) and its export from core_func.
element_data_store.svelte replaced this with Dexie LiveQuery + V3 API.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Click any column header to sort; click again to toggle ASC/DESC.
Sort re-runs the current search immediately if results are showing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Bulk Rename panel: code filter (% wildcard), find/replace text,
preview table before apply, sequential PATCH with IDB cache update
- Fix Code filter label/placeholder to show % wildcard syntax
- Add note that API results are scoped to the active account (backend behavior)
- Replace bg-black/5 with bg-surface-500/10 for light/dark compatibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full CRUD for all data_store records: search by code, account_id,
for_type, for_id; create/edit/delete via modal with type-aware content
editor (CodeMirror / TipTap / textarea). Wired into core nav and
dashboard. for_id shown read-only on edit (DB integer FK constraint).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
for_id is an integer FK in the DB but the frontend passes the random
string ID — sending it on update causes a 400. These fields are
parent-context set at creation time and should never change on edit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes in presenter_view:
1. Name-sync warning (given/family mismatch) was showing even when no
Person was linked. Guard now checks person_id first.
2. Person link replaced the two-step Re-link button flow with on_open
pattern (matches Session POC). Pencil+Re-link button removed; pencil
icon opens the editor and loads the person list in one action.
Admin-only re-link restriction preserved: non-admin staff with a
linked person see a read-only link instead of the editor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v_event_presenter and v_event_presentation both use INNER JOIN with event
on event_id. Without event_id in the payload the view returns 0 rows after
INSERT, the API falls back to a minimal response, and Dexie save fails
with "Object is missing a valid ID". Pull event_id (and event_session_id
for presenters) from events_slct at creation time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace pill grid (too tall) with a single-row label + select
- Use <optgroup> to keep System / AE grouping without any extra height
- Prune unused Skeleton system themes: concord, crimson, hamlindigo,
rocket, terminus, vintage removed (6 of 10 — never used in practice)
Remaining: nouveau, cerberus, modern, wintry
- Added comment noting Firefly event-specific variants can be pruned
when their events are no longer active
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Theme picker (e_app_sys_bar.svelte):
- Replace flat <select> dropdown with grouped pill buttons
- Two groups: 'System' (Skeleton defaults) and 'AE' (custom themes)
- Active theme highlighted via bg-primary-500 (shows in the current theme's
own primary color — instant visual confirmation)
- Pill labels shortened where helpful (e.g. 'AE_Firefly_SteelBlue' → 'Steel ✦')
- Rename theme_options → skeleton_themes + ae_themes; extract apply_theme()
shared handler (removes duplicated setAttribute + ae_loc.update pattern)
Orphaned component removal (→ ~/tmp/agents_trash):
- e_app_theme.svelte — only imported by the dead legacy menu; the sys_bar
Appearance section fully covers it
- e_app_sys_menu_legacy.svelte — no consumers anywhere in the codebase;
sys_bar (mounted in +layout.svelte) fully replaces it
- e_app_cfg.svelte — only mounted from the dead legacy menu; its live
functionality (iframe toggle, reload, URL builder) is all in sys_bar's
Dev/Tools section
npx svelte-check: 0 errors (file count drops from 5403 → 5400)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.css: import ae-c-lci-new.css alongside existing ae-c-lci.css
- e_app_theme.svelte, e_app_sys_bar.svelte, e_app_url_builder.svelte:
add 'LCI (New ✦)' option pointing to AE_c_LCI_new selector
Both themes coexist — switch between 'LCI' and 'LCI (New ✦)' in the
theme picker to compare old vs brand-guide-accurate colors and fonts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New parallel theme (data-theme='AE_c_LCI_new') based on the LCI Brand
Guidelines (March 2023, leanconstruction.org). The current ae-c-lci.css
is untouched. Both can coexist.
To activate for review: add @import './ae-c-lci-new.css' to app.css and
add 'AE_c_LCI_new' as a theme option in e_app_theme.svelte /
e_app_sys_bar.svelte / e_app_url_builder.svelte.
Changes vs current theme:
- Primary blue: anchored to #3a5997 (oklch 39% 0.14 264°) — was too
light (47%) and undersaturated (C=0.11). Pantone Surf the Web.
- Secondary: rebuilt from 3-stop LCI scale (#d4e7f7 → #3598dc → #173378),
hue shifted from 264° to 245° (brighter sky-blue direction).
- Tertiary purple: corrected to #8f44ad oklch(43% 0.20 309°) — was too
light (52.73%) and low chroma (0.17). Pantone Dewberry.
- Success teal: minor tweak to #1bbc9d oklch(68% 0.14 175°). Pantone Mint Leaf.
- Error red: corrected to #df3527 oklch(53% 0.27 28°) — was too light
(59.32%) and undersaturated (C=0.21). Pantone Cherry Tomato.
- Typography: heading-font-family → Proxima Nova/Montserrat (brand spec);
base-font-family → Palatino/serif (brand spec). See file for Montserrat
Google Fonts note and app-UI override guidance.
- Heading color: primary-500 (LCI blue) per brand guide PDF.
- Surface: hue shifted from 196° (teal, wrong direction) to 248° (blue).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Updated Element_ae_obj_field_editor_new.svelte with on_open callback support.
- Improved Select field UX in the editor with an inline loading spinner when options are empty.
- Streamlined session_view.svelte by using on_open to trigger person record loading automatically.
- Removed redundant manual 'Select Person' button in favor of automated lazy-loading.
element_data_store.svelte:
- Source/Visual HTML mode toggles: add Code/Eye icons and title attributes
- Cancel button: add X icon and title="Discard changes and close"
- Delete button: add title="Permanently delete this data store — cannot be undone"
- Save button: add title, and Check icon for updated/created success state
(Check was imported but unused; now put to work matching field editor pattern)
- Remove Eraser import (unused)
- Remove ds_submit_results $state (declared but never read in template —
submit status already tracked via $ae_sess.ds.submit_status; drop the
assignment in handle_submit_form too)
element_ae_obj_field_editor_new.svelte:
- Fix duplicate is_editing declaration: user promoted it to a $bindable prop;
remove the now-conflicting local $state declaration
npx svelte-check: 0 errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Integrated AE_Comp_Editor_CodeMirror as a supported field_type.
- Enhanced button UX with descriptive Lucide icons and title text (Eraser for Clear, etc.).
- Switched Location View description field to use the CodeMirror editor.
- Updated test playground to include a CodeMirror example.
Rolls out the full rewrite of the generic inline field editor, standardizing on
Tailwind v4 + Flowbite styling and resolving several long-standing contract issues.
Component Improvements:
- Fully implemented datetime/date conversion (component now owns format normalization).
- Added type-safety via Svelte 5 generics (<script generics="T">).
- Added automatic select value coercion (prevents stuck optimistic state).
- Added support for 'email', 'url', and 'tel' field types.
- Rebuilt styling on Tailwind v4 utility classes, removing legacy Skeleton UI.
- Improved accessibility: added ARIA labels, Escape-to-cancel, and autofocus.
- Improved error handling with visible inline error messages.
Rollout/Migration:
- Migrated all 8 primary call sites (person_view, device_li, locations_li,
location_view, presentation_li, leads_manage, presenter_view, session_view).
- Swapped manual datetime pre-conversion at call sites for internal handling.
- Updated the test playground to show legacy vs. new side-by-side.
This migration runs the new component in parallel with the old one (still present)
ensuring a safe transition.
Creates the _new version of the field editor element alongside the working
original (which remains untouched). The _new file starts from the original's
hardened optimistic-update state machine and adds:
- Svelte 5 generics (T) on current_value/draft_value instead of any
- email / url / tel added to the field_type union with edit-mode branches
- object_reload prop removed (was declared, never implemented — on_success
is and remains the caller's cache-refresh hook)
- to_input_value() / from_input_value() stubs at the two right call sites
for datetime conversion (both directions, TODO #4 to implement)
- coerce_select_value() stub for select type-mismatch fix (TODO #5)
- Inline TODO mini how-to checked-list mirroring the project doc
- Styling still uses Skeleton classes — tagged for Tailwind/Flowbite swap
(TODO #6) and a11y gaps marked at their exact markup locations (TODO #7)
Companion files:
- documentation/PROJECT__AE_Obj_Field_Editor_New.md — full plan, migration
order for all 8 call sites, naming convention note
- documentation/TODO__Agents.md — new entry pointing to the project doc
- documentation/README__Docs_Index.md — project doc added to Active Projects
npx svelte-check: 0 errors, 1 expected benign warning (state_referenced_locally
on the field_type initializer, documented in-file).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
presenter_is_authed only checked sign-in state, never .agree, so a
presenter could upload without ever agreeing whenever the admin's
"Require Presenter Agreement" setting was on.
Added presenter_agree_ok (trusted_access || !require__presenter_agree ||
auth__event_presenter_obj.agree) and presenter_can_upload
(presenter_is_authed && presenter_agree_ok) in
presenter/[presenter_id]/+page.svelte, swapped into every place the
upload UI / file-list permissions are gated in both the default and
manage_files alt views. The alt view's public_access identity bypass is
preserved but now also requires presenter_agree_ok.
Added an inline warning message in place of the upload section when
signed in but pending agreement, instead of it silently disappearing.
Marked done in TODO__Agents.md; all three open LCI Pres Mgmt restoration
items are now resolved (2 were already fixed and just needed verifying).
svelte-check: 0 errors, 0 warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two things prompted by direct feedback after testing:
1. Every input on the Config page now has a title tooltip explaining what
it actually controls, where, and any interaction with other settings
(e.g. clarifying Hide Location affects both the session detail page AND
the Location column in Session Search results — not obvious from the
label alone).
2. Split "Session Field Visibility" into "Session Display" (just
description/location/message) and a new dedicated "POC Settings"
section. Previously hide__session_poc ("Hide POC", checked = remove)
sat directly next to show__session_li_poc_field ("Show POC Column",
checked = add) in the same flat list — same checked state meaning
opposite things for adjacent fields, confusing even though each field's
own hide__/show__ naming is internally consistent with the project
convention. Hide POC is now rendered as a visually distinct master
switch (bold, its own row) with the three dependent settings indented
under a left border below it and auto-disabled (with reduced opacity)
whenever Hide POC is checked, so the "no effect once hidden" dependency
is visible in the UI, not just documented in a footnote.
No field semantics changed — same PressMgmtRemoteCfg, same sync function,
same save path. Pure presentation/documentation pass.
Logged the rationale in PROJECT__AE_Events_PressMgmt_Config_Cleanup.md.
svelte-check: 0 errors, 0 warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same bug pattern as the earlier POC column fix. The Session Search
results table's Location column prop only ever read the local-only,
never-synced hide__session_li_location_field — never the admin-synced
hide__session_location (Config page > Session Field Visibility > Hide
Location). Column always showed regardless of that setting or
permission level.
Fixed in pres_mgmt/+page.svelte:
hide__session_location || hide__session_li_location_field
The other two usages of this component already hardcode
hide__session_location={true} correctly (you're already on that
location's own page, showing its name again would be redundant).
Logged in PROJECT__AE_Events_PressMgmt_Config_Cleanup.md with a note to
audit other hide__* fields for the same gap if more reports come in.
svelte-check: 0 errors, 0 warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lock_config made roughly half of sync_config__event_pres_mgmt()'s fields
conditional: they only updated in a given browser if lock_config happened
to be true at the exact moment that browser last synced. A field's local
value therefore depended on the history of saves, not the current setting
— undebuggable from the UI, and easy to corrupt by toggling Lock Config
off even briefly during testing. This was the actual root cause of the
"POC column / Hide POC sometimes works" reports.
Checked the DB: every event, old and new, already had lock_config: true.
The "unlocked, per-browser preference" use case it was built for has
never actually been used in practice.
- Removed lock_config from PressMgmtRemoteCfg, PresMgmtLocState, the
sync function (no more conditional block — every field syncs
unconditionally now, same as labels/Require Agreements always did),
and the Config page UI (no more System section)
- Old DB records with a lock_config key are simply ignored, same as any
other removed key — no migration needed
- Moved the now-fully-orphaned ae_comp__event_settings_pres_mgmt_form.svelte
to ~/tmp/agents_trash (zero imports anywhere; the settings page already
links to the canonical Config page) — completes Step 5 from the
cleanup doc
- Marked PROJECT__AE_Events_PressMgmt_Config_Cleanup.md complete — all
Implementation Steps and known regressions resolved
svelte-check: 0 errors, 0 warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four fixes found while tracing why Manager-saved Config page changes
(QR, POC column, etc.) weren't reliably reaching pres_mgmt_loc:
1. Config page save was a race, not deterministic. The save handler only
called load_ae_obj_id__event() (SWR — returns stale Dexie cache
immediately, refreshes in the background, not awaited) and assumed
that "picked up the new config." It never called
sync_config__event_pres_mgmt() itself. Now calls it directly with the
just-saved draft, so the editing browser updates instantly with no
race. Kept the load_ae_obj_id__event() call (default try_cache: true)
for propagating to other browsers/tabs via Dexie — do not pass
try_cache: false there, that skips the Dexie write entirely.
2. Removed the dead "Lock Config" Sync/Unlink toggle in the sign-in
panel (e_app_access_type.svelte). It wrote to four fields
($ae_loc.lock_config/sync_local_config,
pres_mgmt_loc.current.lock_config/sync_local_config) that are never
read anywhere (confirmed via full-repo grep), and confusingly shared
a name with the real, functional "Lock Config" checkbox on the Pres
Mgmt Config page. Removed the button and the now-orphaned
lock_config/sync_local_config fields from PresMgmtLocState.
3. show__launcher_link was never assigned by sync_config__event_pres_mgmt()
— only its inverse hide__launcher_link was. The toggle button's
`show__launcher_link || trusted_access` visibility gate (in 3 menu
files) always collapsed to trusted-only, ignoring the admin's setting.
Added the missing assignment.
4. AE_PRES_MGMT_LOC_VERSION was bumped to 2 this morning claiming it
"forces a localStorage reset" — it didn't, because _check_and_wipe()
was never wired up for ae_pres_mgmt_loc, and even if it had been, the
store never wrote a __version field to compare. Fixed: the store's
serializer now stamps __version, and store_versions.ts wires the
check. Found and fixed the same bug already live in ae_leads_loc,
except worse there — it was wiping leads users' local prefs on EVERY
page load, not just once.
All logged in PROJECT__AE_Events_PressMgmt_Config_Cleanup.md.
svelte-check: 0 errors, 0 warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>