Files
OSIT-AE-App-Svelte/documentation/TODO__Agents.md
Scott Idem c0f828ec2c feat: Add platform-specific VLC launch profiles (macOS working; Linux deferred)
- macOS: Uses direct binary path (/Applications/VLC.app/Contents/MacOS/VLC)
  with --no-play-and-exit --play-and-pause flags and AppleScript fullscreen toggle.
  Confirmed working: pause on last frame, stays in window.

- Linux: Uses shell command (vlc --no-play-and-exit --play-and-pause).
  Known issue: not going fullscreen, not pausing on end. Deferred for later
  investigation of flag interpretation vs launcher execution layer.

- Created separate profile constants: VLC_MIRROR_MAC_PROFILE and
  VLC_MIRROR_LINUX_PROFILE in ae_launcher__default_launch_profiles.ts

- Updated TODO__Agents.md with Linux VLC issue for future debugging

Files: ae_launcher__default_launch_profiles.ts, documentation/TODO__Agents.md
2026-05-20 19:09:15 -04:00

427 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Frontend Agent Task List
> Use this file to track steps for complex features or bug fixes.
> **Status:** Stable — ongoing development.
## 🔴 CMSC Charlotte — May 27 (Presentation Management)
**Drive down:** May 25 | **Setup:** May 26 morning | **Show:** May 27+
**[Electron/Launcher] Clean up presentation file launch profiles** — BGH show revealed issues
with the profiles used to open/launch presentation files. Architecture decision: move launch
logic to the Svelte side so it can be changed without an Electron rebuild. Electron becomes a
thin OS primitive layer; all business logic lives in Svelte and device config.
Why this matters: the profile map is policy, while the native template is the exact runtime
command. Keeping those separate avoids a second hidden source of truth and keeps Electron from
guessing defaults.
**Electron groundwork (2026-05-11) — DONE:**
- [x] `run_osascript` hardened — temp `.scpt` file approach; handles multi-line + special chars
- [x] `native:copy-from-cache-to-temp` primitive added — copy to tmp, caller decides launch
- [x] `native:launch-from-cache` executes a provided `native_template` string — AppleScript or `shell:` prefix; no Electron-side fallback
- [x] `get_launch_profile()` in `launcher_file_cont.svelte` reads from device config then event config; resolves to a `native_template` string and passes it to `launch_from_cache`
- [x] Built-in Launcher defaults refactored into canonical profile names plus extension aliases (`ae_launcher__default_launch_profiles.ts`)
- [x] Device-level Launch Timing section added under Launcher Configuration → Device, with per-profile `launch_profiles[profile].post_delay_ms` overrides
- [x] **URL file launch support (2026-05-13)**`event_file.extension = 'URL'` (or filename starting with `http://`/`https://`) is treated as a non-downloaded URL. Background sync skips URL files so they are never treated as cacheable hosted files. Shared `AE_Comp_Hosted_Files_Download_Button` now hard-bypasses the download path for URL records. In native mode, `handle_open_file()` routes to `native.open_external({ url, app: 'chrome' })` with `'default'` fallback. Health section crash fixed (guarded `native_device` against undefined in preview/edit mode).
- [x] **[Launcher/Electron] Display mirroring auto-detection (2026-05-20)** — `native:set-display-layout` rewrote to auto-detect displays via `displayplacer list` when no `configStr` provided. Old code silently returned `{ success: false }` (swallowed by `.catch(() => {})` in relay). Now parses `displayplacer list` output, extracts quoted display strings, builds correct mirror/extend commands. Manual `configStr` still takes priority when provided.
**Svelte-side migration — remaining before May 26:**
- [x] **[Launcher] Built-in Svelte default profiles (2026-05-11)** — canonical profile constants live in `ae_launcher__default_launch_profiles.ts` with extension aliases and a `resolve_launch_profile()` 3-step fallback (device config → event config → built-in defaults). Covers macOS (`pptx`, `ppt`, `key`, `odp`, `pdf`), media (`mp4`, `mkv`, `mp3`, etc.), Windows/Parallels variants, and URL path.
- [ ] **[Launcher] Composable open flow** — refactor `handle_open_file()` to use
`copy_from_cache_to_temp` + `run_osascript` / `run_cmd` directly instead of the all-in-one
`launch_from_cache`. Finer error handling at each step (verify copy succeeded before
attempting script; surface failure clearly in UI).
- [x] **[Launcher] Error handling + fallback (2026-05-14)** — post-script failure is non-fatal: surfaces `'fallback'` status with error detail (file already open). If `open_cmd` itself fails, falls back to `open_local_file_v2` (OS default); surfaces combined error detail if that also fails. UI renders `'open'` / `'fallback'` / `'error'` states distinctly.
- [ ] **[Launcher] Slide control scripts in Svelte config** — `control_presentation` AppleScript
one-liners are hardcoded in Electron (`shell_handlers.ts` lines 149159). The Svelte side
(`launcher_cfg_native_os.svelte`) already calls `native.control_presentation({ app, action })`
with next/prev/start/stop buttons wired and working. The Electron IPC call is stable enough
for current shows.
**Remaining (post June 10):** Move scripts to device config (`data_json.control_scripts`) or
Svelte constants so behavior can be changed without an Electron rebuild. Replace
`native.control_presentation()` call with `native.run_osascript(script)` using the
config-resolved script string. The `run_osascript` primitive is already in place; this is
purely a wiring/config task. Low priority — hardcoded scripts work fine for PowerPoint and
Keynote for now.
- [ ] **[Launcher] `kill_processes` target list in config** — The `kill_processes` IPC primitive
exists in Electron and is exposed via `electron_relay.ts`, but **is never called from anywhere
in Svelte** — no routes, no config components, no file launch flow. No UI exists for it yet.
**What needs doing (post June 10):**
- Decide where the "kill on cleanup" action lives: end of `handle_open_file()` (auto), or a
manual "Kill Apps" button in Native OS config, or both.
- Process names should come from device config (`data_json.kill_process_li`) with a built-in
fallback list (e.g. `['Microsoft PowerPoint', 'Keynote', 'LibreOffice Impress']`).
- The Native OS config section (`launcher_cfg_native_os.svelte`) is the natural home for a
manual kill button; auto-cleanup on file open would go in `launcher_file_cont.svelte`.
**Device tab length note:** The Device tab now has 6 collapsible sections (Sync Timers, Health,
Native OS, Wallpaper, Launch Timing, Updates). Consider moving Launch Timing + a future Kill
Apps control into a dedicated **"Presentation"** section or a fourth tab to keep Device focused
on machine/OS concerns rather than per-app launch behavior.
- [x] **[Launcher] Launcher config UI — launch_profiles editor (2026-05-14)** — Launch Timing section in `launcher_cfg_launch_timing.svelte` exposes per-profile `post_delay_ms` overrides with Save/Reset per row. PATCHes `event_device.other_json.launcher.launch_profiles` via V3 CRUD. Wired into `launcher_cfg.svelte`. Shown under Technical Mode (`$ae_loc.edit_mode`).
- [ ] **[Launcher] End-to-end test on macOS** — test pptx and key opens on a real podium Mac
before May 26 setup day. Verify: file copies to tmp correctly, script fires, app opens in
slideshow mode, error fallback works.
- [ ] **[Launcher/Electron] Wallpaper stops applying after several changes (post-CMSC)** —
After setting the wallpaper 35 times in a session, macOS silently ignores further `set desktop
picture` calls even though the SvelteKit side reports "Saved & Applied ✓". Restore Default
(`restore_macos_default_wallpaper`) immediately unblocks it; closing/reopening Electron does
not. **Workaround:** use Restore Default, then re-apply. **Root cause:** macOS caches the
current wallpaper path and skips the AppleScript call when the downloaded file lands at the
same temp path. **Fix (post-CMSC):** in the Electron `set_wallpaper` handler
(`aether_app_native_electron`), append a timestamp or random suffix to the temp filename on
every download so macOS always sees a new path (e.g. `wallpaper_1748123456.jpg` instead of
`wallpaper.jpg`).
- [ ] **[Launcher/Electron] `run_cmd`/`run_cmd_sync` — phantom `return_stdout` param (low priority)** —
`electron_relay.ts` passes `return_stdout` in the args object, but both IPC handlers ignore it
(stdout is always returned). Effectively a no-op, but creates a misleading API contract. Fix:
remove the param from relay calls, or honor it in the handlers. No behavioral impact currently.
- [ ] **[Launcher/Electron] `power_control` — sudo requirement not surfaced in UI (low priority)** —
`shutdown` and `reboot` require `sudo` on Linux. macOS works without it; Launcher only targets
macOS so this is a non-issue in production. If Linux podiums are ever deployed, `power_control`
shutdown/reboot will fail silently with a permissions error. No fix needed before CMSC.
- [ ] **[Launcher/Electron] `manage_recording` — aperture binary path on packaged builds** —
Handler resolves the aperture binary via a dev-relative path. Needs verification that the path
resolves correctly inside a packaged `.app` bundle (`app.asar` / `resources/`). Not blocking
for CMSC (recording not part of CMSC workflow). Verify on next packaged build test.
---
## 🔴 Axonius DC — June 9 (Badge Printing)
**Setup/Registration:** June 8 | **Show:** June 9
- [ ] **[Badges] Epson C3500 fanfold badge layout** — Axonius is using Epson C3500 printers
with fanfold (continuous) badge stock. Create/configure a fanfold badge layout compatible
with the C3500 format. Must be ready before the June 8 setup/registration day.
---
## 🚧 Upcoming High Priority
### ~~[IDAA] Random "Access Denied" — Root Cause Review & Fixes~~ ✅ Resolved (2026-05-19)
All known root causes fixed across 10+ commits to `src/routes/idaa/(idaa)/+layout.svelte`.
Deploying as of 2026-05-19. Monitor for further member reports.
#### All fixes applied
- [x] **Server-side Novi verification migrated (key fix for hotel/VPN/Cloudflare-filtered networks)**
`verify_novi_uuid()` now calls `GET /v3/action/idaa/novi_member/{uuid}` through the Aether
backend (server-to-server, Redis-cached 4h) instead of making a browser-to-Novi call.
Eliminates false Access Denied for members on hotel/conference WiFi, VPNs, and corporate
networks where the member's IP was rejected by Novi/Cloudflare.
Frontend: `(idaa)/+layout.svelte` | Backend: FastAPI `aether_api_fastapi` | Docs: `GUIDE__AE_API_V3_for_Frontend.md` §12, `CLIENT__IDAA_and_customized_mods.md`
- [x] Novi TTL extended to **12 hours** (5 min → 45 min → 12 h) — covers a full conference day
- [x] Access Denied on iframe reload (sessionStorage URL preservation) — `2855e091f`
- [x] TTL cache bypassed when `$ae_loc` auth flags reset — `2855e091f`
- [x] "Verification Unavailable" screen distinct from "Access Denied" — `2855e091f`
- [x] "Try Again" without page reload (`retry_count` pattern) — `2855e091f`
- [x] 12 s AbortController hard timeout on Novi fetch — `e921ca973`
- [x] Network/AbortError gets 3 s grace + one retry — `e921ca973`
- [x] Clear Cache & Reload added to Access Denied state (iframe mode) — `2855e091f`
- [x] `VERIFY_TIMEOUT_MS` 8 s → 35 s (was firing mid-retry, causing premature Reset clicks) — `53fd5e7de`
- [x] `sessionStorage` try-catch (iOS Safari Private Browsing throws on access) — `53fd5e7de`
- [x] Appshell stores guarded behind `account_id``8850db89c`
- [x] Recovery meetings over-filtering bug (API `default_qry_str`) — `76e21b08f`
- [x] A→Z sort in recovery meetings API revalidation path — `c0386f27b`
- [x] `events.event` IDB content version bump (stale cache) — previous commit
#### Root layout SWR verified safe:
The root `+layout.ts` builds `ae_loc_init` as a plain site-config object (no `authenticated_access`,
`trusted_access`, or `access_type` fields). The root layout sync effect
`$ae_loc = { ...current_loc, ...ae_acct.loc }` therefore cannot overwrite Novi-set auth flags.
Confirmed safe — this is NOT a cause of Access Denied.
#### Remaining architectural note:
The long-term fix for the coarse `$ae_loc` reactivity (Svelte 4 store) causing Effect 2 to
re-run on unrelated writes is tracked under **[Stores] Svelte 4 → Svelte 5 State Migration**
below. The TTL + `verify_in_flight` guards are the current mitigation.
---
### ~~[IDAA] Server-side Novi verification — 503 not auto-retried~~ ✅ Fixed (2026-05-20)
---
### [Launcher/VLC] Linux playback — fullscreen + pause-on-end not working
**Status:** Mac ✅ working perfectly; Linux 🚧 deferred for later investigation
**Date discovered:** 2026-05-20
macOS VLC profile (direct binary path) successfully:
- Opens VLC with the media file
- Plays + pauses on the last frame (instead of returning to playlist)
- Fullscreen toggle works (Cmd+F via AppleScript post-script)
Linux VLC command (`vlc --no-play-and-exit --play-and-pause "{{path}}"`) currently:
- Does NOT go fullscreen
- Does NOT pause on the last frame (plays through, returns to playlist)
**Current state:** Both macOS and Linux commands in `ae_launcher__default_launch_profiles.ts`.
macOS is the primary venue deployment platform; Linux support is nice-to-have.
**Investigation needed:** Determine if the VLC flags are being interpreted on Linux,
or if there's a launcher execution layer issue (e.g. `shell:` prefix handling).
File: `src/lib/ae_events/ae_launcher__default_launch_profiles.ts``make_vlc_mirror_linux_profile()`.
---
### [Stores] Svelte 4 → Svelte 5 State Migration (prerequisite for Phase 2c)
The app uses `svelte-persisted-store` (Svelte 4 store contract) for all core persisted state
(`ae_loc`, `idaa_loc`, `ae_api`, `ae_sess`, etc.). In Svelte 5 `$effect`, reading **any field**
of a Svelte 4 store subscribes to the **entire store** — coarse-grained reactivity. This is the
root cause of the IDAA Novi re-auth bug (2026-03-30): unrelated `$ae_loc` writes (e.g. iframe
height, SWR cfg reload) triggered the Novi verification effect repeatedly.
Migration target: replace `svelte-persisted-store` with Svelte 5 `$state`-based persistence
(e.g. `runed` `PersistedState`, or a lightweight custom wrapper). This gives fine-grained
reactivity — only effects that actually read a changed field re-run.
**Phased approach (do NOT do all at once):**
- [ ] **Phase A — Project plan + wrapper decision:** Write `PROJECT__Stores_Svelte5_Migration.md`.
Decide: `runed` library vs. custom `$state` + localStorage wrapper. Audit all store consumers.
Identify stores in priority order. Estimate blast radius per store.
- [ ] **Phase B — Core auth stores (highest impact, start here):**
- `ae_loc` (persisted) — auth flags, site cfg, UI state; ~471 consumer sites across 150+ files
- `idaa_loc` (persisted) — Novi auth, IDAA query prefs
These two cause the most reactive noise. Migrating them also unlocks Phase 2c (separate `ae_auth`
store) since the callsite sweep is now required anyway.
- [ ] **Phase C — Remaining persisted stores:**
- `ae_api` (persisted) — API config / JWT
- `ae_events_stores` persisted entries (badges, launcher, leads, pres_mgmt loc stores)
- [ ] **Phase D — Non-persisted writable stores:**
- `ae_sess`, `idaa_sess`, `slct`, `slct_trigger`, `ae_auth_error`, `ae_trig`, `ae_snip`, etc.
- Lower urgency (no localStorage churn), but fine-grained reactivity still beneficial.
- [ ] **Phase E — Phase 2c (unblocked after B):** Split `ae_loc` into `ae_auth` + `ae_app`
(see entry below — ~471 callsites, but sweep is cheap once already touching every consumer).
**Project plan doc needed:** Yes — scope is app-wide. Do NOT start Phase B without Phase A.
---
### [Stores] IDB Content Version System
Scaffolded in `store_versions.ts` (`IDB_CONTENT_VERSIONS` constant + `check_and_clear_idb_table()`
helper) and `core__idb_dexie.ts` (`check_and_clear_idb_tables()` batch helper). Mirrors
`AE_LOC_VERSION` but targets Dexie table contents rather than localStorage keys.
**Currently active:** `journals.journal_entry` (db_journals.ts), `events.event` (IDAA layout).
All other tables are defined but not yet wired.
**Real-world impact:** Stale IDB records from a `properties_to_save` change were the root cause
of the IDAA Recovery Meetings "no meetings found" bug — a ~1-year unresolved issue (20252026).
Fixed 2026-05-16 by wiring `events.event` into the IDAA layout and bumping its version to 2.
See `BOOTSTRAP__AI_Agent_Quickstart.md` mistake #13 for the full postmortem.
**How it works:**
- `check_and_clear_idb_table(db_table, 'module', 'table')` reads a localStorage key with the
expected version from `IDB_CONTENT_VERSIONS`
- On mismatch (or missing key), the Dexie table is cleared and the key is updated
- SWR repopulates from API on next access — no explicit reload needed
- Cost on version match: one `localStorage.getItem()` — effectively free
- Bump a table's version in `IDB_CONTENT_VERSIONS` when `properties_to_save` changes shape
**IDAA consideration:**
IDAA tables are already cleared by `indexedDB.deleteDatabase()` on sign-out/auth failure in
`(idaa)/+layout.svelte`. The content version check is a *complementary* deploy-time reset, not
a replacement.
**Tasks:**
- [x] Write `check_and_clear_idb_tables()` helper in `core__idb_dexie.ts` (2026-05-14)
- [x] Wire helper into `db_journals.ts` (pilot — `journal_entry: 2` cleared stale content_md_html) (2026-05-14)
- [x] Wire `events.event` into IDAA layout `(idaa)/+layout.svelte` + bump version to 2 (2026-05-16)
- [ ] Roll out to `db_events.ts` (module-wide: session, presenter, badge, device, location, file)
- [ ] Roll out to `db_core.ts` (site_domain, person, user)
- [ ] Roll out to IDAA modules (`db_posts.ts`, `db_archives.ts`) — verify auth-wipe interaction first
- [ ] Consolidate the two `check_and_clear_idb_table*` helpers (single-table in `store_versions.ts`, batch in `core__idb_dexie.ts`)
### [Stores] Refactor — Phase 2c (deferred)
Phases 1, 2a, 2b are complete (see ✅ Completed below). One phase remaining:
- [ ] **Phase 2c — Actual separate stores (`ae_auth`, `ae_app`):** Requires touching ~471
`$ae_loc.*` auth-field read sites across 150+ files. Deferred until a Svelte runes migration
of the store layer itself (touching every component anyway makes the callsite sweep cheap).
### [TypeScript] svelte-check hidden errors — discovered 2026-03-27
**HOW WE FOUND THIS:** The `@lucide/svelte` 0.577.0 update (2026-03-10) dropped `class` from
`IconProps`. Fixing it required a `declare module '@lucide/svelte'` augmentation. That
augmentation was mistakenly placed in `app.d.ts`, which is a *script-context* declaration file
(no `export {}`). In that context, `declare module` is an **ambient replacement**, not a merge —
it wiped all icon exports from svelte-check's view, surfacing 1368 previously hidden errors.
Once moved to `src/lucide-augment.d.ts` (a proper module file with `export {}`), the masking
lifted and the real pre-existing errors became visible.
**Lesson:** A broken ambient declaration can silently hide unrelated errors. If svelte-check
suddenly jumps to 0 errors, verify it's not because a bad `.d.ts` replaced a package's types.
**Current state (2026-03-31):** 32 errors, 0 warnings — all `ModalProps.children`.
- [ ] **[flowbite-svelte] `ModalProps.children` — 31 errors across 26 files.** The flowbite-svelte
`Modal` component API changed; `children` is no longer a direct prop (now Svelte snippet-based).
Affected files span journals, pres_mgmt, events/settings, and IDAA archives.
Run `npx svelte-check 2>&1 | grep ModalProps` to get the current list.
Fix pattern: replace `children` prop binding with Svelte snippet syntax per flowbite-svelte docs.
### [Journals] Journal Entry Config follow-ups
- [ ] **[Journals] Visibility / audience toggle contrast** — the flag buttons need a clearer
selected state in both light and dark mode.
- [ ] **[Journals] Footer button style** — the actual `Done` button should read like a real button,
not a seamless footer spacer.
- [ ] **[Journals] Entry passcode secondary auth** — `passcode_hash` stores a hash; compare the
entered passcode hash to the stored hash, gate entry loading, and honor the TTL-based access
window. This is secondary entry auth, not a plain-text passcode field.
- [ ] **[Journals] Summary AI shortcut** — add an AI summarize button next to Entry Details
Summary so staff can generate a summary directly from the modal.
- [ ] **[Journals] Archive On sizing** — constrain the Archive On control to a reasonable width
instead of letting it expand to full width.
- [ ] **[Journals] Archive On behavior** — define what Archive On actually means and wire the
behavior; it is currently just a UI field with no live effect.
- [x] **[IDAA] Do not cache IDAA data in IDB when access is denied (2026-04-19, audited 2026-04-28)**
Full audit confirmed all protection layers are in place. No code changes required.
- All `+page.ts` / `+layout.ts` under `src/routes/idaa/` are clean — no SWR loads run before auth resolves.
- All `$effect` SWR calls in IDAA `+page.svelte` files are gated on `$idaa_loc.novi_verified || $ae_loc.trusted_access`.
- `(idaa)/+layout.svelte` purges `db_posts`, `db_archives`, `db_events` on auth failure, no-UUID/no-session, and inconsistent state.
- `sign_out()` calls `indexedDB.deleteDatabase()` on all IDAA databases.
- API 401/403 responses fail-fast in `api_get_object.ts` (throw before any IDB write).
- `idaa_trig` is in-memory `writable()` only — cannot carry stale trigger state across sessions.
- `$effect` auth guards in IDAA page components are reactivity guards (prevent spurious SWR calls on coarse `$ae_loc` writes), NOT auth-bypass guards. SvelteKit layout hierarchy already prevents child components from mounting when `(idaa)/+layout.svelte` blocks rendering.
- Doc: SvelteKit layout hierarchy security model captured in `GUIDE__SvelteKit2_Svelte5_DexieJS.md` and `BOOTSTRAP__AI_Agent_Quickstart.md` (Mistake #7).
- [ ] **[IDAA] IDB fast-path contact search — Recovery Meetings (2026-04-08, updated 2026-05-19)**
**API path is now working**`default_qry_str` already includes contact name/email.
Two bugs were fixed 2026-05-19: (1) STORED GENERATED columns had stale values; forced
rebuild via fake updates. (2) Frontend secondary filter was re-checking text against
response fields, silently dropping API results that matched only via `default_qry_str`.
**Remaining gap — IDB fast-path only:** The local cache fast-path returns all cached meetings
without text filtering; users see the unfiltered list first, then the API-filtered result
replaces it. To make contact matches appear instantly from cache:
- `src/lib/ae_events/ae_events__event.ts` → fast-path filter in `search__event()`: parse
`contact_li_json` and include contact names/emails in the local text match.
- `src/routes/idaa/(idaa)/recovery_meetings/+page.svelte` fast-path display: same filter.
Backend enhancement (`contact_li_json_ext` whitelist) is not required for this — the IDB
records already store `contact_li_json` raw JSON which can be parsed client-side.
- [ ] **[IDAA] Optimize Recovery Meetings SQL VIEW and indexes.**
The current search query can be taxing on the server. With ~150 active meetings, the view
logic and supporting indexes need a performance review to ensure fast responses as the
database grows. (Requested 2026-05-18)
- [ ] **[IDAA / Events] Audit `default_qry_str` coverage in other event search pages.**
The backend was updated 2026-03-31 to expose `default_qry_str` in API responses.
Frontend fix applied to Recovery Meetings (`+page.svelte` + `properties_to_save`).
Check all other event search pages that use `db_events.event.filter()` or a secondary
post-API text filter — they may have the same mismatch (local searches `name`/`description`
only while server uses `default_qry_str`). Start with: any route under `/events/` or `/idaa/`
that has a full-text search input.
### [IDAA] Jitsi config editor + live site fix
- [ ] **Fix live site (id=17) `jitsi_token_endpoint` pointing to dev-api:** DB has
`https://dev-api.oneskyit.com/api/jitsi_token` for both site 10 and site 17 (IDAA live).
Need to update site 17 in **production** to `https://api.oneskyit.com/api/jitsi_token`.
SQL: `UPDATE site SET cfg_json = JSON_SET(cfg_json, '$.jitsi_token_endpoint', 'https://api.oneskyit.com/api/jitsi_token') WHERE id = 17;`
- [ ] **Add IDAA Jitsi config editor UI** to the jitsi_reports page (administrator_access only),
alongside the existing Jitsi URL Builder section. Should allow editing key fields in
`site_cfg_json` without needing phpMyAdmin:
- `jitsi_token_endpoint` — the JWT signing endpoint (needs to point to prod)
- Jitsi domain default (currently hardcoded as `jitsi.dgrzone.com` fallback in the page)
- `novi_jitsi_mod_li` — list of Novi UUIDs who get moderator privileges
Read from `$ae_loc.site_cfg_json`, PATCH the site record via V3 CRUD
(`PATCH /v3/crud/site/{id}/`), reload `$ae_loc.site_cfg_json` on save so it takes
effect without re-login.
### [IDAA] Jitsi Reports still incomplete
- [x] **Finish Jitsi Reports filters** — added Novi UUID exclusion plus meeting-name whitelist
filtering, with room-level unique counts based on Novi UUID when present. (2026-05-06)
### ~~[PWA] Service worker ignoring `chrome-extension://` requests~~ ✅ Fixed (2026-05-14)
Guard added to `src/service-worker.js` fetch handler: `if (!event.request.url.startsWith('http')) return;`
Also skips cross-origin requests entirely (origin check). No console errors from extension URLs.
### [CSS] Global placeholder text color — too dark in light mode
Placeholder text inherits full input text color in light mode (Tailwind CSS default), making
placeholders indistinguishable from filled-in values. Most visible in badge print controls
where placeholders show the actual badge value (e.g. "John Smith").
Workaround: scoped `::placeholder` rule added to `ae_comp__badge_print_controls.svelte`
(gray-400 light / gray-500 dark) — `commit 7733ef8`.
**Long-term fix:** Add a global rule to the main CSS (e.g. `src/app.css` or a theme file):
```css
::placeholder {
color: #9ca3af; /* gray-400 */
opacity: 1; /* overrides Firefox's 0.54 default */
}
.dark ::placeholder {
color: #6b7280; /* gray-500 */
}
```
Once the global rule is in place, remove the scoped workaround from the badge controls.
### [Backend/DevOps] Re-add `Access-Control-Allow-Private-Network: true` CORS header
Chrome's Private Network Access (PNA) policy blocks public-origin iframes from fetching
private-network addresses. Symptom: when `dev-api.oneskyit.com` resolves to a LAN IP
(testing from home), Chrome blocks the site domain lookup → ghost account → `site_cfg_json`
never loads → `novi_idaa_api_key` is null → IDAA Novi verifier spins forever → timeout banner.
Firefox unaffected. Production unaffected (public IPs only).
- [ ] **Re-add PNA header to API CORS config**`dev-api` Nginx or FastAPI CORS middleware
must respond with `Access-Control-Allow-Private-Network: true` when Chrome sends
`Access-Control-Request-Private-Network: true` in the preflight. This was fixed ~1 month
ago and regressed. Check Nginx site config and FastAPI `CORSMiddleware` settings.
Low urgency (dev-only, Firefox workaround available), but blocks home-network iframe testing.
### [DevOps] Remaining deployment items
- [ ] **Simplify Dockerfile env file selection** — Currently the Dockerfile uses a `BUILD_MODE` arg to
select between `.env.dev`, `.env.test`, `.env.prod` during the Docker build. This is unnecessary
complexity: each server (test Linode, prod Linode, workstation) only ever runs one environment, so
there will only ever be one env file present in that server's app directory.
**The fix:** Each server's app dir (`/srv/apps/test_aether_app_sveltekit/`, etc.) should have a
plain `.env` file (gitignored, placed manually during server setup). The Dockerfile should just
`COPY . .` and `cp .env .env.runtime` unconditionally — no `if prod / elif test / else dev`
branching for env file selection.
**What this changes:**
- `aether_app_sveltekit/Dockerfile` — remove the `BUILD_MODE`-driven `cp` block; always use `.env`
- Each Linode app dir gets a plain `.env` instead of `.env.test` / `.env.prod`
- Workstation keeps `.env.local` (for `npm run dev`) and `.env.dev` (for `build:docker:dev`) —
those stay as-is since they legitimately coexist locally
- `BUILD_MODE` arg can stay if needed for other build differences; just stop using it to pick the env file
- Update `.gitignore` in sveltekit to un-ignore `.env.test` / remove stale entries if desired
Low risk but unnecessary churn — defer until after the next active show.
- [ ] **Branch strategy cleanup:** All environments (test, prod, bak) currently pull from the same
branches. `deploy.sh` defaults are `ae_app_3x_llm` / `development` — acceptable for now but
should establish proper branch separation (e.g. `main`/`master` for prod).
- [ ] **Tier 2 deploy (Gitea webhook):** Push-triggered deploys via Gitea webhook → listener on
Linode → `deploy.sh`. Deferred until Gitea usage is more established.
### [Files] `db_events.file.clear()` on upload clears all cached files (2026-04-22)
In `ae_comp__event_files_upload.svelte` line 114, `db_events.file.clear()` wipes the entire
`file` Dexie table, not just files for the current session/presenter. Normally harmless (the
reload right after repopulates), but if multiple sessions' file lists are open simultaneously
they'd briefly flash empty. Low priority — only noticeable in multi-panel workflows.
### [General]
- **Input Field Audit:** Several input fields are missing `name`/`id` attributes or `data-testid`. Known examples: badge override fields in `ae_comp__badge_obj_view.svelte`; template name input in `ae_comp__badge_template_form.svelte`. Matters for: accessibility, autofill, label associations, and test targeting. (For tests, use `getByLabel()` rather than `input[value*=...]` which only checks the HTML attribute, not the Svelte-bound DOM property.)
## ✅ Completed (2026-04)
## ✅ Completed (archived)
See the full completed history in:
[documentation/archive/TODO__Agents__ARCHIVE_2026-03.md](documentation/archive/TODO__Agents__ARCHIVE_2026-03.md)
[documentation/archive/TODO__Agents__ARCHIVE_2026-04.md](documentation/archive/TODO__Agents__ARCHIVE_2026-04.md)