From af2236eea4dd621ce17a0308ba15efd1beaa0b5d Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 16 Jun 2026 13:56:27 -0400 Subject: [PATCH] =?UTF-8?q?fix(pres=5Fmgmt):=20remove=20lock=5Fconfig=20?= =?UTF-8?q?=E2=80=94=20sync=20is=20now=20fully=20unconditional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ...ECT__AE_Events_PressMgmt_Config_Cleanup.md | 65 +++++--- src/lib/ae_events/ae_events__event.ts | 125 ++++++++------- .../app_components/e_app_access_type.svelte | 7 +- .../ae_events_stores__pres_mgmt_defaults.ts | 21 +-- .../(pres_mgmt)/pres_mgmt/config/+page.svelte | 32 +--- ...comp__event_settings_pres_mgmt_form.svelte | 149 ------------------ 6 files changed, 119 insertions(+), 280 deletions(-) delete mode 100644 src/routes/events/[event_id]/settings/ae_comp__event_settings_pres_mgmt_form.svelte diff --git a/documentation/PROJECT__AE_Events_PressMgmt_Config_Cleanup.md b/documentation/PROJECT__AE_Events_PressMgmt_Config_Cleanup.md index 7cf0d8b0..4aaa64f6 100644 --- a/documentation/PROJECT__AE_Events_PressMgmt_Config_Cleanup.md +++ b/documentation/PROJECT__AE_Events_PressMgmt_Config_Cleanup.md @@ -1,9 +1,9 @@ # Project: Pres Mgmt Config Cleanup & Config UI -**Status:** 🟡 ~70% Complete — Core Working, Cleanup Pass Needed -**Priority:** Medium (core features functional; remaining work is deprecation + consistency) +**Status:** 🟢 Complete — all Implementation Steps and known regressions resolved +**Priority:** Low (maintenance/monitoring only — re-open if new sync inconsistencies surface) **Created:** 2026-04-02 -**Last Updated:** 2026-06-12 (regression audit) +**Last Updated:** 2026-06-16 (`lock_config` removed; sync is now fully unconditional) **Related:** `TODO__Agents.md`, `PROJECT__Stores_Svelte5_Migration.md` --- @@ -61,8 +61,8 @@ persisted store which is part of the paused Svelte 5 migration. ```typescript interface PressMgmtRemoteCfg { - // System - lock_config: boolean; // true = force remote→local sync (prevent user overrides) + // No system/lock_config field — every field below syncs unconditionally, + // to every browser, on every event load. Removed 2026-06-16; see note below. // Labels (event-specific terminology overrides) label__person_external_id: string | null; // default: 'External ID' @@ -176,17 +176,18 @@ AFTER: pres_mgmt_loc.current.hide__session_code ### Form sections (grouped) -1. **System** — `lock_config` -2. **Labels** — `label__*` fields (text inputs, nullable) -3. **Session Visibility** — `hide__session_*` toggles -4. **Presenter Visibility** — `hide__presenter_*` toggles -5. **Presentation Visibility** — `hide__presentation_*` toggles -6. **Code Visibility** — `hide__*_code` toggles -7. **Opt-in Features** — `show__*` toggles -8. **Requirements** — `require__presenter_agree`, `require__session_agree` -9. **Navigation Limits** — `limit__navigation`, `limit__options` -10. **File Purpose Config** — `file_purpose_option_kv` (JSON editor or structured form) -11. **Report Visibility** — `hide__report_kv` (key-value toggles) +No System section — there is no `lock_config` (removed 2026-06-16). + +1. **Labels** — `label__*` fields (text inputs, nullable) +2. **Session Visibility** — `hide__session_*` toggles +3. **Presenter Visibility** — `hide__presenter_*` toggles +4. **Presentation Visibility** — `hide__presentation_*` toggles +5. **Code Visibility** — `hide__*_code` toggles +6. **Opt-in Features** — `show__*` toggles +7. **Requirements** — `require__presenter_agree`, `require__session_agree` +8. **Navigation Limits** — `limit__navigation` (`limit__options` removed — YAGNI) +9. **File Purpose Config** — `file_purpose_option_kv` (JSON editor or structured form) +10. **Report Visibility** — `hide__report_kv` (key-value toggles) --- @@ -204,13 +205,13 @@ Safe and backward compatible — old DB records fall through to `?? false` defau ## Implementation Steps - [x] **Step 1** — Define `PressMgmtRemoteCfg` TypeScript interface (new file or in `ae_events__event.ts`) -- [x] **Step 2** — New `ae_events_stores__pres_mgmt.svelte.ts` with `PersistedState` ⚠️ **Missing:** version gate in `store_versions.ts` -- [x] **Step 3** — Rewrite `sync_config__event_pres_mgmt()` in `ae_events__event.ts` to use canonical keys ⚠️ **Issue:** `show__launcher_link_legacy` hard-coded instead of synced from remote +- [x] **Step 2** — New `ae_events_stores__pres_mgmt.svelte.ts` with `PersistedState`, version gate wired (2026-06-16) +- [x] **Step 3** — Rewrite `sync_config__event_pres_mgmt()` in `ae_events__event.ts` to use canonical keys; syncs unconditionally, no `lock_config` gate (2026-06-16) - [x] **Step 4** — Build config UI page at `(pres_mgmt)/pres_mgmt/config/+page.svelte` (manager_access + edit_mode gated) -- [ ] **Step 5** — Strip `ae_comp__event_settings_pres_mgmt_form.svelte` from settings page (or replace with a link to new page) **BLOCKING** +- [x] **Step 5** — `ae_comp__event_settings_pres_mgmt_form.svelte` moved to trash (2026-06-16) — it was already fully orphaned (zero imports anywhere); the settings page already links to the canonical Config page with a raw-JSON fallback - [x] **Step 6** — Migrate all `$events_loc.pres_mgmt.*` references in pres_mgmt templates to `pres_mgmt_loc.current.*` -- [ ] **Step 7** — Update BGH (and any other active events) via new UI (blocked on Step 5) -- [ ] **Step 8** — `npx svelte-check` clean; commit +- [x] **Step 7** — Active events' `lock_config` keys are now just inert orphan data in the DB, ignored by the sync function — no migration needed, nothing to update +- [x] **Step 8** — `npx svelte-check` clean ### Regression Fixes Needed (2026-06-12 Audit) @@ -305,6 +306,21 @@ Safe and backward compatible — old DB records fall through to `?? false` defau was never even in `ae_loc`'s declared defaults (a phantom field created ad-hoc by the dead button), and `ae_loc.sync_local_config` is out of scope for a pres_mgmt-only pass; defer to the planned `ae_loc` migration in `PROJECT__Stores_Svelte5_Migration.md`. +- [x] **`lock_config` removed from `PressMgmtRemoteCfg` entirely (2026-06-16)** — the real + one this time (the item above was a different, dead local-only mirror of the same name). + This was the actual root cause of the "POC column / Hide POC sometimes works" reports. + `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. 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. Removed it from `PressMgmtRemoteCfg`, + `PresMgmtLocState`, the sync function (no more conditional block — every field syncs + unconditionally, 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 now, + same as any other removed key. Moved the now-fully-orphaned + `ae_comp__event_settings_pres_mgmt_form.svelte` to trash in the same pass (Step 5). ### Step 6 scope (mechanical find-replace) @@ -323,8 +339,11 @@ The `$events_loc.pres_mgmt` pattern appears across: ## Notes -- The `lock_config: true` default means most events will always sync from remote. - This is intentional — it prevents presenter laptops from drifting into different configs. +- All pres_mgmt display config now always syncs from remote, unconditionally, to every + browser, on every event load (no `lock_config` toggle — removed 2026-06-16). This is + intentional — it prevents presenter laptops from drifting into different configs, which + is exactly what `lock_config: true` was meant to guarantee, minus the conditional that + made it fragile. - `file_purpose_option_kv` may need a structured editor (not raw JSON) to be usable. Consider a simple key-value form row per purpose type for Phase 2. - QR link keys (`hide__presenter_qr_link`, `hide__session_qr_link`) appeared in LCI config diff --git a/src/lib/ae_events/ae_events__event.ts b/src/lib/ae_events/ae_events__event.ts index 92b02ea1..78537058 100644 --- a/src/lib/ae_events/ae_events__event.ts +++ b/src/lib/ae_events/ae_events__event.ts @@ -954,12 +954,20 @@ export async function process_ae_obj__event_props({ * Syncs the server-side event pres_mgmt config (mod_pres_mgmt_json) into the * local PersistedState store (pres_mgmt_loc.current). * - * Called reactively in pres_mgmt/+page.svelte whenever the event object changes. + * Called reactively whenever the event object changes — see the sync $effect + * in pres_mgmt/+page.svelte, session/[session_id]/+page.svelte, + * presenter/[presenter_id]/+page.svelte, locations/+page.svelte, + * location/[event_location_id]/+page.svelte, and reports/+page.svelte. Any new + * pres_mgmt page that can be a first-load entry point needs the same block. * - * Always-synced fields: labels, requirements, access links, file config. - * Lock-synced fields (only when lock_config=true): all visibility/hide__ toggles, - * launcher links, navigation limits. This prevents presenter laptops from - * drifting into different display configs across a conference. + * Every field in PressMgmtRemoteCfg is synced unconditionally, every call — + * no opt-out, no gate. (Removed 2026-06-16: a `lock_config` toggle used to make + * a subset of these fields conditional. In practice every event always had it + * set to true, and the conditional sync it created meant a field's local value + * depended on whatever lock_config happened to be during the LAST sync that + * touched that particular browser — not its current value. That's effectively + * undebuggable from the UI and was the root cause of several "sometimes works" + * bug reports. See PROJECT__AE_Events_PressMgmt_Config_Cleanup.md.) * * Canonical key convention (enforced here): * hide__* → feature ON by default; true = turn off (default: false = visible) @@ -981,8 +989,7 @@ export function sync_config__event_pres_mgmt({ const loc = pres_mgmt_loc.current; - // --- Always sync: labels, requirements, opt-in features, file config --- - // These are safe to always apply — they don't override local display preferences. + // --- Labels --- loc.label__person_external_id = pres_mgmt_cfg_remote?.label__person_external_id ?? 'External ID'; loc.label__presenter_external_id = @@ -992,85 +999,77 @@ export function sync_config__event_pres_mgmt({ loc.label__session_poc_name = pres_mgmt_cfg_remote?.label__session_poc_name ?? 'Point of Contact'; + // --- Requirements --- loc.require__presenter_agree = pres_mgmt_cfg_remote?.require__presenter_agree ?? false; loc.require__session_agree = pres_mgmt_cfg_remote?.require__session_agree ?? false; + // --- Opt-in features --- loc.show__copy_access_link = pres_mgmt_cfg_remote?.show__copy_access_link ?? false; loc.show__email_access_link = pres_mgmt_cfg_remote?.show__email_access_link ?? false; - - // QR code feature enable — always synced (admin opt-in, not a user preference) loc.show__session_qr = pres_mgmt_cfg_remote?.show__session_qr ?? false; loc.show__presenter_qr = pres_mgmt_cfg_remote?.show__presenter_qr ?? false; + // --- File / report config --- loc.file_purpose_option_kv = pres_mgmt_cfg_remote?.file_purpose_option_kv ?? null; - loc.hide__report_kv = pres_mgmt_cfg_remote?.hide__report_kv ?? {}; - // --- Lock-synced: visibility, launcher, navigation, session/presenter fields --- - // When lock_config=true the remote config wins for ALL display flags, preventing - // local browser state from overriding the admin's event-level configuration. - if (pres_mgmt_cfg_remote?.lock_config) { - if (log_lvl) console.log(`sync_config__event_pres_mgmt: lock_config=true, forcing full sync`); + // --- Codes --- + loc.hide__location_code = + pres_mgmt_cfg_remote?.hide__location_code ?? false; + loc.hide__presentation_code = + pres_mgmt_cfg_remote?.hide__presentation_code ?? false; + loc.hide__presenter_code = + pres_mgmt_cfg_remote?.hide__presenter_code ?? false; + loc.hide__session_code = + pres_mgmt_cfg_remote?.hide__session_code ?? false; - // Codes - loc.hide__location_code = - pres_mgmt_cfg_remote?.hide__location_code ?? false; - loc.hide__presentation_code = - pres_mgmt_cfg_remote?.hide__presentation_code ?? false; - loc.hide__presenter_code = - pres_mgmt_cfg_remote?.hide__presenter_code ?? false; - loc.hide__session_code = - pres_mgmt_cfg_remote?.hide__session_code ?? false; + // --- Session fields --- + loc.hide__session_description = + pres_mgmt_cfg_remote?.hide__session_description ?? false; + loc.hide__session_location = + pres_mgmt_cfg_remote?.hide__session_location ?? false; + loc.hide__session_msg = + pres_mgmt_cfg_remote?.hide__session_msg ?? false; + loc.hide__session_poc = + pres_mgmt_cfg_remote?.hide__session_poc ?? false; + loc.show__session_li_poc_field = + pres_mgmt_cfg_remote?.show__session_li_poc_field ?? false; + loc.hide__session_poc_biography = + pres_mgmt_cfg_remote?.hide__session_poc_biography ?? false; + loc.hide__session_poc_profile = + pres_mgmt_cfg_remote?.hide__session_poc_profile ?? false; + loc.hide__session_poc_profile_pic = + pres_mgmt_cfg_remote?.hide__session_poc_profile_pic ?? false; - // Session fields - loc.hide__session_description = - pres_mgmt_cfg_remote?.hide__session_description ?? false; - loc.hide__session_location = - pres_mgmt_cfg_remote?.hide__session_location ?? false; - loc.hide__session_msg = - pres_mgmt_cfg_remote?.hide__session_msg ?? false; - loc.hide__session_poc = - pres_mgmt_cfg_remote?.hide__session_poc ?? false; - loc.show__session_li_poc_field = - pres_mgmt_cfg_remote?.show__session_li_poc_field ?? false; - loc.hide__session_poc_biography = - pres_mgmt_cfg_remote?.hide__session_poc_biography ?? false; - loc.hide__session_poc_profile = - pres_mgmt_cfg_remote?.hide__session_poc_profile ?? false; - loc.hide__session_poc_profile_pic = - pres_mgmt_cfg_remote?.hide__session_poc_profile_pic ?? false; + // --- Presenter fields --- + loc.hide__presenter_biography = + pres_mgmt_cfg_remote?.hide__presenter_biography ?? false; - // Presenter fields - loc.hide__presenter_biography = - pres_mgmt_cfg_remote?.hide__presenter_biography ?? false; + // --- Presentation fields --- + loc.hide__presentation_datetime = + pres_mgmt_cfg_remote?.hide__presentation_datetime ?? false; + loc.hide__presentation_description = + pres_mgmt_cfg_remote?.hide__presentation_description ?? false; - // Presentation fields - loc.hide__presentation_datetime = - pres_mgmt_cfg_remote?.hide__presentation_datetime ?? false; - loc.hide__presentation_description = - pres_mgmt_cfg_remote?.hide__presentation_description ?? false; + // --- Launcher links (show__ in remote → invert to hide__ in local display state) --- + loc.hide__launcher_link = + !(pres_mgmt_cfg_remote?.show__launcher_link ?? false); + // Mirror the raw remote flag too — the toggle BUTTON's own visibility (not the + // launcher link content itself) is gated on show__launcher_link directly in + // ae_comp__events_menu_opts.svelte / event_page_menu.svelte / location_page_menu.svelte. + loc.show__launcher_link = + pres_mgmt_cfg_remote?.show__launcher_link ?? false; - // Launcher links (show__ in remote → invert to hide__ in local display state) - loc.hide__launcher_link = - !(pres_mgmt_cfg_remote?.show__launcher_link ?? false); - // Mirror the raw remote flag too — the toggle BUTTON's own visibility (not the - // launcher link content itself) is gated on show__launcher_link directly in - // ae_comp__events_menu_opts.svelte / event_page_menu.svelte / location_page_menu.svelte. - // This was never assigned before, so that gate always collapsed to trusted_access-only. - loc.show__launcher_link = - pres_mgmt_cfg_remote?.show__launcher_link ?? false; - - // Navigation / UI constraints - loc.limit__navigation = - pres_mgmt_cfg_remote?.limit__navigation ?? false; - } + // --- Navigation / UI constraints --- + loc.limit__navigation = + pres_mgmt_cfg_remote?.limit__navigation ?? false; } /** diff --git a/src/lib/app_components/e_app_access_type.svelte b/src/lib/app_components/e_app_access_type.svelte index 7d7f2f2c..aca597c2 100644 --- a/src/lib/app_components/e_app_access_type.svelte +++ b/src/lib/app_components/e_app_access_type.svelte @@ -360,10 +360,9 @@ function handle_clear_access() { - - -
- - {#if sections.system} -
- -
- {/if} -
- diff --git a/src/routes/events/[event_id]/settings/ae_comp__event_settings_pres_mgmt_form.svelte b/src/routes/events/[event_id]/settings/ae_comp__event_settings_pres_mgmt_form.svelte deleted file mode 100644 index 36524f71..00000000 --- a/src/routes/events/[event_id]/settings/ae_comp__event_settings_pres_mgmt_form.svelte +++ /dev/null @@ -1,149 +0,0 @@ - - -
- {#if mod_pres_mgmt_json} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- {/if} - - -