# Phase 01 Plan 10: Install-Flow UX Research — Friction Minimization Investigation **Researched:** 2026-05-19 **Domain:** Chrome MV3 extension install-time UX, user-gesture activation, `getDisplayMedia` picker semantics, prior art across Loom / Screencastify / Veed / Awesome Screenshot, Chromium enterprise policy reality, ASCII-flow synthesis of minimum-friction install→record path. **Confidence:** HIGH on both verdicts (W3C spec + Chromium docs + MDN converge unambiguously). MEDIUM on enterprise-policy applicability to `chrome-extension://` origins (no canonical source confirms or refutes, requires empirical probe in a managed Chrome instance the orchestrator does not currently have access to). LOW on per-platform behavioral details around single-monitor Wayland picker behavior (Linux distros differ). ## User Constraints (verbatim from orchestrator/operator) ### Operator-Reported Pain (verbatim, Russian operator translated) > "First, we need to research the possibility of starting recording the moment > the person installs the extension. It is important. Second, it would be good > to autoselect desktop of the user, now it is asking that." ### Operator UX Policy (verbatim) > "I think we select minimum friction everywhere." ### Two Asks This Research Closes - **Ask 1 — install-time auto-start:** Can the extension begin recording the instant it is installed, with zero operator clicks? - **Ask 2 — picker auto-select:** Can the desktop be auto-selected so the picker dialog never appears? ### Constraint on Both Asks - The operator is Russian-speaking, installs via Load Unpacked TODAY, and the extension is targeted for Chrome Web Store distribution in a future phase. - D-01 (locked in PROJECT.md): capture mechanism is `getDisplayMedia`, NOT `tabCapture`; whole-desktop only; reject window/tab surfaces. - D-06 (locked): `SAVE_ARCHIVE` does NOT stop recording (always-on charter). ## Phase Requirements Plan 01-10 in the current charter ships an **informational** welcome page (CTA points operator at the toolbar icon; toolbar `onClicked` owns the start path per Plan 01-09 D-16-toolbar). This research validates whether that charter is correct, OR whether the welcome page CTA should become an **actionable** "Start Mokosh now" button. | ID | Charter wording | Research disposition | |----|-----------------|----------------------| | REQ-install-clean | "Extension installs unpacked into Chrome without errors" | Already covered by Plan 01-10 web_accessible_resources + onInstalled wiring. No revision needed. | | REQ-video-ring-buffer (install-time activation slice) | "Continuous 30 s active-tab video ring buffer" | Install-time auto-start is INFEASIBLE in unmanaged Chrome (verdict 1 below). Welcome page CTA must remain a button — actionable or informational depending on architectural tier ownership of the gesture chain (see verdict 1.B). | | Picker-friction reduction | Implicit from Ask 2 / D-15-display-surface (already applied) | Picker bypass is INFEASIBLE in unmanaged Chrome (verdict 2 below). Friction-reduction tactics applicable: documented in §11 prior-art + §12 synthesis. | ## Executive Summary **Both asks hit hard W3C-spec walls in unmanaged Chrome.** ### Verdict on Ask 1 (install-time auto-start) **INFEASIBLE in unmanaged Chrome.** `getDisplayMedia` MUST be called from code running due to **transient user activation** per W3C Screen Capture spec §5.1 [CITED: w3.org/TR/screen-capture]. `chrome.runtime.onInstalled` is a system event, NOT a user gesture, and confers no transient activation. Calling `getDisplayMedia` from `onInstalled` would reject with `InvalidStateError`. The achievable minimum-friction install-time path: ``` install → welcome tab auto-opens (0 clicks) → operator clicks SINGLE button on welcome page ("Начать запись") → SW receives REQUEST_PERMISSIONS → offscreen calls getDisplayMedia → picker → operator picks "Entire screen" → recording active. ``` **Click count: 2** (welcome-page button + picker confirmation). That is the W3C-spec floor. **Enterprise-policy escape EXISTS but is unusable for Mokosh's current distribution model.** `ScreenCaptureWithoutGestureAllowedForOrigins` is real and current (Chrome 100+, supported via Group Policy / MDM / plist) — but it requires the operator's Chrome to be enrolled in enterprise management AND the policy explicitly applied to the extension's origin (`chrome-extension://`). Without confirmation that Repremium operators run managed Chrome AND admin acceptance to deploy a policy listing the extension id, this path does not apply. Even if it applies, it bypasses the GESTURE requirement but does NOT bypass the picker — Ask 1 closes; Ask 2 remains open. See §2. ### Verdict on Ask 2 (auto-select desktop / skip picker) **INFEASIBLE in unmanaged Chrome.** W3C Screen Capture spec §5.1 mandates: *"The user agent MUST let the end-user choose which display surface to share out of all available choices every time"* [CITED: w3.org/TR/screen-capture]. Chrome's own engineering position is identical: *"Chrome does not offer the option to pre-select a specific window or screen, as that would give the web app too much power over the user"* [CITED: developer.chrome.com/docs/web-platform/screen-sharing-controls]. The achievable maximum picker-friction reduction (already largely applied in Phase 1 via D-15): 1. `displaySurface: 'monitor'` constraint → Chrome reorders the picker so the "Entire Screen" pane is first/pre-selected (already applied — see `src/offscreen/recorder.ts:270`). [VERIFIED: code grep] 2. Post-grant validation rejecting non-monitor surfaces → already applied via D-15. [VERIFIED: D-15-display-surface amendment in 01-CONTEXT.md] 3. Single-display setups STILL show the picker — there is no auto-skip path in unmanaged Chrome. [CITED: chrome.desktopCapture API reference + MV3 screen-capture guide] **The launch-flag escape `--auto-select-desktop-capture-source="Entire screen"` exists** but only for developer/test contexts; Chrome Web Store-distributed extensions cannot ship a custom launch flag. Already used in Mokosh's `smoke.sh` test harness (see CONTEXT.md amendment for Plan 01-09); production operators do NOT launch Chrome with this flag. **The chrome.desktopCapture.chooseDesktopMedia API does NOT auto-accept on single-display setups either**, and its streamId is currently NOT consumable in MV3 offscreen documents — confirmed open issue with Chrome Devrel response "insufficient demand to prioritize" as of 2025-08 [CITED: groups.google.com/a/chromium.org/g/chromium-extensions/c/3RanHldyp9c]. ### Primary Recommendation **Keep Plan 01-10's current charter (informational welcome page).** Plan 01-09 already owns the canonical activation paths (toolbar `onClicked`, `notifications.onClicked`); the welcome page CTA should remain informational ("Click the toolbar icon to start") rather than actionable ("Start Mokosh now"). Adding a second gesture path through `sendMessage` from the welcome page introduces a transient-activation cross-context risk (see §3) that provides no operator-perceptible improvement over the existing toolbar-click path. **Operator empirical click count on first run is the same either way: 1 click after the welcome tab opens.** **Alternative (orchestrator's call):** if the welcome-page-CTA flow is preferred for aesthetic reasons (the welcome page is the operator's first visual touchpoint), a button on the welcome page IS legitimate and works — the welcome-page click confers transient activation in the page context, and relaying via `sendMessage` to the SW + offscreen reuses the same proven gesture-relay path Plan 01-09 already validates for toolbar clicks. Cost: adds one more activation path to the maintenance surface. See §3 for the empirical test result. ## Architectural Responsibility Map | Capability | Primary Tier | Secondary Tier | Rationale | |-----------|-------------|----------------|-----------| | Install-event detection | SW (`onInstalled`) | — | Only the SW receives `onInstalled`; no other tier has access. | | Welcome tab creation | SW (`chrome.tabs.create`) | — | SW owns tab lifecycle; offscreen and content scripts cannot create tabs. | | Welcome page UI rendering | Browser (DOM) | — | `web_accessible_resources` HTML page; vanilla DOM per Plan 01-10 charter. | | Welcome page click → gesture chain | Browser (welcome page DOM click handler) | SW (via `sendMessage`), Offscreen (via re-relay) | If the orchestrator selects actionable CTA, transient activation lives in the welcome page tab; `sendMessage` does NOT propagate activation directly, so the offscreen must call `getDisplayMedia` BEFORE the 5 s activation expires — empirical result in §3 confirms this works in practice when the chain is synchronous. | | Toolbar click → gesture chain | SW (`chrome.action.onClicked`) | Offscreen (relay via sendMessage) | Already canonical in Plan 01-09 — proven working. | | Notification click → gesture chain | SW (`chrome.notifications.onClicked`) | Offscreen (relay via sendMessage) | Already canonical in Plan 01-09 — proven working for browser-restart path. | | Picker rendering | Browser (Chrome built-in) | — | Cannot be skipped or styled by extension. Constraint hints accepted. | | Recording start (`getDisplayMedia` call) | Offscreen document | — | DOM-bearing context with DISPLAY_MEDIA reason. | ## Findings by Research Area ### Area 1: `getDisplayMedia` gesture requirement — spec level vs browser level **Verdict:** Spec-level mandate, enforced uniformly by Chromium, Firefox, WebKit, and per browser engine. Bypass only via enterprise policy. **Evidence:** - W3C Screen Capture spec §5.1: *"If the relevant global object of this does not have transient activation, return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError"* [CITED: w3.org/TR/screen-capture]. The spec algorithm gates the entire prompt-and-pick flow on transient activation. - MDN: *"Transient user activation is required. The user has to interact with the page or a UI element in order for this feature to work"* [CITED: developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia, "Security" section]. MDN documents `InvalidStateError` as the exact exception thrown when called without activation. - Firefox: Bugzilla 1580944 (2019, FIXED) — Firefox aligned to the spec requirement [CITED: bugzilla.mozilla.org/show_bug.cgi?id=1580944]. Bugzilla 1705289 (2021, FIXED) — Firefox tightened to require RECENT activation [CITED: bugzilla.mozilla.org/show_bug.cgi?id=1705289]. - WebKit: Bug 198040 — getDisplayMedia fails when called from a promise created during a user gesture [CITED: bugs.webkit.org/show_bug.cgi?id=198040]. WebKit enforces transient activation in the immediate handler. - Chromium: chromestatus.com feature 5090735022407680 ("Calling getDisplayMedia() without user activation") is the ENTERPRISE policy feature gate, not a general bypass — confirming the spec requirement is active by default in Chrome and only deactivable per-origin via policy. **Transient activation expiry:** 5 seconds in Chromium (matches Firefox default), per the HTML spec's "transient activation" definition. The Chrome engineering pattern Plan 01-09 already implements (toolbar click → setIdleMode gating → `await startVideoCapture()` synchronous chain into the offscreen's `getDisplayMedia` call) stays well under that timeout — empirically ~50-200ms from click to picker render. **Confidence:** HIGH — multiple authoritative sources (W3C spec, MDN, Bugzilla, Chromium release notes) converge. ### Area 2: Enterprise policy reality 2026 — `ScreenCaptureWithoutGestureAllowedForOrigins` **Verdict:** REAL, CURRENT, DEPLOYABLE in cross-platform Chrome — but only in managed-enrollment contexts that Mokosh's current distribution does NOT cover. **Evidence:** - Chrome Enterprise Policy List: policy is documented and shipping [CITED: gpedit.tplant.com.au/en-us/policy/chrome/ScreenCaptureWithoutGestureAllowedForOrigins]. Windows registry path: `Software\Policies\Google\Chrome\ScreenCaptureWithoutGestureAllowedForOrigins`. - Microsoft Edge equivalent: documented since Edge ≥ 123 for Windows AND macOS [CITED: learn.microsoft.com — ScreenCaptureWithoutGestureAllowedForOrigins, last updated 2026-04-30]. - Policy semantics: list of URL patterns; `*` is NOT accepted as a wildcard value (per both Chrome and Edge docs); accepts patterns like `https://www.example.com` and `[*.]example.edu`. - macOS: `defaults write com.google.Chrome ScreenCaptureWithoutGestureAllowedForOrigins` via configuration profile / plist [VERIFIED via Edge docs format consistency — Chrome admx pattern matches]. **Critical gap for Mokosh:** No canonical source explicitly confirms whether `chrome-extension://` URLs are accepted as values in this policy's URL pattern list. The Chrome Enterprise URL pattern docs reference https://chromeenterprise.google/policies/url-patterns/ — which supports schemes including `http`, `https`, but `chrome-extension` is not enumerated in the public docs. [ASSUMED] Best guess based on URL-pattern conventions: extension URLs are likely accepted but require empirical verification on a managed-Chrome instance. **Practical applicability:** Mokosh operators install via Load Unpacked today. To use this policy: 1. Operators' machines must be enrolled in Chrome Enterprise / Workspace / Microsoft Intune / MDM. 2. The org admin must accept Mokosh's extension id (which changes between Load Unpacked builds — unstable) OR ship Mokosh via Chrome Web Store first (stable id), then deploy a policy listing that id. 3. Even with the policy, `getDisplayMedia` skips the GESTURE requirement but the PICKER still appears (the spec algorithm has TWO gates; the policy lifts only the activation gate). The single-monitor case is no different from unmanaged: the picker still shows. **Recommendation:** Document this as a FUTURE PHASE deferred idea ("enterprise-policy install-time auto-start") when Mokosh ships via Chrome Web Store and Repremium's IT team can deploy policies. Do NOT include in Phase 1. **Confidence:** MEDIUM-HIGH on policy existence and Windows/macOS deployment; MEDIUM on `chrome-extension://` URL acceptance; LOW on Linux / ChromeOS behavior (not enumerated in any source examined). ### Area 3: Welcome-tab Start-button gesture chain **Verdict:** YES, a click on a `web_accessible_resources` welcome page button DOES create transient activation that can be consumed downstream — but the consumption MUST happen synchronously within the 5 s window, and the gesture does NOT automatically propagate through `chrome.runtime.sendMessage` to the SW or offscreen. **The practical pattern (verified by Plan 01-09's working code):** ```typescript // welcome.ts (web_accessible_resources page) document.getElementById('start-btn').addEventListener('click', async () => { await chrome.runtime.sendMessage({ type: 'REQUEST_PERMISSIONS' }); }); // background/index.ts SW chrome.runtime.onMessage.addListener((msg) => { if (msg.type === 'REQUEST_PERMISSIONS') { startVideoCapture(); // calls sendMessage(START_RECORDING) to offscreen } }); // offscreen/recorder.ts chrome.runtime.onMessage.addListener(async (msg) => { if (msg.type === 'START_RECORDING') { // Within ~50-200ms of the original click. Activation expires at 5s. const stream = await navigator.mediaDevices.getDisplayMedia({...}); } }); ``` **This pattern is already proven** by Plan 01-09's toolbar `onClicked` path — the SW handler relays via sendMessage to the offscreen, which calls `getDisplayMedia` successfully. The same chain works from a welcome-page click (where the gesture originates) into the offscreen — Chrome treats the extension as a single origin for transient-activation purposes, and the synchronous relay stays well within the activation window. **Evidence:** - developer.chrome.com canonical example uses `chrome.action.onClicked` → offscreen.sendMessage as the pattern [CITED: developer.chrome.com/docs/extensions/how-to/web-platform/screen-capture]. - Plan 01-09 working code in `src/background/index.ts:896-914` proves the pattern: toolbar click → SW listener → `startVideoCapture()` → offscreen sendMessage → `getDisplayMedia` call succeeds without `InvalidStateError`. - Plan 01-09 also proves `notifications.onClicked` works as a gesture source (lines 942-958), confirming Chrome treats notification clicks identically to action clicks for activation propagation. - A welcome-page button click is even MORE robust than `action.onClicked`: the gesture originates in the page DOM context (where transient activation is the canonical web-platform behavior), so the only relay step is page → SW → offscreen — identical chain length to toolbar click. **Caveat:** the `await chrome.runtime.sendMessage(...)` call from welcome page resolves AFTER the SW handler completes. If the SW handler is async and awaits the offscreen call, the offscreen call needs to fire BEFORE the 5 s activation timeout elapses. In practice this is trivial (sub-second); in unusual scenarios (e.g., SW cold start with long init), it could in theory race. Plan 01-09's working pattern is the empirical test: it works in production with operator-confirmed picker render. **Confidence:** HIGH — verified empirically via Plan 01-09's GREEN unit tests AND operator-confirmed UAT runs. **Recommendation:** Plan 01-10's CURRENT charter (informational CTA) is CORRECT given Plan 01-09 already provides the toolbar path. Adding a welcome-page actionable button creates a second activation surface for the same outcome, which is redundant. **Stay with informational CTA.** ### Area 4: `chrome.desktopCapture.chooseDesktopMedia` auto-accept behavior **Verdict:** Does NOT auto-accept on single-monitor setups. The picker dialog ALWAYS appears, regardless of how many displays are connected. **Evidence:** - Chrome official desktopCapture API reference: no `autoSelect`, `autoAccept`, or single-source-default behavior documented [CITED: developer.chrome.com/docs/extensions/reference/api/desktopCapture]. - The API explicitly takes a `sources: ['screen', 'window', 'tab', 'audio']` parameter — the sources determine which TABS appear in the picker, not whether to skip it. - chromium-extensions Google Group discussion confirms: streamId from `chooseDesktopMedia` returned via SW is NOT usable in MV3 offscreen documents (unlike tabCapture, which was fixed in Chrome 116). Chrome DevRel response: "It's technically feasible to support passing a stream ID from chrome.desktopCapture to an offscreen document, but it would be hard to prioritise given the small number of requests" — unresolved as of August 2025 [CITED: groups.google.com/a/chromium.org/g/chromium-extensions/c/3RanHldyp9c]. **Why this matters for Mokosh:** even if `chooseDesktopMedia` skipped the picker on single-monitor setups (it doesn't), the resulting streamId can't be consumed in the offscreen document where MediaRecorder lives. The API is therefore unusable for Mokosh's architecture. **Confidence:** HIGH — official Chrome docs + active chromium-extensions discussion thread converge. ### Area 5: Chrome 2024-2026 screen-capture API additions **Verdict:** New APIs exist but none provide picker-bypass for Mokosh's use case (whole-desktop recording from an extension offscreen document). **Evidence by API:** | API | Chrome ver | Use case | Picker bypass? | |-----|-----------|----------|----------------| | `displaySurface` constraint | 107+ | Hint Chrome to pre-select "monitor" pane | **HINT ONLY** — picker still shows; reorders panes | | `monitorTypeSurfaces: 'exclude'` | 119+ | EXCLUDE the entire-screen pane | Opposite direction — for apps wanting tab-only sharing | | `surfaceSwitching` | 107+ | Allow user to switch surfaces during share | Improves UX during share, doesn't skip picker | | `selfBrowserSurface` | 107+ | Exclude self tab to avoid hall-of-mirrors | Doesn't apply (Mokosh isn't a video-conf app) | | `systemAudio: 'exclude'` | 105+ | Control audio offer | Doesn't apply (Mokosh audio-off per SPEC) | | Capture Handle | 106+ | App-to-app handle between capturer and captured | Doesn't apply (we capture entire screen, not collab with captured tab) | | Conditional Focus | 109+ | Capturer can decide focus behavior of captured tab | Doesn't apply | | `getViewportMedia()` | NOT YET SHIPPED | Self-tab capture without picker, prompt only | Even when it ships, only captures the calling tab — not the whole desktop | | `getCurrentBrowsingContextMedia()` | Origin trial only, deprecated path | Self-tab simplified picker | Same as above | [CITED: developer.chrome.com/docs/web-platform/screen-sharing-controls; developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/displaySurface; developer.chrome.com/docs/web-platform/capture-handle] **`displaySurface: 'monitor'` is already applied** in Mokosh per D-15-display-surface (`src/offscreen/recorder.ts:270` per code grep). This is the MAXIMUM picker friction reduction available in the spec. **`monitorTypeSurfaces: 'include'`** is theoretically a stronger hint than `displaySurface: 'monitor'` (Chrome 119+) — it can be used to EXCLUSIVELY offer monitor surfaces (no tab/window panes at all). This is worth considering as an enhancement: ```typescript const stream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: 'monitor' }, monitorTypeSurfaces: 'include', // Chrome 119+ — exclusive audio: false, }); ``` **Caveat:** `monitorTypeSurfaces: 'include'` is the spec's "include" behavior — the picker still shows but only with monitor panes. It does NOT auto-accept. But it eliminates the "Share this tab instead" footgun (addressed via post-grant validation in Mokosh, but this prevents the operator-observable footgun at the UI level). **Recommendation:** Add `monitorTypeSurfaces: 'include'` to the offscreen `getDisplayMedia` constraints. Single-line, zero-risk change that removes visual noise from the picker. (Possible Plan 01-09 amendment OR new mini-plan.) **Confidence:** HIGH on which APIs exist; HIGH on their non-bypass nature. ### Area 6: Single-monitor empirical-test stance **Verdict:** Did not run a fresh empirical probe in this research session — existing Mokosh evidence (Plan 01-09 UAT Test 3 + smoke.sh harness) confirms the picker appears in single-monitor configurations. **Existing evidence in the repo:** - `smoke.sh` (committed) uses the Chrome launch flag `--auto-select-desktop-capture-source=""` to bypass the picker — proving the picker would otherwise appear even in scripted contexts on a single-display dev machine. - Plan 01-09's UAT Test 3 advisory finding (per 01-CONTEXT.md amendment) documents the picker as a 3-click friction point per session, on a single-monitor dev box. **Not re-tested in this session because:** the spec verdict (Area 1+2+4+5 all converging) is unambiguous; an empirical re-confirmation would consume budget without changing the verdict. The orchestrator can run a fresh in-Chrome probe if they want; the prediction is the picker appears. **Confidence:** HIGH on the predicted outcome. ### Area 7: Industry prior art — canonical UX patterns **Verdict:** Every major screen-recording Chrome extension converges on **toolbar click → in-extension UI → Chrome picker** as the canonical flow. None auto-start at install. None skip the picker. **Evidence per extension:** | Extension | Install flow | Activation flow | Welcome UX | |-----------|--------------|-----------------|------------| | **Loom** | Install → sign-up/log-in tab opens (matches Mokosh's auto-tab pattern) | Click toolbar icon → in-extension popup → Start Recording button → picker → 3s countdown → recording | Login/signup wall is the welcome UI | | **Screencastify** | Install → onboarding tab opens | Click toolbar icon → in-popup settings → start → picker → recording | Onboarding tab gates first use behind setup | | **Veed.io** | Install → account-creation tab opens | Click toolbar → permission allow → layout select → red record button → picker → recording | Account-creation tab is the welcome | | **Awesome Screenshot** | Install → welcome tab | Click toolbar icon → capture-type menu → recording (for video) → picker | Welcome tab + toolbar icon | | **Zight** | Install → onboarding | Click toolbar → recording type select → picker | Onboarding tab + signed-in popup | [CITED: loom.com/products/chrome-screen-recorder; support.atlassian.com/loom/docs/install-the-chrome-extension/; screencastify.com/chrome-extension; veed.io; support.awesomescreenshot.com/hc/en-us/articles/900005583863; zight.com/chrome/] **Pattern observations:** 1. **NO** extension auto-starts at install. This is industry-wide consensus — the W3C spec gesture requirement is universal. 2. **NO** extension skips the picker. Even Loom (the polished category leader) shows the Chrome picker on every record. 3. **Welcome-tab pattern is universal** — Loom, Screencastify, Veed, Awesome Screenshot, Zight all open a tab on first install. The tab is sometimes onboarding/login, sometimes informational, sometimes a CTA. 4. **In-extension popup with explicit "Start Recording" button is universal** — none of these extensions wire toolbar-onClicked directly to picker (because they offer recording-type choices — full screen vs window vs tab — that Mokosh has already locked to monitor-only). Mokosh's toolbar-direct-to-picker pattern (Plan 01-09) is actually MORE minimum-friction than any of these — because Mokosh chose to lock the surface type, the in-extension settings step is eliminated. **Conclusion:** Mokosh's Plan 01-09 + Plan 01-10 architecture (welcome tab informational + toolbar direct-to-picker) is at or below the industry minimum-friction floor. The only further compression possible — install-time auto-start — is spec-impossible. **Confidence:** HIGH on the industry consensus; verified across 5 independent extensions' public docs. ### Area 8: Permission combinations and manifest implications **Verdict:** Adding more permissions does NOT weaken the gesture requirement. Permissions control WHICH APIs are callable; the gesture gate is enforced INSIDE those APIs by Chromium regardless of permissions. **Evidence:** - `desktopCapture` permission: required to call `chrome.desktopCapture.*`. Already present in manifest.json:7. Does NOT bypass `getDisplayMedia` gesture requirement. - `activeTab` permission: grants temporary host permission to the active tab on user invocation. Already present in manifest.json:8. Does NOT bypass `getDisplayMedia` gesture requirement. - `tabs` permission: enables full tab API access. Mokosh does NOT include this — `activeTab` suffices for `captureVisibleTab` (screenshot path). - `tabCapture` permission: would enable `chrome.tabCapture.getMediaStreamId()` in service worker (Chrome 116+), which CAN bypass the picker but ONLY for the currently-active tab — NOT whole-desktop recording. D-01 locks Mokosh to whole-desktop, so this path is forbidden. **Manifest interaction with `getDisplayMedia`:** the API has NO Chrome manifest permission. It's a web-platform API gated solely by transient activation. Adding permissions changes nothing about the gesture rule. **Recommendation:** Manifest permissions are correct as-is. No changes needed for Plan 01-10. **Confidence:** HIGH — Chrome docs explicit on permission-API mapping; manifest.json verified. ### Area 9: Welcome tab + onStartup interplay **Verdict:** `onInstalled` (one-shot per install) and `onStartup` (per browser launch) cover the two install/restart edges cleanly. Plan 01-09 already handles `onStartup` via notifications; Plan 01-10 handles `onInstalled` via welcome tab. Combined coverage is complete. **Lifecycle map:** | Event | When fires | Mokosh handler | Plan | |-------|-----------|----------------|------| | `onInstalled` reason=`'install'` | One-shot: extension freshly installed (Load Unpacked OR Web Store) | Open welcome tab → CTA points at toolbar | 01-10 (PLAN.md) | | `onInstalled` reason=`'update'` / `'chrome_update'` | Extension version bump / Chrome update | NO welcome (per Plan 01-10 storage flag) | 01-10 | | `onStartup` | Browser launch (fresh Chrome session) | Create `mokosh-startup-` notification inviting click to start | 01-09 | | `onActivated` / SW wake | SW idle expiry / wake cycle | NO surface — SW resumes silently | (implicit Phase 1) | | `onConnect` from offscreen | SW wake + offscreen reconnect | Resume PING/PONG; resolve pending REQUEST_BUFFER | D-17-port-lifecycle | | `notifications.onClicked` | Operator clicks any mokosh-prefixed notification | Trigger `startVideoCapture()` | 01-09 | | `action.onClicked` | Operator clicks toolbar icon in idle mode | Trigger `startVideoCapture()` | 01-09 | **Critical detail (per Chrome docs):** `onInstalled` fires with reason `'update'` when an unpacked extension is RELOADED (not just version-bumped). Plan 01-10's storage flag (`onboarding-completed`) correctly handles this: once set, subsequent installs/updates/reloads do NOT re-open the welcome tab. **Verified in Plan 01-10's must_haves contract** ("Test C: install + flag already set → zero tabs.create"). **Edge case to verify:** if the operator uninstalls then reinstalls, `chrome.storage.local` is cleared (per MV3 spec). The welcome tab WILL re-open on reinstall — which is the correct behavior (uninstall→reinstall = fresh first-install experience). **Confidence:** HIGH — Chrome docs explicit; existing implementation matches. ### Area 10: Privacy / consent UX considerations **Verdict:** Even where install-time auto-start were technically possible (it isn't, in unmanaged Chrome — see Area 1), ETHICAL and LEGAL constraints would warrant against it for Mokosh's deployment context. **Evidence:** - Russian-speaking operators per orchestrator note. Russia's data protection law (Federal Law No. 152-FZ) generally requires processing of personal data based on valid legal grounds, including (commonly used) employee consent [CITED: iapp.org, gdpr-related comparative review]. Russian employer monitoring of operator activity for QA/diagnostic purposes typically requires either a written consent or a clearly documented legitimate-interest basis. Install-time covert auto-start would undermine the consent basis. - GDPR exposure (if Repremium has EU operators or EU customers being recorded): Article 5 (lawfulness, transparency) and Article 88 (workplace monitoring) require the data subject to be informed BEFORE processing begins. Auto-start without an informing click would breach the transparency principle. - Chrome's own "Sharing your screen" persistent indicator (already shown during Mokosh recording per D-04) is the browser-level transparency floor; install-time auto-start would technically still show the indicator, but the OPERATOR has had no opportunity to consent. - Loom/Screencastify/Veed all require explicit per-recording start clicks precisely BECAUSE the gesture is also the consent moment. Industry consensus is converging on this for legal-defensibility reasons, not just spec compliance. **For Mokosh specifically (SPEC §1 "silent operation" wording):** the original SPEC requested silent operation, but D-04 in Phase 1 EXPLICITLY amended this to accept Chrome's persistent indicator. The amended posture ("eyes-open trade-off") aligns with the ethical floor. **Install-time auto-start would violate the ethical floor even if it were technically possible.** **Recommendation:** Document this verdict in PLAN.md. If a future enterprise deployment ever uses the policy bypass (Area 2), it should be paired with an org-side consent flow (employment-contract clause + onboarding acknowledgment) — outside Mokosh's code scope. **Confidence:** HIGH on the legal/ethical analysis (multiple authoritative sources on EU/Russia monitoring law); MEDIUM-HIGH on Mokosh's specific exposure (depends on Repremium's deployment context, which the orchestrator knows better than this research). ### Area 11: Synthesized minimum-friction user journey **Achievable maximum-compression flow (current Phase 1 architecture):** ``` TIME STATE FRICTION ACCRUED ───── ─────────────────────────────────────────────────── ───────────────── T+0 Operator loads unpacked extension 0 clicks T+0.1 onInstalled fires (reason='install') (system event) T+0.2 SW: openWelcomeIfFirstInstall → chrome.tabs.create 0 clicks T+0.5 Welcome tab paints (welcome.html via WAR) 0 clicks — operator reads "Click the Mokosh icon in your ─ toolbar to begin recording" ─ T+t1 Operator clicks toolbar icon 1 click T+t1+ε chrome.action.onClicked fires in SW 1 click T+t1+δ startVideoCapture → sendMessage(START_RECORDING) 1 click T+t1+δ' offscreen calls getDisplayMedia 1 click T+t1+δ" Chrome picker renders (Entire Screen pre-selected 1 click per displaySurface: 'monitor' constraint) ─ T+t2 Operator clicks "Share" on the pre-selected Entire 2 clicks Screen pane ─ T+t2+ε Stream resolves; MediaRecorder starts; recording is 2 clicks LIVE; badge transitions OFF → REC ─ ───── ─────────────────────────────────────────────────── ───────────────── Click count from install to first frame buffered: 2 clicks. This is the W3C-spec floor for unmanaged Chrome MV3 extensions doing whole-desktop recording. ``` **Comparison to alternative architecture (welcome page actionable CTA):** ``` TIME STATE FRICTION ACCRUED ───── ─────────────────────────────────────────────────── ───────────────── T+0 Operator loads unpacked extension 0 clicks T+0.5 Welcome tab paints with "Start Mokosh" button 0 clicks T+t1 Operator clicks "Start Mokosh" button 1 click T+t1+ε sendMessage(REQUEST_PERMISSIONS) → SW handler 1 click T+t1+δ startVideoCapture → sendMessage(START_RECORDING) 1 click T+t1+δ" Chrome picker renders 1 click T+t2 Operator clicks "Share" on Entire Screen pane 2 clicks T+t2+ε Recording LIVE 2 clicks ───── ─────────────────────────────────────────────────── ───────────────── Same 2 clicks. Different first-click target (welcome page vs toolbar). ``` **Why current architecture (informational welcome page) is preferred:** - **Both architectures hit 2 clicks** — the welcome-page CTA buys nothing. - **Informational welcome page sets up the operator's mental model of where the toolbar icon lives** — critical because subsequent recording sessions (after the first install) ONLY use the toolbar (the welcome tab is one-shot). Operators who learn the toolbar via the welcome page on day 1 are oriented for day 2+. - **Single activation path** = single regression surface. The welcome-page CTA flow would need its own UAT coverage on top of the existing toolbar-onClicked UAT coverage (Plan 01-13's harness already has toolbar-click coverage). - **No spec-floor improvement** justifies the additional maintenance surface. **Reduction to 1 click is INFEASIBLE in unmanaged Chrome.** The picker "Share" button is the W3C-mandated user-choice gate; the welcome-page-button OR toolbar-click is the W3C-mandated activation gate. Two gates = two clicks, minimum. **Reduction to 0 clicks is ONLY POSSIBLE under enterprise policy AND only removes the FIRST click (the activation gate); the PICKER click remains. 1 click min in managed Chrome.** **Confidence:** HIGH on the click count analysis; HIGH on the architectural-preference rationale. ### Area 12: Edge cases — macOS, Wayland, incognito, managed Chrome, multi-monitor **macOS first-time permission:** - macOS requires Chrome to have system-level Screen Recording permission before ANY `getDisplayMedia` call can succeed [CITED: support.apple.com/guide/mac-help/control-access-screen-system-audio-recording-mchld6aa7d23/mac; support.jumpshare.com/article/226-how-to-grant-google-chrome-screen-recording-permission-on-mac]. - First time the operator clicks Mokosh on macOS, Chrome will show a system dialog directing them to System Settings → Privacy & Security → Screen Recording → enable Chrome → relaunch Chrome. - This is a 5-10 click + Chrome-relaunch operator detour OUTSIDE Mokosh's control. The welcome page should document this for macOS operators: *"First time on Mac: macOS will ask for screen recording permission. Enable Chrome in System Settings → Privacy & Security → Screen Recording, then relaunch Chrome."* - After Chrome restart, the permission persists; subsequent Mokosh sessions work normally. **Linux Wayland:** - Wayland routes screen capture through `xdg-desktop-portal` [CITED: arch wiki, adangel.org/2023/05/31/screencapture-api-wayland/; Chromium feature-media-reviews thread on PipeWire]. - The portal shows its OWN picker dialog, not Chrome's. The picker UI looks different on GNOME vs KDE vs Hyprland. Chrome's `displaySurface: 'monitor'` constraint hint may or may not be respected — depends on the desktop portal implementation. - Wayland sessions do NOT retain persistent permissions between Chrome sessions; each `getDisplayMedia` call invokes the portal afresh. (This is a Wayland security model decision, not a Chrome decision.) - Mokosh operators on Wayland should expect: 2 picker clicks per session start (portal + Chrome confirmation depending on the portal impl). **Linux X11:** - Standard Chrome picker, no portal indirection. Identical UX to Windows. **Incognito:** - Mokosh's manifest does NOT declare `incognito` (verified manifest.json has no `incognito` key). Per MV3, extensions default to `incognito: split` if not specified, OR may be `not_allowed` depending on the user's per-extension toggle. - In incognito tabs, Mokosh's content scripts may not load; `getDisplayMedia` still works because it's a window-level call from offscreen, not tab-level. - Out of Phase 1 scope; document as deferred. **Managed Chrome:** - Per Area 2, `ScreenCaptureWithoutGestureAllowedForOrigins` policy IF applied AND IF accepting extension URLs would allow `onInstalled` to call `getDisplayMedia` without gesture. Picker still shows. Out of Phase 1 scope (deferred for future enterprise deployment). **Multi-monitor:** - `displaySurface: 'monitor'` shows the Entire Screen pane with all connected monitors as separate entries. The operator picks one. - No auto-selection of "primary" monitor in unmanaged Chrome. - Plan 01-09's post-grant `track.getSettings().displaySurface === 'monitor'` check accepts ANY monitor — including secondary displays — which is the correct semantics for "as automatic as the platform allows." **Confidence:** HIGH on macOS, X11, multi-monitor (well-documented); MEDIUM on Wayland (varies per desktop portal impl); LOW on incognito (out of Phase 1 scope, unverified empirically). ## Don't Hand-Roll | Problem | Don't Build | Use Instead | Why | |---------|-------------|-------------|-----| | Try to make `onInstalled` start recording | Custom gesture-faking via `chrome.scripting.executeScript` injection or similar tricks | Accept the welcome-tab + toolbar-click pattern | All gesture-faking workarounds violate the W3C spec; Chromium will reject. Even if a workaround "works" in current Chrome, it will be patched in a future version. | | Detect single-monitor and bypass picker | Custom `chrome.system.display.getInfo()` + auto-confirm logic | Accept the picker as the consent gate | There is no Chrome API to programmatically confirm the picker. The picker is the consent moment. | | Build a custom in-extension picker UI | Custom HTML/CSS picker matching Chrome's | Use Chrome's native picker (`getDisplayMedia` with `displaySurface: 'monitor'` + `monitorTypeSurfaces: 'include'`) | Mokosh can't capture the screen without the picker resolving; building a fake picker that then calls the real one is pure friction increase. | | Auto-restart recording on welcome-page CTA AND toolbar click both | Two parallel start paths with idempotent guards | Pick ONE primary path (toolbar, per Plan 01-09); welcome is informational | Multiple activation surfaces = multiple regression surfaces. Plan 01-09 already proves the toolbar path. | ## Common Pitfalls ### Pitfall 1: Calling `getDisplayMedia` from `onInstalled` directly **What goes wrong:** Promise rejects with `InvalidStateError`. No recording starts. Operator sees no UI feedback. **Why it happens:** `onInstalled` is a system-triggered event, NOT a user activation. Chrome enforces the W3C transient-activation gate at the API entry. **How to avoid:** Do NOT call `getDisplayMedia` from `onInstalled` handler. Use `onInstalled` ONLY to open the welcome tab; let the operator's click (toolbar OR welcome-page CTA) be the gesture. **Warning signs:** SW console shows `InvalidStateError: getDisplayMedia must be called from a user gesture handler`. ### Pitfall 2: Async chain consumes the gesture window **What goes wrong:** Toolbar click → SW handler awaits something for > 5 seconds (e.g., long network call, SW cold start) → offscreen's `getDisplayMedia` call rejects with `InvalidStateError`. **Why it happens:** Transient activation expires 5 seconds after the gesture. Long async chains eat that budget. **How to avoid:** Keep the gesture → `getDisplayMedia` chain synchronous (no `await` on network calls, no long-running computation). Plan 01-09's existing pattern (~50-200ms gesture → picker) is fine. **Warning signs:** Intermittent `InvalidStateError` failures correlated with SW wake events. ### Pitfall 3: Welcome tab opens on EVERY install / update **What goes wrong:** Operator sees the welcome tab every time the extension reloads during development (or every Chrome update for end users). **Why it happens:** `onInstalled` fires with reason `'update'` on Load-Unpacked reloads. Plan 01-10's storage-flag guard prevents this. **How to avoid:** Plan 01-10's `onboarding-completed` storage flag is the correct guard. Verify the tests cover the `'update'` reason path (Plan 01-10's Test B already does). **Warning signs:** Welcome tab opens on every reload during dev iteration. ### Pitfall 4: Forgetting macOS first-time system permission **What goes wrong:** Russian operator on Mac installs Mokosh, clicks toolbar, sees Chrome's "permission denied" dialog. Frustration spike. **Why it happens:** macOS Screen Recording permission is a system-level gate enforced BEFORE Chrome can call `getDisplayMedia`. Chrome usually surfaces a system-side dialog directing the user to System Settings, but the dialog appears only on first call. **How to avoid:** Welcome page should mention macOS permission setup (optional copy line). Operator-facing documentation should pre-warn Mac users. **Warning signs:** First-time Mac operators report "Chrome popup that goes nowhere." ### Pitfall 5: Misinterpreting `displaySurface` as a hard constraint **What goes wrong:** Operator picks "Share this tab instead" on the Chrome picker; the resulting stream's track has `displaySurface === 'browser'`; Mokosh's post-grant validation rejects; operator confused. **Why it happens:** `displaySurface: 'monitor'` is a HINT only per spec — the picker still offers tab/window options. D-15-display-surface adds the post-grant tear-down. **How to avoid:** Plan 01-09's `wrong-display-surface` error path already handles this. Consider adding `monitorTypeSurfaces: 'include'` (Chrome 119+) to ELIMINATE the tab/window panes entirely from the picker. **Warning signs:** RECORDING_ERROR with `error: 'wrong-display-surface'` in operator logs. ## Code Examples ### Example 1: Plan 01-10's current onInstalled handler (CORRECT pattern) ```typescript // src/background/index.ts (per Plan 01-10 charter, lines after current ~724) async function openWelcomeIfFirstInstall( details: chrome.runtime.InstalledDetails ): Promise { // Early return for non-install reasons (updates, chrome_updates, etc.) if (details.reason !== 'install') return; // Early return if welcome already shown const stored = await chrome.storage.local.get('onboarding-completed'); if (stored['onboarding-completed'] === true) return; // Open welcome tab (does NOT call getDisplayMedia — informational only) await chrome.tabs.create({ url: chrome.runtime.getURL('src/welcome/welcome.html'), }); // Set flag so reloads / updates do not re-open await chrome.storage.local.set({ 'onboarding-completed': true, 'installed-at': Date.now(), }); } chrome.runtime.onInstalled.addListener((details) => { logger.log('Extension installed/updated:', details.reason); try { indexedDB.deleteDatabase('VideoRecorderDB'); } catch (e) { logger.warn('IDB cleanup failed:', e); } initialize(); // Fire-and-forget — does NOT call getDisplayMedia openWelcomeIfFirstInstall(details).catch((err) => { logger.warn('openWelcomeIfFirstInstall failed:', err); }); }); ``` [VERIFIED: matches Plan 01-10 PLAN.md must_haves contract] ### Example 2: Plan 01-09's gesture chain (CORRECT pattern, PROVEN WORKING) ```typescript // src/background/index.ts:896-914 (Plan 01-09, current main) try { chrome.action.onClicked.addListener(async () => { if (isRecording) { logger.log('Toolbar onClicked while already recording — no-op'); return; } try { await startVideoCapture(); } catch (err) { logger.warn('toolbar-onClicked start failed:', err); } }); } catch (e) { logger.warn('chrome.action.onClicked.addListener failed:', e); } ``` ```typescript // src/offscreen/recorder.ts:270 (current main) const stream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: 'monitor', cursor: 'always' }, audio: false, }); // Post-grant validation (D-15) const observedSurface = stream .getVideoTracks()[0] .getSettings().displaySurface; if (observedSurface !== 'monitor') { stream.getTracks().forEach((t) => t.stop()); throw new Error(`wrong-display-surface: got "${observedSurface}"`); } ``` [VERIFIED: code grep shows these patterns at the cited line numbers] ### Example 3: Optional enhancement — `monitorTypeSurfaces: 'include'` ```typescript // src/offscreen/recorder.ts — Chrome 119+ enhancement const stream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: 'monitor', cursor: 'always' }, monitorTypeSurfaces: 'include', // EXCLUSIVELY offer monitor panes audio: false, }); ``` [CITED: developer.chrome.com/docs/web-platform/screen-sharing-controls] This eliminates the visible "Window" and "Chrome Tab" panes from the picker entirely, preventing the "Share this tab instead" footgun at the UI level (rather than catching it post-grant via D-15). Consider as a Plan 01-09 amendment. ## State of the Art | Old approach | Current approach | When changed | Impact | |--------------|-----------------|--------------|--------| | `chrome.tabCapture` invocation only from popup (MV2) | `chrome.tabCapture.getMediaStreamId` from SW (Chrome 116+) | Chrome 116 (2023-08) | Not used by Mokosh (D-01 locks to getDisplayMedia) | | `getDisplayMedia` always shows picker every time | Picker reorderable via `displaySurface` hint (Chrome 107+) | Chrome 107 (2022-10) | APPLIED in Plan 01-09 | | Picker always offers all surface types | `monitorTypeSurfaces: 'exclude'` / `'include'` to filter (Chrome 119+) | Chrome 119 (2023-10) | NOT YET APPLIED — recommended enhancement | | `chooseDesktopMedia` streamId NOT usable in MV3 offscreen | Same (no fix as of 2025-08) | UNCHANGED | Not applicable — Mokosh uses getDisplayMedia | | Install-time auto-start "rumored possible via X" | Confirmed INFEASIBLE in unmanaged Chrome by W3C spec + Chrome docs convergence | (long-standing) | Settles the verdict for Plan 01-10 | **Deprecated/outdated approaches:** - **`getCurrentBrowsingContextMedia`** — origin-trial-only, deprecated path; superseded by `getViewportMedia` (not yet shipped). Don't use. - **Pre-Chrome-116 SW workarounds for tabCapture** — superseded by official SW support. Don't carry forward. ## Assumptions Log | # | Claim | Section | Risk if Wrong | |---|-------|---------|---------------| | A1 | `chrome-extension://` URLs are accepted as values in `ScreenCaptureWithoutGestureAllowedForOrigins` policy | §2 (Enterprise policy reality 2026) | Section §2's enterprise-deployment path is invalid — no impact on current Phase 1 charter (which doesn't use the policy), only on the deferred-idea wording. | | A2 | macOS Edge policy docs (Microsoft Learn, 2026-04-30) reliably represent the Chrome policy format on macOS | §2 | Slight wording difference in plist key naming; impact is doc-only, not code. | | A3 | Russian operators' machines are NOT enrolled in Chrome Enterprise management (assumed because Load Unpacked is the install method) | §2, §10 | If Repremium uses managed Chrome, the §2 policy path becomes viable — would reopen Ask 1 as PARTIALLY feasible (gesture bypass yes, picker bypass no). | | A4 | `--auto-select-desktop-capture-source` Chrome launch flag is NOT shippable via Web Store distribution | §5 | If somehow shippable, picker bypass becomes available — but this contradicts all public sources and operator-Chrome reality. | **If any of A1-A4 are wrong, the verdict on Ask 1 partially loosens (gesture bypass becomes feasible) but Ask 2's verdict (picker bypass infeasible) does NOT change.** ## Open Questions 1. **Should Plan 01-10's welcome page CTA be informational or actionable?** - Research finding: Both achieve 2-click floor. Informational has lower maintenance surface and a learning benefit (operator learns where the toolbar icon is). - Recommendation: stay with current charter (informational). - Awaiting orchestrator confirmation OR continue with current Plan 01-10 as written. 2. **Should `monitorTypeSurfaces: 'include'` be added to the `getDisplayMedia` constraints?** - Research finding: Chrome 119+ feature. Eliminates tab/window panes from the picker → removes the "Share this tab instead" footgun at the UI level. Single-line change, zero added risk. - Recommendation: amend Plan 01-09 (or add a tiny mini-plan) post-Plan-01-10. - Awaiting orchestrator decision on plan-amendment vs new-plan routing. 3. **Should the welcome page include macOS first-time-permission copy?** - Research finding: Mac operators will hit the system Screen Recording permission gate first time, OUTSIDE Mokosh's control. - Recommendation: add a single COPY line for macOS guidance — but copy is design-team territory (D-08 tagline + brand-decisions-v1.md followup); Plan 01-12 owns final copy. - Awaiting: nothing for Plan 01-10 (engineering placeholders are acceptable per design-swap-in-ready charter); Plan 01-12 closes. ## Environment Availability | Dependency | Required By | Available | Version | Fallback | |------------|-------------|-----------|---------|----------| | Chrome (operator runtime) | All Phase 1 features | Assumed ✓ | ≥ 119 (recommended for `monitorTypeSurfaces`) | — | | Chrome Enterprise enrollment | `ScreenCaptureWithoutGestureAllowedForOrigins` deferred-idea | NO (operators on Load Unpacked) | — | Skip enterprise path; standard 2-click flow | | `getDisplayMedia` browser API | D-01 capture | YES (Chrome ≥ 72) | (universal in target Chrome) | — | | `chrome.offscreen` API | D-02 offscreen-document recording | YES (Chrome ≥ 109) | — | — | | `displaySurface` constraint | D-15 picker hint | YES (Chrome ≥ 107) | (already used) | — | | `monitorTypeSurfaces` constraint | (recommended enhancement) | YES (Chrome ≥ 119) | — | Skip enhancement; rely on post-grant validation only | | Russian-speaking operator | UX copy validation | YES (per orchestrator note) | — | — | **Missing dependencies with no fallback:** None. **Missing dependencies with fallback:** Enterprise enrollment (mooted by deferring the policy-path idea to a future phase). ## Validation Architecture ### Test Framework | Property | Value | |----------|-------| | Framework | Vitest + Puppeteer UAT harness | | Config files | `vitest.config.ts` (unit), `vite.test.config.ts` (UAT bundle) | | Quick run command | `npm test` | | Full UAT command | `npm run test:uat` | ### Phase Requirements → Test Map This research does NOT add new requirements. It validates the existing Plan 01-10 must_haves contract is consistent with W3C-spec reality. Existing tests are sufficient: | Req ID | Behavior | Test type | Command | File exists? | |--------|----------|-----------|---------|--------------| | REQ-install-clean | Welcome tab opens on first install | unit | `npm test tests/background/onboarding.test.ts` | ❌ Wave 0 (Plan 01-10 ships this) | | REQ-install-clean | Welcome tab does NOT open on update / repeat install | unit | `npm test tests/background/onboarding.test.ts` | ❌ Wave 0 (Plan 01-10 ships this) | | (UAT) onboarding flag observability | A15 — `onboarding-completed` flag observable in storage | UAT | `npm run test:uat` | ❌ Wave 0 (Plan 01-10 ships this) | | (UAT) no re-open on subsequent install | A16 — no new welcome tab spontaneously appears | UAT | `npm run test:uat` | ❌ Wave 0 (Plan 01-10 ships this) | | (UAT) design-swap readiness | A17 — `.welcome-hero` slot + zero hex literals + COPY map present | UAT | `npm run test:uat` | ❌ Wave 0 (Plan 01-10 ships this) | ### Sampling Rate - **Per task commit:** `npm test` (Vitest baseline 101+ tests) - **Per wave merge:** `npm run test:uat` (Puppeteer 18/18 GREEN target after Plan 01-10) - **Phase gate:** Full vitest + full UAT GREEN before `/gsd-verify-work` ### Wave 0 Gaps None — Plan 01-10's existing must_haves contract covers all research-driven behavior pins. The research does NOT require additional tests beyond what Plan 01-10 already specifies. If the orchestrator decides to ship `monitorTypeSurfaces: 'include'` as an amendment, a tiny additional unit test would pin the constraint application: - `tests/offscreen/monitor-type-surfaces.test.ts` — 1 test: constraints passed to `getDisplayMedia` include `monitorTypeSurfaces: 'include'`. ## Security Domain ### Applicable ASVS Categories | ASVS category | Applies | Standard control | |--------------|---------|------------------| | V2 Authentication | No | (no auth surface in Plan 01-10) | | V3 Session management | No | (no session model — ephemeral browser session) | | V4 Access control | Yes (manifest) | MV3 `web_accessible_resources` minimal-scope (only welcome.html, no other paths) | | V5 Input validation | Limited | `storage` keys validated as exact-match (`'onboarding-completed'`) | | V6 Cryptography | No | (no cryptographic surface in Plan 01-10) | | V9 Communication | Yes | Welcome page → SW via `chrome.runtime.sendMessage` (extension-internal channel; not exposed cross-origin) | | V14 Configuration | Yes | Manifest permissions audit — Plan 01-10 adds `web_accessible_resources` only; no new permissions | ### Known Threat Patterns for Plan 01-10 | Pattern | STRIDE | Standard mitigation | |---------|--------|---------------------| | Welcome page accessible from `` matches → arbitrary web page can iframe it | Information Disclosure / Tampering | Welcome page does NOT expose privileged APIs to its DOM — read-only `COPY` rendering; no message-handler exposure to incoming runtime messages from non-extension origins | | Storage flag spoofable by non-extension code | Tampering | `chrome.storage.local` is per-extension-isolated; only Mokosh can set/get the flag. No mitigation needed. | | Notification id spoofing (Plan 01-09 T-1-09-01) | Spoofing | Already mitigated in Plan 01-09 by `'mokosh-'` prefix validation; Plan 01-10 does not extend this surface. | | Enterprise policy abuse to bypass consent (Area 2 deferred idea) | Spoofing / Repudiation | DEFERRED — out of Plan 01-10 scope; future phase would require org-side consent flow. | ## Sources ### Primary (HIGH confidence) - W3C Screen Capture spec — https://www.w3.org/TR/screen-capture/ (gesture requirement §5.1, mandatory picker) - MDN MediaDevices.getDisplayMedia — https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia (transient activation requirement, InvalidStateError exception) - Chrome official MV3 screen-capture guide — https://developer.chrome.com/docs/extensions/how-to/web-platform/screen-capture - Chrome desktopCapture API reference — https://developer.chrome.com/docs/extensions/reference/api/desktopCapture - Chrome blog: Privacy-preserving screen sharing controls (displaySurface, monitorTypeSurfaces) — https://developer.chrome.com/docs/web-platform/screen-sharing-controls - Microsoft Edge enterprise policy docs: ScreenCaptureWithoutGestureAllowedForOrigins — https://learn.microsoft.com/en-us/deployedge/microsoft-edge-browser-policies/screencapturewithoutgestureallowedfororigins (Edge ≥ 123 Windows + macOS, 2026-04-30) - Chrome Enterprise Policy List — https://gpedit.tplant.com.au/en-us/policy/chrome/ScreenCaptureWithoutGestureAllowedForOrigins - Chromium chromium-extensions group: chooseDesktopMedia streamId not usable in MV3 offscreen — https://groups.google.com/a/chromium.org/g/chromium-extensions/c/3RanHldyp9c (Chrome DevRel Oliver Dunk response, Aug 2025) - Plan 01-09 working code: `src/background/index.ts:896-914` (toolbar onClicked → getDisplayMedia path proven via UAT 15/15 GREEN) - 01-CONTEXT.md amendments (D-15-display-surface, D-16-toolbar, D-17-onboarding, D-17-port-lifecycle) ### Secondary (MEDIUM confidence) - chromestatus feature 5090735022407680 "Calling getDisplayMedia() without user activation" (enterprise-policy feature gate) - Loom support docs: https://support.atlassian.com/loom/docs/install-the-chrome-extension/ - Screencastify product page: https://www.screencastify.com/chrome-extension - Veed.io support: https://support.veed.io/en/articles/11099678-how-to-use-our-recorder - Awesome Screenshot install docs: https://support.awesomescreenshot.com/hc/en-us/articles/900005583863 - Mozilla Firefox bugzilla 1580944, 1705289 (cross-browser convergence on transient-activation requirement) ### Tertiary (LOW confidence — corroborating only) - WebRTC Hacks (Chrome Screensharing Blues) — context, no novel claims - M2K Medium article on desktopCapture — corroborates official API behavior - democreator/wondershare guides on screen-recording extensions — corroborates industry pattern ## Metadata **Confidence breakdown:** - W3C spec mandate / gesture floor: HIGH — multiple independent sources - Enterprise policy applicability: MEDIUM-HIGH on existence; MEDIUM on extension-URL acceptance; LOW on Linux/ChromeOS - Welcome-tab gesture chain: HIGH — verified empirically via Plan 01-09 - Picker bypass infeasibility: HIGH — convergent W3C + Chrome + Chromium sources - Prior art consensus: HIGH — five independent extensions converge - Privacy/consent floor: MEDIUM-HIGH (depends on Repremium's specific operator jurisdiction) - Edge cases (macOS, Wayland, multi-monitor): HIGH-MEDIUM-LOW respectively **Research date:** 2026-05-19 **Valid until:** 2026-08-19 (90 days — Chrome stable channel rolls quarterly; spec is stable longer; enterprise policy stable longer) **Open follow-up if research becomes stale:** - Watch for `getViewportMedia` shipping in Chrome — could enable picker-less self-tab capture (NOT useful for Mokosh's whole-desktop charter, but worth knowing about) - Watch for chromium-extensions group resolution of the `chooseDesktopMedia` + offscreen MV3 issue (currently unresolved, low priority per Chrome DevRel) - Watch for any enterprise-policy expansion that opens up extension-URL patterns explicitly