# 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