19 KiB
Slack Webview Parity — QA Matrix
Issue: #1016 Branch:
feat/1016-slack-parity-auditTester: oxoxDev Build: main @b11b8f33+feat/1016-slack-parity-auditHEAD Date: 2026-04-29 Method: manual smoke againstpnpm dev:appon macOS (perfeedback_validation_test_target.md)
Verdict legend
- ✅ pass — feature works as native app
- ⚠️ partial — works but with limitation; needs follow-up
- ❌ fail — broken; child issue filed
- 🔍 needs investigation — non-deterministic behavior; revisit
- ⏭️ skipped — could not test (env / dependency missing)
Acceptance criteria audit
| # | Criterion | Verdict | Evidence | Notes / child issue |
|---|---|---|---|---|
| 1 | Auth — Google SSO, email/password, SAML SSO; session persists across app restarts | ✅ pass (login) / 🔍 (restart) | Vezures workspace loaded, signed-in as Nikhil Bajaj. Restart-persistence untestable in dev:app due to dev-mode restart-loop hack (separate child issue) | Login flow works through in-app webview. SAML not tested (no SAML org). Restart re-auth needs to be validated in a packaged .app. |
| 2 | Messaging — channels, DMs, group DMs; threads | ✅ pass | User confirmed send-receive works in DMs/channels. Threads navigable. UI parity matches web Slack. | Memory-side extraction blocked by #7 (scanner doesn't run); will re-verify post-fix. |
| 3 | Reactions & emoji — picker opens; reactions post correctly | ✅ pass | User confirmed reactions + threads work end-to-end. UI render confirmed in screenshot (🙏 1, 👍 1). |
Pre-audit "extraction missing" claim DEFERRED — unverifiable until #7 fix lands and scanner actually runs. Will re-recall memories post-fix; if reactions show in memory docs, the static audit was wrong (like #4 was). Only if missing post-#7-fix do we touch extract.rs. |
| 4 | File sharing — upload, download; image previews | ✅ pass | User confirmed upload/download/preview all work. | Pre-audit "allowed_hosts mismatch" was a false alarm: slackb.com IS the working CDN host. Spec's slack-imgs.com + slack-files.com are stale — DO NOT change webview_accounts/mod.rs:101. Drop that fix from the plan. |
| 5 | Huddles — popup spawn (about:blank → huddle URL whitelisted); audio/video; popup cleanup on end | ✅ pass (paint lifecycle fixed) | Original symptom: popup briefly rendered "name + voice + camera tile" then went white after ~1s. Root cause turned out to be embedder-side (NOT a CEF runtime gap, as initially feared): tauri-cef's BrowserLifeSpanHandler::on_after_created scheduled a 1s blank-page guard using self.initial_url. CEF passes the opener's CefClient through on_before_popup unless the embedder overrides it, so the handler also fires for popup browsers — and self.initial_url is the OPENER's URL, never the popup's. The guard then saw the popup's about:blank URL (legit — Slack uses popup.document.write(html), no navigation) and called frame.load_url(parent_initial_url), wiping Slack's document-written huddle content. Fix: skip the blank-page guard when browser.is_popup() != 0. Verified working — popup stays interactive for full huddle lifetime. |
Fixed in tinyhumansai/tauri-cef#12 + this PR (submodule pin bump). Closes #1079. The earlier #1074 work (media perms grant + slack:// deep-link isolation, see "Fixes shipped in this PR" below) is still required — the popup-paint fix unlocks getUserMedia/getDisplayMedia in the popup, but those require the perms grant from row E. |
| 6 | Notifications — native OS notifications; per-channel mute; DND; notification_settings toggle |
❌ fail | User received Slack DM from Shanu while NOT focused on OpenHuman. macOS Notifications perm granted for OpenHuman.app. Result: no native toast fired. Log shows zero forward_native_notification / webview_notification events at message-arrival time. CEF shim installed shims browser_id=3 origin=https://app.slack.com/client was registered, but page-side new Notification(...) never reached Rust handler. |
CHILD ISSUE TO FILE: title [Bug] webview/slack: native OS notifications never fire — page→Rust bridge broken. Repro: signed-in Slack workspace, OpenHuman backgrounded, peer sends DM → no toast. Hypothesis: (a) Slack web suppresses Notification when its own permission state for the origin isn't "granted", or (b) CEF Notification shim wrap doesn't actually call forward_native_notification. Need to verify CEF Notification.permission value at slack.com origin and confirm the constructor wrap path. Pre-audit gaps (per-channel mute missing; default=true) are still valid but moot until basic toast path works. |
| 7 | Memory ingestion — IDB scanner; memory_doc_ingest posts; current behavior groups by channel_id (per-day grouping deferred) |
❌ fail (scanner never spawns) | RPC call openhuman.memory_recall_memories {"namespace":"slack-web:29da7de6...","limit":20} returns {memories:[]}. Log shows: [webview-accounts] slack ScannerRegistry not in app state immediately after Slack account opens. Zero memory_doc_ingest events ever fired. Root cause: app/src-tauri/src/lib.rs:998 does manage(std::sync::Arc::new(slack_scanner::ScannerRegistry::new())) — but slack_scanner::ScannerRegistry::new() already returns Arc<Self> (mod.rs:744-746). Result: managed state is Arc<Arc<ScannerRegistry>>. Lookup at webview_accounts/mod.rs:1751 is try_state::<Arc<ScannerRegistry>>() → returns None → scanner never spawns. WhatsApp/Discord/Telegram lines (997/999/1000) correctly use the bare ScannerRegistry::new() (no extra Arc wrap). Slack alone is double-wrapped. |
ONE-LINE FIX. Change lib.rs:998 from .manage(std::sync::Arc::new(slack_scanner::ScannerRegistry::new())) to .manage(slack_scanner::ScannerRegistry::new()). Post-fix: confirmed scanner ingests one doc per channel (emit_and_persist at mod.rs:230 explicitly groups by channel_id only — no per-day split); the (channel_id, day) shape from the original spec remains a deferred follow-up rather than a regression. Reactions/threads extraction gaps still apply but blocked by getting scanner running first. |
| 8 | DOM snapshot — fast-tick captures unread badges + channel list | ✅ pass | Sidebar matches web Slack: channel list (general, random, team-backend/frontend/product, notify-frontend-gi/se, External connections), DMs (Sanil 1, Alan, Aniketh, Cyrus, Mega Mind, Shanu, Steven), DMs+Activity nav badges (1 each), per-DM unread badge on Sanil (1). | DOM extraction working as designed. |
| 9 | Multi-workspace — switching workspaces; scanner tracks team_id |
TBD | TBD | Pre-audit: infer_team_id() parses DB-name pattern (slack_scanner/mod.rs:162-175) — fragile |
| 10 | Session persistence — tab switch preserves warm session; no re-auth | ✅ pass | Hard-killed all OpenHuman + core processes (pkill -9 + kill 75132 on port 7788). Relaunched pnpm dev:app cold. Clicked Slack tile → already signed in to Vezures workspace, no prompts. Per-account data_directory (CEF profile + cookies) survives full process termination. |
Persisted via ~/.openhuman-staging/users/<id>/cef/ profile. Logout-from-inside-Slack edge case (user-reported): in-app logout removes Slack tile from OpenHuman left rail; re-add comes back already signed in (Slack web's logout doesn't clear CEF cookies, OR sidebar removal isn't tied to actual session purge — UX quirk worth a follow-up). |
| 11 | Search — Slack built-in search functional | ✅ pass | User confirmed search works in-webview. | No app-layer interceptor needed. |
| 12 | Navigation — external links → system browser; allowed hosts slack.com, slack-edge.com, slack-imgs.com, slack-files.com resolve |
✅ pass | User confirmed external links open in system browser, in-app links stay. | Per #4 finding, slackb.com is correct CDN host — spec's slack-imgs.com + slack-files.com claim was outdated. |
Smoke run procedure
For each criterion:
- Reproduce in running app (
pnpm dev:app). - Capture exact symptom + console/CDP log line if relevant.
- Mark verdict in table.
- If ❌: file child issue against
tinyhumansai/openhumantitled[Bug] webview/slack: <symptom>linking back to #1016. - If ⚠️: note limitation + scope follow-up; decide whether to fix in this PR or defer.
Additional bug discovered during smoke (not in issue body)
Slack CEF surface goes blank after huddle interaction + tab switch
Symptom: After spawning a huddle popup (white blank window per #5) and dismissing it, the parent Slack webview becomes unclickable. Switching to OpenHuman home and clicking back to Slack: sidebar UI renders, but the entire CEF webview area is white.
Repro:
- Open Slack account in OpenHuman.
- Click "Start huddle" or any feature that triggers a popup
window.open. - Close / dismiss popup.
- Switch to OpenHuman home via sidebar.
- Click Slack tab again → sidebar shown, CEF area is white / blank.
Log evidence (timestamps from b9qimj6ka.output):
14:14:57first huddle popup spawned14:35:02second huddle popup spawned (the one that broke things)14:39:18Slack tab re-opened:[webview-accounts] reused existing label=acct_29da7de6...,revealed bounds=Bounds { x: 76.0, y: 0.0, width: 924.0, height: 768.0 }14:39:23Same again — Tauri-side reveal fires correctly with right bounds; CEF surface stays white
Root-cause hypothesis: CEF child popup window holds the GPU render context or some lifecycle state. When parent webview is hidden (tab switch) and revealed, CEF doesn't repaint. May share root cause with #5 (huddle popup blank).
Child issue to file: [Bug] webview/slack: parent CEF webview goes blank-white after huddle popup interaction + tab switch. Tauri-side reveal/bounds events fire correctly — bug is purely CEF render lifecycle.
Known issues from issue body (verify status)
- Huddle popup uses
about:blankwhitelisting — fragile if Slack changes flow - IDB scan interval 30s — messages may lag native push by up to 30s
OPENHUMAN_DISABLE_SLACK_SCANNER=1env escape hatch (debug only)
Pre-audit code-level gaps (from research dossier)
These were confirmed by static read of main before smoke. The smoke run will validate which manifest as user-visible bugs vs. intentional non-features.
webview_accounts/mod.rs:101— allowed_hosts hasslackb.com; spec asksslack-imgs.com+slack-files.com. Image/file CDN may bounce out to system browser.slack_scanner/mod.rs:178-225— memory grouping bychannel_idonly; spec requires(channel_id, day). Single doc per channel may grow unbounded.slack_scanner/extract.rs— reactions, threads (thread_ts + reply_count) not extracted.webview_accounts/mod.rs:754-793(forward_native_notification) — only per-account mute; ignores Slack's own per-channel mute state.notification_settings/mod.rs:33— defaulttrue(toast storm on first run).
Fixes shipped in this PR
| Bug | Root cause | Fix | File:line | Verified |
|---|---|---|---|---|
| A | lib.rs:998 double-Arc-wrapped slack_scanner::ScannerRegistry::new() (which already returns Arc<Self>). Tauri lookup at webview_accounts/mod.rs:1751 for Arc<ScannerRegistry> missed the Arc<Arc<…>> shape. Scanner never spawned. |
Drop the redundant outer Arc::new(...) so the managed type matches the lookup. Mirrors the pattern already used for whatsapp/discord/telegram (lines 997/999/1000). |
app/src-tauri/src/lib.rs:998 |
✅ post-fix log shows [sl] scanner up account=… interval=30s and no slack ScannerRegistry not in app state warning |
| B | Native Slack notifications never fired. CEF Notification permission for slack.com origin remained default; the existing JS shim only masked Notification.permission === "granted" for the page check, but the real CEF Notification path silently no-op'd at the C++ level when no actual grant existed. |
Issue a browser-scoped Browser.grantPermissions(["notifications"]) CDP call against the provider's origin right after attach. Adds an origin_of() helper to extract scheme://host[:port] from real_url. |
app/src-tauri/src/cdp/session.rs (new helper + grant call between shim injection and Page.enable) |
✅ post-fix log shows [cdp-session][…] granted notifications for origin=https://app.slack.com; macOS toasts now fire when OpenHuman is unfocused |
| D | (Surfaced once Bug A let the scanner run.) Slack's client router does pushState('/client/<workspace>/<channel>') shortly after first load, stripping the #openhuman-account-<id> fragment from the page-target URL. Scanner's find matcher starts_with(prefix) && ends_with(fragment) failed every tick after pushState. Memory ingest stayed empty. |
Relax the matcher: try strict (prefix + fragment) first, fall back to any same-origin Slack page target. Per-account data_directory isolation guarantees one Slack page-target per origin per account, so the broader match is safe. Same fix in both scan_once and dom_scan_once. |
app/src-tauri/src/slack_scanner/mod.rs:114-135 (scan_once) and :740-755 (dom_scan_once) |
✅ post-fix log shows multiple [sl][…] memory upsert ok namespace=slack-web:… key=<channel_or_dm> msgs=<N> lines (general/random/team-product channels + alan/sanil/shanu/elvin516/nikhil DMs); RPC openhuman.memory_recall_memories {namespace:"slack-web:<acct>"} returns 7 docs |
| C | Slack slack://T.../magic-login/<token> deep-links from inside the embedded Slack webview were being routed to open_in_system_browser. macOS handed them to the native Slack desktop app, which consumed the magic-login token and signed the workspace into the native client — breaking embedded-webview workspace isolation. Same risk for discord://, tg://, msteams://. Discovered while instrumenting #1074. |
Introduce is_provider_native_deep_link_scheme() helper. In both the on_navigation external-fallback and on_new_window external-fallback paths, suppress these schemes BEFORE the open_in_system_browser call — the page's HTTPS fallback completes sign-in without the deep link. zoomus:// / zoommtg:// continue to take the existing rewrite_provider_deep_link path (locked by zoomus_join_still_rewrites_and_is_recognized_as_native_scheme). |
app/src-tauri/src/webview_accounts/mod.rs (new helper near popup_should_stay_in_app; wired into both navigation paths) |
✅ unit tests in webview_accounts::tests::deep_link_scheme_*; manual smoke deferred to merge of #1074 PR |
| E | Slack Huddles' getUserMedia / getDisplayMedia surface had no browser-level permission grants — enumerateDevices() returned empty inside the huddle popup, the join button silently no-op'd. Same class as Bug B (notifications) but for media. Mirrors the gmeet pattern landed in PR #1054. |
Extend the per-origin grant list in cdp/session.rs::run_session_cycle to add audioCapture / videoCapture / displayCapture / clipboardReadWrite for app.slack.com alongside notifications. |
app/src-tauri/src/cdp/session.rs (slack arm in the Browser.grantPermissions block) |
✅ unit test origin_host_is_matches_app_slack_com_for_huddle_grant; functional verification deferred to the CEF popup-paint fix in #1079 (popup currently can't render long enough to use the perms; once it can, mic/cam/screenshare are wired) |
Out of scope (separate child issues recommended)
- Huddle popup blank — resolved 2026-05-04. Initial hypothesis (CEF same-document popup paint runtime gap) was wrong; real cause was tauri-cef's blank-page reload guard firing on popup browsers with the opener's URL, wiping Slack's
document.writecontent after 1s. Fixed in tinyhumansai/tauri-cef#12 (skip guard whenis_popup()); openhuman picks up the fix via the submodule pin bump in this PR (closes #1079). Combined with the earlier #1074 row E perms grant, Slack huddles are now end-to-end functional in the embedded webview. - CEF parent webview blanks after huddle interaction + tab switch — tracked as #1075. Pre-fix theory was that the popup's spurious
frame.load_url(opener_url)clobbered the parent's loading state, so #1079 may resolve this as a side-effect; needs re-verification on a build with the submodule bump from this PR. If still broken post-#1079 it becomes its own track focused onhost->Invalidate(PET_VIEW)/WasResized()on parent reveal. pnpm dev:apprestart_appregression on PR #1007 — orthogonal bug, broader than Slack; affects all dev:app sessions. Requires instrumentinggetActiveUserIdFromCore()to confirm whether it returnsnullin dev mode and triggers the seed/identity mismatch.- In-app Slack logout removes the OpenHuman sidebar tile but cookies persist — UX inconsistency (re-add = already signed in). Decide between (a) keep the tile after in-app logout, or (b) purge the per-account CEF profile on the logout signal.
- Pre-audit hypotheses dropped after smoke:
slackb.comis the working CDN host (spec'sslack-imgs.com/slack-files.comwere stale) — DO NOT changewebview_accounts/mod.rs:101; reactions + threads + per-channel mute + per-day grouping all dropped or deferred (gated on confirming actual gaps after Bug A+D made the scanner usable).
Sign-off
- Tester: oxoxDev
- Result: ✅ Three confirmed bugs (#7 scanner spawn / #6 notifications / Bug-D scanner target match) fixed. Memory ingest end-to-end working for 7 channels/DMs. Five other criteria pass; one skipped (no second workspace); huddle popup paint deferred to CEF tracking issue #1079 (root cause refined from the original "popup.location lost" hypothesis to "Slack uses popup.document.write into about:blank → CEF same-document popup paint dies after first frame"); related slack:// deep-link workspace-isolation leak fixed as Bug C; Slack Huddles media perms grant landed as Bug E (functional once #1079 clears).
- Date: 2026-04-29 (initial smoke); 2026-05-01 (huddle root-cause + Bug C/E follow-ups via #1074 instrumentation)
- Action items: #1079 tracks the CEF same-document popup paint lifecycle (the new path for "huddle popup blank"). The legacy "CEF blank-after-huddle on tab switch" entry above likely shares root cause with #1079 — revisit once that lands. dev:app restart-loop regression remains a separate ticket.