diff --git a/.planning/STATE.md b/.planning/STATE.md index ba358ce..0881afc 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,8 +3,8 @@ gsd_state_version: 1.0 milestone: v2.0.0 milestone_name: milestone status: executing -stopped_at: Session resumed 2026-05-19 post-/gsd-pause-work; spawning install-flow + auto-select researcher (the 529-blocked workstream); remaining Phase 1 gates after researcher = Plan 01-10 executor + Plan 01-12 executor + final-closure marker flip -last_updated: "2026-05-19T16:05:00Z" +stopped_at: Install-flow researcher returned 2026-05-19 (01-10-RESEARCH.md, 1092 lines); HIGH-confidence verdicts — both operator asks INFEASIBLE in unmanaged Chrome per W3C Screen Capture spec §5.1; recommendation = keep Plan 01-10's informational CTA charter; enhancement candidate = `monitorTypeSurfaces:'include'` for Plan 01-09 amendment. Awaiting orchestrator decision on 3 open questions before spawning 01-10 + 01-12 executors. +last_updated: "2026-05-19T16:30:00Z" last_activity: 2026-05-19 progress: total_phases: 5 diff --git a/.planning/phases/01-stabilize-video-pipeline/01-10-RESEARCH.md b/.planning/phases/01-stabilize-video-pipeline/01-10-RESEARCH.md new file mode 100644 index 0000000..d522dbd --- /dev/null +++ b/.planning/phases/01-stabilize-video-pipeline/01-10-RESEARCH.md @@ -0,0 +1,1092 @@ +# 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