"Try Again" resets auto_retry_count but reuses the same localStorage state — if
ae_loc or ae_idaa_loc holds a stale account_id or api_secret_key, every retry
fails identically and the user is stuck in an infinite error loop.
New button clears ae_loc + ae_idaa_loc from localStorage and db_events.event
from IDB, then reloads via the sessionStorage-preserved UUID URL (same logic as
the IDAA layout's Clear Cache & Reload). Forces a fresh FQDN handshake and
re-derives correct auth state. Guidance text shown so users know to try it when
Try Again keeps failing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before: a 503 response from /v3/action/idaa/novi_member/{uuid} hit !response.ok
immediately, set verify_failed_for_uuid, and showed "Verification Unavailable" —
no automatic retry. In the old client-to-Novi flow an unreachable server threw
TypeError (auto-retried); the new server-side path returns a clean HTTP 503
(plain Error), bypassing the is_network_or_timeout branch.
After: 503 gets the same 3s wait + one retry that network/timeout errors get.
Only if the retry also 503s does it fall through to the error UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The server-side migration removed the old novi_idaa_api_key check, which was
also acting as an implicit 'is IDAA configured here?' guard. Without it, any
domain that resolves (including ghost/domain-not-found with account_id='ghost')
would fire the Aether endpoint and get an error response, showing 'Verification
Unavailable' over the root layout's 'Domain Not Found' message.
Restore the site_cfg.novi_idaa_api_key presence check as the first guard:
- key absent → site_cfg_json still loading OR this is not an IDAA site → skip
- account_id='ghost' → domain lookup failed → added explicit ghost guard too
The key itself is unused for auth (server holds it); we only test its presence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
verify_novi_uuid() now calls GET /v3/action/idaa/novi_member/{uuid} instead
of fetching Novi directly from the browser. The Aether backend handles the
Novi call server-to-server, eliminating false Access Denied failures caused
by hotel/conference WiFi, VPNs, and Cloudflare IP filtering.
Response parsing simplified — full_name and email are normalized server-side.
Empty-200 anti-pattern handling, email space→+ normalization, and display-name
formatting moved to the backend (confirmed working per API agent).
Retry logic and error classification unchanged (429→rate_limited, network
error→retry once, all others→api_error).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Classify persistent network/timeout failures as 'network_error' (separate from
generic 'api_error') so the UI can show a targeted message
- Add actionable hint for members on hotel WiFi, VPN, or corporate networks:
turn off VPN, switch to cellular, try a different network
- Extend VERIFIED_TTL_MS_DEFAULT from 45 min to 12 hours — covers a full workday
so members at conferences do not need to re-verify mid-day
- Document planned server-side Novi verification FastAPI endpoint in
CLIENT__IDAA_and_customized_mods.md (once implemented, eliminates client-side
Cloudflare/IP-reputation exposure entirely)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
VERIFY_TIMEOUT_MS 8s → 35s: worst-case auto-retry cycle is 27s (12s abort +
3s wait + 12s abort). At 8s the "Reset & Retry" banner fired while the second
retry was still in flight; members who clicked it cleared their stores and
reloaded mid-attempt, landing on Access Denied. At 35s the escape hatch only
appears if verification is genuinely stuck (slow Novi server or missing api_key).
sessionStorage try-catch: iOS Safari Private Browsing and certain iframe sandbox
configs throw on sessionStorage access. Wrap setItem (onMount) and getItem
(reload_with_uuid) in try-catch so the component degrades gracefully to
location.reload() rather than crashing silently.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
'origin' is a reserved web term (protocol+domain); the function restores the
initial Novi-provided iframe URL (which includes the ?uuid= param), not the
site root. Renamed to reload_with_uuid to make that clear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wrap Novi API fetch in AbortController with 12s hard timeout — prevents
verify_in_flight from getting stuck if Novi's server hangs with no response
- Auto-retry once (after 3s) on network errors (TypeError: Failed to fetch)
and timeouts (AbortError) — these are almost always transient cellular/WiFi
blips and previously hard-failed with no second chance
- Rate-limit retries (429) already had a 10s wait; network retry is separate
- Update status message to "Connection issue — retrying..." during network retry
- Update error panel hint to suggest closing/reopening the Novi page as last resort
- Update Access Denied hint with same guidance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add reload_to_origin(): saves initial iframe URL (with ?uuid=) to sessionStorage
on mount; all reload buttons use it instead of bare location.reload() so the UUID
is preserved after internal SvelteKit navigation strips it from the URL
- Fix TTL short-circuit to also check $ae_loc permissions — without this, a store
reset (browser restart, stale localStorage) while the TTL was still valid would
skip re-verification and fall straight to Access Denied
- Extend Novi verification TTL from 5 to 25 minutes
- Add Clear Cache & Reload option to the Access Denied state (iframe mode)
- Move Novi UUID debug info on Access Denied page to edit_mode only; UUID line
at bottom of auth'd pages stays always visible for troubleshooting
- Remove expired temporary tech-notice variables (template block was already commented out)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The secondary client-side filter was re-checking qry_str against name, description,
location_text, and default_qry_str on the API response. This silently dropped meetings
that the API correctly matched via default_qry_str (a backend-generated combined index
containing contact name/email) — because that field is not always present in the
response body.
The API's LIKE search on default_qry_str is already exact. The secondary filter should
only correct structured dimensions (type, physical/virtual OR-logic) where the backend
uses AND logic that the frontend must compensate for. Text search is left to the API.
Root cause confirmed: STORED GENERATED columns in the event table needed a rebuild;
frontend filtering was masking results that the API returned correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
element_data_store fires its load trigger as soon as api_ready is true,
with no check for account_id. In the IDAA iframe flow, the outer layout
mounts before Novi UUID verification completes, so the footer fetch fires
with no x-account-id header and gets a 403.
Wrap the IDAA outer layout footer in {#if $ae_loc.account_id} so it only
loads once the member's identity is established. Apply the same guard to
the events layout header and footer for consistency.
Journals was already safe (data stores are inside the trusted_access gate).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Distinguish transient API failures (rate limits, server errors, network drops) from
real membership denials, so members see actionable messages instead of 'Access Denied.'
Layout: new verify_error_type state ('rate_limited' | 'api_error') surfaces a
yellow 'Identity Verification Unavailable' banner with three recovery options --
Try Again (no reload, clears latch), Clear Cache and Reload, and Full Reset.
Spinner now shows live status messages (e.g. 'High traffic - retrying in 10 seconds...').
Recovery meetings page: qry_error_detail distinguishes network drops (TypeError /
ERR_NETWORK_CHANGED) from server errors, showing specific guidance in the error UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The RE-SORT block after API revalidation only checked for 'name' (a legacy
sort mode removed when sort_modes was introduced). 'name_asc' and 'name_desc'
fell through to the else branch and were silently re-sorted chronologically
by tmp_sort_1, overriding the user's selection. Updated to match the fast-path
IDB sort logic which already handled all three modes correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update default qry__limit to 100 in idaa_loc
- Add 75 to limit_steps in recovery meetings query component
- Bump AE_IDAA_LOC_VERSION to 2 to apply changes to existing users
- Update IDAA documentation and TODO__Agents.md with SQL optimization task
- Mark implemented UI/UX ideas as done in documentation
Wrap the data store element in an accordion-style toggle. State persists
in idaa_loc (localStorage) so the user's preference survives page reloads.
Added ds_info_collapsed field to idaa_local_data_struct.recovery_meetings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace three sort chips with single cycling button (Last Updated → Name A→Z → Name Z→A → repeat)
- Add min-w-36 to sort button to prevent layout bounce between labels
- Move My Meetings chip to filter row (first position) alongside Virtual/In-Person
- Widen filter chips row from max-w-xl to max-w-2xl to match search bar
- Move sort/max/actions section inside <form> so all rows share the same width constraint
- Flatten span wrappers in bottom row — all buttons are direct flex children of justify-center container, fixing left-drift when conditional buttons are hidden
- Add keyed {#each (val)} to Type chip loop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Group labels (Location, Type, Sort, Max) moved to sr-only — visually hidden,
accessible to screen readers. Chips are self-descriptive without them.
- Max results chips replaced with a [−] 150 [+] stepper that steps through
predefined values [25, 50, 100, 150] (+ 200/500 for trusted_access). Minus/plus
buttons disable at the ends of the list. limit_steps and limit_idx computed as
$derived in script so onclick closures have access without @const gymnastics.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Max: 25 / 50 / 100 / 150 chips for all users; 200 / 500 visible to trusted_access
staff only (consistent with previous select behavior).
- Sort: Last Updated / Name A-Z / Name Z-A chips; clicking triggers the same
qry__order_by_li update and search_version bump as the old select onchange.
- Sort logic extracted into set_sort_mode() helper to keep onclick clean.
- Active state: preset-filled-tertiary-400-600; inactive: outlined + opacity-60.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ae_idaa_comp__event_obj_qry.svelte: replace Location checkboxes and Type radio
inputs with styled pill-chip buttons. Location chips (Virtual / In-Person) are
independent toggles; Type chips (All / IDAA / Caduceus / Family Recovery) are
mutually exclusive — clicking the active chip deselects back to All. Chips fire
the reactive search $effect directly via store updates; no explicit trigger needed.
Remote First dev toggle preserved in edit mode, now inline with filter chips.
- CLIENT__IDAA_and_customized_mods.md: update Recovery Meetings filter/sort docs,
add My Meetings / favorites section, correct idaa_loc and idaa_sess store schemas,
bump Last Verified date.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- +page.svelte: when search returns zero results and filters are active, show
"No meetings found for these filters" with a one-click "Clear all filters" button
instead of the bare no-results message. The 8s cache-reset escape hatch is
unchanged and still fires only when zero results appear with no filters set.
- [event_id]/+page.svelte: add star/favorites button to the detail page nav bar
alongside Back/Edit. Loads the same idaa_meetings_favorites data_store record
on mount; PATCHes the shared record on toggle. State is optimistic with rollback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Favorites are now stored in a dedicated data_store record (code:
idaa_meetings_favorites, scoped to the IDAA account_id) so toggling
never touches ae_event rows or their updated_on timestamps. Structure:
{ [novi_uuid]: [event_id, ...] }. Pre-created DB records for dev (id 150)
and live IDAA (id 151) accounts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Store Novi UUID list in event.mod_meetings_json.favorite so favorites
persist cross-browser without requiring Novi API write access. Optimistic
IDB update with API rollback on failure. Star button uses inline styles
to override Bootstrap v3 iframe CSS conflicts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: stale IDB records from prior deploys persisted indefinitely.
Fast path returned 0 (account_id mismatch), API errored silently, and the
error state showed the same message as a genuinely empty result — making
the failure indistinguishable from real data.
Fix is layered defense:
- Bump IDB_CONTENT_VERSIONS.events.event to 2 (one-time force-clear for all users)
- Add check_and_clear_idb_table() helper to store_versions.ts; wire it in
(idaa)/+layout.svelte to catch future version mismatches on session start
- One silent auto-retry (3s) on API failure before surfacing error UI
- Distinct error state (Unable to load meetings) separate from empty state
- Escape-hatch cache-reset button after 8s when zero results + no active filters
- Document root cause and fix in README.md and BOOTSTRAP__AI_Agent_Quickstart.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
$idaa_trig is a key_val object (dict of boolean flags), not a string signal.
Adds update_zoom_full_url key to the store template and converts all 7
string comparisons/assignments to property access on the object.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix download button to use hosted_file_id instead of id (which resolved
to event_file_id via _process_generic_props, hitting the wrong endpoint)
- Fix Dexie file table query in event_file_obj_tbl_wrapper to use _id
fields (the indexed ones) instead of _id_random variants
- Remove _random fields from properties_to_save in event, event_session
- Drop _id_random fallbacks from launcher device ID resolution and
background sync heartbeat
- Clean up dead comments and old FA anchor in post edit component
- Update TODO__Agents.md: BGH section removed, CMSC/Axonius shows added,
download button fix marked complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chromium's native audio player shadow DOM collapses when max-height is
applied to the <audio> element — audio plays but controls are invisible.
Remove the inline style (carried over from video context) and use w-full
max-w-lg instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Expose archive_content.code in edit form (trusted + edit_mode only)
- Add code to properties_to_save so it persists on every API load/save
- Add code field + index to Archive_Content Dexie interface (schema v2)
- Minor: center "Add" button rows in archive and content list components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
XHR upload % in the button + disabled states now communicate
upload/save progress; the top Saving.../Finished saving block
is no longer needed (and its out:fade was broken on re-entry).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restrict upload to one file (each archive content item maps to one file);
contextual toggle button text (Switch to Select / Switch to Upload);
swap FontAwesome upload icon for Lucide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
archive_obj.archive_id_random → .archive_id in load function and post-create
assignment; remove archive_id_random and hosted_file_id_random from editable
fields list — V3 returns the random string as the primary ID field directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ArchiveContentForm interface + factory for controlled input bindings
- obj_changed bindable prop wired to Cancel button visibility in parent page
- Split Save button: edit mode disables when clean, create mode always enabled
- Post-upload/select/remove syncs orig snapshot so file ops do not dirty the form
- Fix archive_content_id_random / archive_id_random → V3 field names in edit component
- Add missing file_extension field to ae_ArchiveContent type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Svelte 4 store nested property mutations don't call set()/update(), so
$effect on $idaa_slct never fired after upload. Replaced store property
binds with local $state variables that Svelte 5 proxies track natively.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use Jitsi url_params.uuid for exclusion where available, preserve url_params in cached activity logs, and add the temporary staff-name fallback behind the same edit-mode toggle.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add Novi UUID exclusion and known-meeting filtering, default the report date range to the last 60 days, and hide Room Name unless global edit mode is enabled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>