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>
21 KiB
Project: Pres Mgmt Config Cleanup & Config UI
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-16 (lock_config removed; sync is now fully unconditional)
Related: TODO__Agents.md, PROJECT__Stores_Svelte5_Migration.md
Background
The event.mod_pres_mgmt_json config grew organically across several conferences
(LCI, BGH, etc.) and has accumulated serious inconsistencies:
- Mixed
show__andhide__prefixes for the same concepts - Some features have BOTH
show__fooandhide__fookeys active simultaneously - Duplicate keys with different names (
file_purpose_option_kv=file_purpose_option_li) - Dead config (
HOLD__*prefix) - Type inconsistency (
label__person_external_id: falsevs"LCI member ID"string) - Keys in the DB not consumed by
sync_config__event_pres_mgmt() - Bug:
label__session_poc_name_shortis read then immediately overwritten (line 970-972 in ae_events__event.ts) hide_launcher_link/hide_launcher_link_legacymissing the__separator (inconsistent)show_content__presentation_descriptionuses a third naming convention- Admin must edit DB records directly to change config — error-prone
The local config (events_loc.pres_mgmt) is also tangled into the main events_loc
persisted store which is part of the paused Svelte 5 migration.
Goals
- Canonical config schema — define a TypeScript interface for
mod_pres_mgmt_json - Consistent naming convention — one rule for all
show__/hide__keys - New Svelte 5 store — break out local pres_mgmt config from
events_loc - Config UI — admin page within pres_mgmt to manage the remote config
- No more direct DB edits for routine pres_mgmt configuration
Convention Decision
Rule: the prefix reflects the default state.
| Prefix | Default | Use for |
|---|---|---|
hide__ |
false = visible |
Features ON by default that can be turned off |
show__ |
false = hidden |
Features OFF by default that can be turned on |
Never have both show__foo and hide__foo for the same concept.
- Visibility controls (codes, descriptions, POC, biography) → default visible →
hide__ - Opt-in features (access links, launcher, QR links) → default hidden →
show__
Canonical Remote Config Schema
PressMgmtRemoteCfg — the authoritative TypeScript interface for event.mod_pres_mgmt_json:
interface PressMgmtRemoteCfg {
// 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'
label__presenter_external_id: string | null; // default: 'External ID'
label__session_poc_type: string | null; // e.g. 'champion', 'poc'
label__session_poc_name: string | null; // e.g. 'Champion', 'Point of Contact'
// Codes (visible by default — hide to suppress)
hide__location_code: boolean;
hide__presentation_code: boolean;
hide__presenter_code: boolean;
hide__session_code: boolean;
// Session fields (visible by default)
hide__session_description: boolean;
hide__session_location: boolean;
hide__session_msg: boolean;
hide__session_poc: boolean;
hide__session_poc_biography: boolean;
hide__session_poc_profile_pic: boolean;
// Presenter fields
hide__presenter_biography: boolean;
// Presentation fields
hide__presentation_datetime: boolean;
hide__presentation_description: boolean; // replaces show_content__presentation_description
// Opt-in features (hidden by default — show to enable)
show__copy_access_link: boolean;
show__email_access_link: boolean;
show__launcher_link: boolean;
show__launcher_link_legacy: boolean;
show__session_li_poc_field: boolean; // POC column in session list/table; hide__session_poc still wins
// Requirements
require__presenter_agree: boolean;
require__session_agree: boolean;
// Navigation/UI constraints
limit__navigation: boolean;
limit__options: boolean;
// File upload config
file_purpose_option_kv: Record<string, {
name: string;
disabled?: boolean;
hidden?: boolean;
}> | null;
// Report visibility (key = report slug, value = true to hide)
hide__report_kv: Record<string, boolean>;
}
Keys Removed vs. Current DB Records
| Removed Key | Reason |
|---|---|
file_purpose_option_li |
Duplicate of file_purpose_option_kv |
HOLD__file_os_selection_option |
Dead/held feature |
hide__copy_access_link |
Conflicts with show__copy_access_link — use show__ |
hide__email_access_link |
Conflicts with show__email_access_link — use show__ |
hide__launcher_link |
Conflicts with show__launcher_link — use show__ |
hide__launcher_link_legacy |
Conflicts with show__launcher_link_legacy — use show__ |
hide__report_li |
Superseded by hide__report_kv |
show__navigation |
Ambiguous — covered by limit__navigation |
label__session_poc_name_short |
Was a bug — never applied (overwritten immediately) |
show_content__presentation_description |
Renamed to hide__presentation_description |
New Svelte 5 Local Store
Do NOT touch events_loc or the paused Svelte 5 migration.
Instead, create a standalone store for pres_mgmt local config.
File: src/lib/stores/ae_events_stores__pres_mgmt.svelte.ts
import { PersistedState } from 'runed';
import { pres_mgmt_loc_defaults } from './ae_events_stores__pres_mgmt_defaults';
export const pres_mgmt_loc = new PersistedState('ae_pres_mgmt_loc', pres_mgmt_loc_defaults);
// Usage: pres_mgmt_loc.current.hide__session_code
- New localStorage key:
ae_pres_mgmt_loc(separate fromae_events_loc) - Version gate: add
AE_PRES_MGMT_LOC_VERSIONtostore_versions.ts sync_config__event_pres_mgmt()writes topres_mgmt_loc.currentdirectly
Consumer syntax change:
BEFORE: $events_loc.pres_mgmt.hide__session_code
AFTER: pres_mgmt_loc.current.hide__session_code
Config UI Page
Route: /events/[event_id]/(pres_mgmt)/pres_mgmt/config/
Access: $ae_loc.manager_access only
Button visibility: Edit mode only ($ae_loc.edit_mode)
Page behavior
- Loads
event.mod_pres_mgmt_jsonfresh from API on page open - Displays grouped form sections (see below)
- Save = load → merge → PATCH
/v3/crud/event/{event_id}with{ mod_pres_mgmt_json: updated } - The existing settings form at
/events/[id]/settingshas its pres_mgmt section removed or replaced with a link
Form sections (grouped)
No System section — there is no lock_config (removed 2026-06-16).
- Labels —
label__*fields (text inputs, nullable) - Code Visibility —
hide__*_codetoggles - Session Display —
hide__session_description/location/msg(just the simple ones — POC split out below 2026-06-16 for clarity) - POC Settings (Point of Contact) —
hide__session_pocrendered as a visually distinct master switch, withshow__session_li_poc_field/hide__session_poc_biography/hide__session_poc_profile/hide__session_poc_profile_picindented and auto-disabledunderneath it when the master switch is on. Was previously flat in "Session Field Visibility" withhide__session_pocandshow__session_li_poc_fieldsitting adjacent with opposite checked-state polarity ("Hide POC" checked = remove, "Show POC Column" checked = add) — confusing, per direct user feedback. Splitting it into its own section with explicit master/sub-setting hierarchy fixed it without touching the underlying field semantics (stillhide__/show__per the naming convention — only the presentation changed). - Presenter Visibility —
hide__presenter_*toggles - Presentation Visibility —
hide__presentation_*toggles - Opt-in Features —
show__*toggles - Requirements —
require__presenter_agree,require__session_agree - Navigation Limits —
limit__navigation(limit__optionsremoved — YAGNI) - File Purpose Config —
file_purpose_option_kv(JSON editor or structured form) - Report Visibility —
hide__report_kv(key-value toggles)
Migration Path
Safe and backward compatible — old DB records fall through to ?? false defaults.
- No DB migration script needed — old keys are simply ignored by the updated sync function
- Active events (BGH) get updated via the new UI after it's built
- The
sync_config__event_pres_mgmt()rewrite is the critical step — it must handle the canonical keys and clean defaults before the UI ships
Implementation Steps
- Step 1 — Define
PressMgmtRemoteCfgTypeScript interface (new file or inae_events__event.ts) - Step 2 — New
ae_events_stores__pres_mgmt.svelte.tswithPersistedState, version gate wired (2026-06-16) - Step 3 — Rewrite
sync_config__event_pres_mgmt()inae_events__event.tsto use canonical keys; syncs unconditionally, nolock_configgate (2026-06-16) - Step 4 — Build config UI page at
(pres_mgmt)/pres_mgmt/config/+page.svelte(manager_access + edit_mode gated) - Step 5 —
ae_comp__event_settings_pres_mgmt_form.sveltemoved 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 - Step 6 — Migrate all
$events_loc.pres_mgmt.*references in pres_mgmt templates topres_mgmt_loc.current.* - Step 7 — Active events'
lock_configkeys are now just inert orphan data in the DB, ignored by the sync function — no migration needed, nothing to update - Step 8 —
npx svelte-checkclean
Regression Fixes Needed (2026-06-12 Audit)
hide__launcher_link_legacyremoved entirely (other agent's "config schema cleanup phase 2" commit, 2026-06-16) — Flask launcher is fully retired, no longer hard-coded or present anywhere inPressMgmtRemoteCfg/PresMgmtLocState/ the sync function.hide__launcher_link*/show__launcher_linklocal/remote conflict resolved (2026-06-16) — kept separate (they serve different purposes:hide__launcher_linkgates the launcher link content,show__launcher_linkgates the manual toggle button's visibility), butshow__launcher_linkwas never actually assigned bysync_config__event_pres_mgmt()— only its inversehide__launcher_linkwas. So the toggle button'sshow__launcher_link || trusted_accessgate (inae_comp__events_menu_opts.svelte,event_page_menu.svelte,location_page_menu.svelte) always collapsed to trusted-only, ignoring the admin's setting. Added the missingloc.show__launcher_link = ...assignment right next tohide__launcher_linkin the lock-synced block.AE_PRES_MGMT_LOC_VERSIONproperly wired intostore_versions.ts(2026-06-16) — the other agent's commit bumped this constant to 2 claiming it "forces a localStorage reset," but_check_and_wipe()was never actually called forae_pres_mgmt_loc, and even if it had been, the store's serializer never wrote a__versionfield for it to compare against — so the bump was a complete no-op. Fixed:ae_events_stores__pres_mgmt.svelte.ts's custom serializer now stamps__versionon every write, andstore_versions.tscalls_check_and_wipe('ae_pres_mgmt_loc', AE_PRES_MGMT_LOC_VERSION). Side effect: every browser's existingae_pres_mgmt_loc(no__versionever written before) will wipe once on next load and resync clean from the remote config — this is expected and fine. Found the same bug already live inae_leads_loc(actively wiping leads users' local prefs on every page load, not just once) and fixed it the same way — seeae_events_stores__leads.svelte.ts.badges_loc/launcher_loc/events_auth_lochave version constants declared but not wired into_check_and_wipe()at all (dormant, not actively harmful) — not fixed, flagged for whoever picks that up next.- POC column local/remote conflict fixed (2026-06-16) —
show__session_li_poc_fieldwas local-only (never synced) and the session-list-table prop computation ignored the admin'shide__session_pocmaster switch entirely. Fixed: addedshow__session_li_poc_fieldtoPressMgmtRemoteCfg+ Config UI (Session Field Visibility) +sync_config__event_pres_mgmt()lock-synced block; list/table column visibility is nowhide__session_poc || !show__session_li_poc_fieldinpres_mgmt/+page.svelteandlocations/ae_comp__event_location_obj_li.svelte. The local per-browser "Show/Hide POC Column" toggle buttons inae_comp__events_menu_opts.svelteandevent_page_menu.sveltewere removed — the field is lock-synced from the per-event Config page now, same as the other session field visibility toggles. - Presenter QR matched to session QR pattern (2026-06-16) —
show__session_qr/show__presenter_qrare the admin-set global default for everyone, signed in or not.show_content__*_qris a trusted-staff-only local override, used when the admin has NOT enabled QR globally. The session QR side already worked this way (fixed earlier today); presenter QR was still on the old buggy logic requiringshow_content__presenter_qrto be true even when the admin had enabled it for everyone, which non-trusted users (presenters) have no way to set. Fixedpresenter_view.svelte(generation effect + display block) toshow__presenter_qr || (trusted_access && show_content__presenter_qr). Also corrected two toggle-button visibility bugs found along the way:ae_comp__events_menu_opts.sveltewas showing the QR toggle to allauthenticated_accessusers (not just trusted) whenever the admin had enabled QR globally, even though the toggle had no effect for them; andpresenter_page_menu.svelte's QR toggle was gated toadministrator_access, hiding it from plain Trusted onsite staff entirely. Both now use the canonical pattern fromsession_page_menu.svelte:trusted_access && !show__*_qr. - Missing
sync_config__event_pres_mgmt()calls on deep-link entry pages (2026-06-16) — root cause of the presenter QR bug above: a browser landing directly on the presenter page (e.g. via a presenter sign-in link) never synced remote config intopres_mgmt_loc.currentat all, since onlypres_mgmt/+page.svelteandsession/[session_id]/+page.sveltecalled the sync function. Every "always synced" and "lock-synced" field (QR enables, POC visibility, code visibility, labels, etc.) silently stayed at hardcoded local defaults on that browser regardless of admin Config page settings. Added the same sync$effect(mirroring the existing comment/pattern insession/[session_id]/+page.svelte) topresenter/[presenter_id]/+page.svelte,locations/+page.svelte,location/[event_location_id]/+page.svelte, andreports/+page.svelte. Any new pres_mgmt page that can be a first-load entry point (i.e. not always reached via/pres_mgmtor/session/[id]first) needs this same block. - Config page save was a race, not deterministic (2026-06-16) — after PATCHing
mod_pres_mgmt_json, the save handler only calledload_ae_obj_id__event()(SWR — returns the stale Dexie cache immediately, refreshes from the API in the background, not awaited) and assumed that "picked up the new config." It never actually calledsync_config__event_pres_mgmt()itself. Whether the editor's own browser reflected the change depended entirely on winning a race against an un-awaited background fetch — explains why specific just-changed fields (QR, POC column, profile-pic visibility, one report key) intermittently looked stale even to the admin who just saved them, while older unchanged fields stayed correct. Fixed: the save handler now callssync_config__event_pres_mgmt({ pres_mgmt_cfg_remote: draft })directly with the just-saved draft, so the editing browser updates instantly with no race. (Kept theload_ae_obj_id__event()call too, with its defaulttry_cache: true— that's what propagates the fresh record to Dexie for other browsers/tabs. Do not passtry_cache: falsethere — that skips the Dexie write entirely, see the documented "try_cache: false Bug" inGUIDE__SvelteKit2_Svelte5_DexieJS.md.) - Removed dead "Lock Config" Sync/Unlink toggle (2026-06-16) — a Manager-only
button in the sign-in panel (
e_app_access_type.svelte) wrote to$ae_loc.lock_config/sync_local_configandpres_mgmt_loc.current.lock_config/sync_local_config. Confirmed via full-repo grep that none of those four fields are read anywhere. It also confusingly shared the name "Lock Config" with the real, functional checkbox on the Pres Mgmt Config page (draft.lock_config, part ofPressMgmtRemoteCfg, which actually gatessync_config__event_pres_mgmt()'s remote→local sync). Removed the button and the now-fully-orphanedlock_config/sync_local_configfields fromPresMgmtLocState. Left$ae_loc.lock_config/sync_local_config(the general app store) alone —lock_configwas never even inae_loc's declared defaults (a phantom field created ad-hoc by the dead button), andae_loc.sync_local_configis out of scope for a pres_mgmt-only pass; defer to the plannedae_locmigration inPROJECT__Stores_Svelte5_Migration.md. lock_configremoved fromPressMgmtRemoteCfgentirely (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_configmade roughly half ofsync_config__event_pres_mgmt()'s fields conditional: they only updated in a given browser iflock_confighappened to betrueat 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 hadlock_config: true— the "unlocked, per-browser preference" use case it was built for has never actually been used. Removed it fromPressMgmtRemoteCfg,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 alock_configkey are simply ignored now, same as any other removed key. Moved the now-fully-orphanedae_comp__event_settings_pres_mgmt_form.svelteto trash in the same pass (Step 5).- Location column ignored the admin's "Hide Location" setting (2026-06-16) — same
bug pattern as the POC column fix earlier, just missed for Location. The Session Search
results table's Location column prop only ever read the local-only, never-synced
hide__session_li_location_field— it never looked at the admin-syncedhide__session_location(Config page → Session Field Visibility → Hide Location) at all. So the column always showed regardless of that setting or the user's permission level, exactly as reported. Fixed inpres_mgmt/+page.svelte:hide__session_location || hide__session_li_location_field. The two other usages of this component (locations/ae_comp__event_location_obj_li.svelte,location/[event_location_id]/+page.svelte) already hardcodehide__session_location={true}— correct, since you're already on that location's own page. Worth auditing the other per-event hide__* fields for the same "admin field exists but the list/table prop computation never reads it" gap if more reports come in.
Step 6 scope (mechanical find-replace)
The $events_loc.pres_mgmt pattern appears across:
ae_comp__event_session_obj_li.svelteae_comp__events_menu_opts.sveltesession/[session_id]/+page.sveltesession/[session_id]/session_view.sveltesession/[session_id]/session_page_menu.sveltelocations/locations_page_menu.sveltereports/+page.sveltepres_mgmt/+page.svelte- (and likely others — run
grep -r 'events_loc.pres_mgmt' src/to get full list)
Notes
- All pres_mgmt display config now always syncs from remote, unconditionally, to every
browser, on every event load (no
lock_configtoggle — removed 2026-06-16). This is intentional — it prevents presenter laptops from drifting into different configs, which is exactly whatlock_config: truewas meant to guarantee, minus the conditional that made it fragile. file_purpose_option_kvmay 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 but are not in the canonical schema above. Evaluate whether they're actively used before adding them back. limit__navigationandlimit__optionsare in the DB but not currently read bysync_config__event_pres_mgmt(). Confirm where they're consumed before adding to sync.