Source: codebase + UI audit conducted this session. Each item carries file:line pointers and a severity.
-
src/web/api.js:251— gatePATCH /todos/:idbehindrequireAdmin. Action-item toggle now admin-only; non-admin receives 403. -
src/pipeline/orchestrator.js:88—seedTodoscreatedAt =meeting.started_at(notnow()). Long transcription queues no longer drag action items onto "today" in the timeline. -
src/voice/meeting-manager.js:73-82— fail-loud.catchonfinalizeescape: logs stack, flipsprocessing→transcription_failed(only if stillprocessing), re-throws sodoneawaiters still see rejection. Resolves "stuck Processing card between restarts". -
src/pipeline/retry.js:73—seedTodoscreatedAt =meeting.started_at(consistency with orchestrator path). -
src/web/api.js:231— mergeseedTodoscreatedAt =target.started_at(same alignment). -
web/src/pages/ActionItems.jsx:69-83+:124-127— wrap toggle in try/catch; surfacetoggleErrbanner. Non-admin 403 path now shows "Only admins can edit action items." instead of uncaught rejection. -
web/src/pages/Search.jsx:1+:8-23—Fragmentimport added;highlightloops over all matches, not just first. -
src/store/db.js+src/pipeline/orchestrator.js— atomically replace meeting utterances and persist transcript completeness; failed replacement rolls back without leaving a partial transcript. -
src/pipeline/retry.js— retranscribe retained PCM for incomplete or legacy-unknown transcript state; known-complete transcripts still re-summarize without STT. -
docs/audit-2026-07-06.md— reconcile audit #7 with the actual transactional persistence and PCM-aware retry fix. - Repository DB hygiene verified —
*.db,*.db-shm, and*.db-walare already ignored, andgit log --all -- meetings.dbis clean.
Verified: npm test 270/270 pass. npm --prefix web run build 64 modules, 282 kB bundle, clean. Sidecar: 10/10 pass (one known Starlette deprecation warning).
-
src/pipeline/orchestrator.js:91-102— persistdelivered/deliveryErrorto the meetings table (audit #14, half-closed). Returned but never stored;/meetings/:idcan't surface "notes saved but not posted" or offer a re-post. Fix: schema migration addsdelivered/delivery_errorcolumns; orchestrator writes them; expose via/meetings/:id; addPOST /meetings/:id/repostreusingpostNotes. ~3 files:db.js,orchestrator.js,api.js; 1 UI line inReading.jsx. -
src/pipeline/retry.js:75— log delivery failures on the dashboard retry's resummarize path. Todaydeliver(...).catch(() => {})swallows with no log, while the orchestrator logs the same class (orchestrator.js:99). Pick one behavior. -
src/store/db.js— retention + VACUUM + scheduled sweep (audit #17).meetings/utterances/FTS grow unbounded for done meetings.reconcileOnBootruns once at process start only. AddpruneOlderThan(days)callable from asetIntervalinindex.js+ aPOST /system/pruneadmin route.
-
src/web/auth.js:141—requirePasswordChangedfails open whenreq.useris undefined. Mirrors pre-fix shape ofrequireAdmin(audit #22). Safe today becauseserver.js:26always mountsattachUserfirst, but a future bare-mount on the standalone server reintroduces the default-cred takeover surface. Fix: samereq.authResolvedgating asrequireAdmin. -
src/store/db.js:53—todos_dedupUNIQUE INDEX lets duplicates in whenmeeting_idis NULL (SQLite NULLs are distinct in UNIQUE indexes). EveryseedTodoscall today setsmeeting_id, so it never fires; add an explicit comment for future maintainers noting the invariant. -
src/pipeline/transcribe.js:30— round-robin worker assignment stalls on a hungconvert/stt. Minor at default concurrency (2/4); skip unless you scale up.
- Drop one of the duplicated reprocessing script pairs.
scripts/reprocess-meeting.mjs≡scripts/resummarize.js;scripts/retranscribe-meeting.mjs≡scripts/retranscribe.js.bot.js:77failure message points atreprocess-meeting.mjs; the dashboard Retry button (api.js:167) covers the same path. Pick one set, delete the other, updatebot.jsmessage to point at the dashboard primarily.
-
web/src/pages/Reading.jsx:205— delete-then-navigate to/meetingsleaves a stale list.Meetings.jsxuseEffectonly fires onguildIdchange. Fix requires a global meetings cache or a manual refresh signal — bigger refactor. -
web/src/GuildContext.jsx:11-15— fetches guilds once at mount. New guilds added in Discord after page load never appear until refresh (bot-controller emits no SSE). Audit #36 category. -
web/src/components/Markdown.jsx— single-pass line parser, no nested formatting (e.g. bold link inside a list item). LLM answers are simple; dependency-free intent preserved.react-markdownmigration optional. -
web/src/components/Layout.jsxGuildSwitcher — no loading vs empty distinction. Shows "No servers" both while loading and when truly empty. Minor cosmetic. -
web/src/pages/ForcePasswordChange.jsx:20— client-side min length hardcoded at 8. Matches serverPASSWORD_MINtoday; drift risk if server bumps the threshold. Tolerable for self-hosted. -
web/src/pages/Analytics.jsx— top-N hardcoded at 8. Could expose a "show all" toggle. Polish only. -
web/src/components/ui.jsx:117StatusPill— only knows 5 statuses. Unknown states (e.g.'empty') fall through to a generic "Unknown" pill. Today'empty'meetings get deleted bybot.js:65before they reach the UI, so this is theoretical.
-
npm test→ 270/270 pass. -
npm --prefix web run build→ clean, 64 modules. -
stt_sidecar/.venv/bin/python -m pytest stt_sidecar/test_server.py -q→ 10/10 pass; one known Starlette deprecation warning. - Manual smoke: start bot + sidecar, record a short meeting, confirm notes deliver; toggle an action item as admin (works) and as non-admin (403 + UI banner); retry a failed meeting from the dashboard.