feat(01-13): wave-3A — add get-display-surface bridge op (A3 prereq) + extend Tier-1 grep gate

Scope: prerequisite step for Wave 3A's A3 assertion (displaySurface=monitor
verification). The page→offscreen bridge gains a new op so the harness can
query the active stream's `getSettings().displaySurface` without needing
direct offscreen.evaluate access (impossible by-construction; the only
cross-isolate path is chrome.runtime.sendMessage).

Bridge op contract (`src/test-hooks/offscreen-hooks.ts`):
  - Protocol: { type: '__mokoshOffscreenQuery', op: 'get-display-surface' }
  - Response: { displaySurface: string|null }
    • null when no current stream (recording not active)
    • 'monitor' when installFakeDisplayMedia's monkey-patched
      getSettings() reports it (production code in
      src/offscreen/recorder.ts enforces this same value — tears down
      stream + throws 'wrong-display-surface' otherwise).
  - Failure: { ok: false, error: <message> } only on getSettings throw.

Tier-1 grep gate extension (`tests/background/no-test-hooks-in-prod-bundle.test.ts`):
  - FORBIDDEN_HOOK_STRINGS: 8 → 9 entries.
  - Added: 'get-display-surface' (the literal bridge-op string;
    matches the production-bundle absence invariant — the offscreen-hooks
    module is tree-shaken in production builds by the Vite mode gate in
    src/offscreen/recorder.ts top-of-module).

Verification:
  - npx tsc: clean
  - npm run build: clean (dist/ 4 chunks; no offscreen-hooks artifact)
  - npm run build:test: clean (dist-test/ adds offscreen-hooks-DfWtG71P.js, 2.38kB)
  - SKIP_BUILD=1 vitest run no-test-hooks-in-prod-bundle.test.ts → 10/10 GREEN
    (1 build-sanity + 9 forbidden-string checks; production bundle hook-free)
  - SKIP_BUILD=1 vitest run (full) → 93/93 GREEN
    (Wave 0+1+2 baseline 92 + 1 from the 9th grep-gate string)
  - npx tsx tests/uat/a6.test.ts → A6 5/5 GREEN
    (lib-driven path preserved; bridge op addition does not interfere)

Wave 3A continuation: assertA1/A2/A3/A4 land in the next commit which
wires the harness-page surface + driver wrappers + harness.test.ts
orchestrator. This commit is the bridge prerequisite — keeping the
bridge-op extension atomic + the grep-gate extension atomic so the
'production bundle hook-free' invariant is provable BEFORE the page-side
surface lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 15:33:35 +02:00
parent eb64521321
commit 2f1b1f36a7
2 changed files with 36 additions and 1 deletions

View File

@@ -300,6 +300,10 @@ globalThis.__mokoshTest = {
// op='install-fake-display-media' → { ok: true } OR { ok: false, error }
// op='dispatch-ended' → { ok: true } OR { ok: false, error: 'no stream' }
// op='has-stream' → { hasStream: boolean }
// op='get-display-surface' → { displaySurface: string|null } OR { ok: false, error }
// — Plan 01-13 Wave 3A A3 contract. Returns the active track's
// `getSettings().displaySurface` value (monkey-patched to 'monitor'
// by `installFakeDisplayMedia`); returns null when no stream is live.
// Unknown ops respond { ok: false, error: 'unknown-op' }.
//
// The bridge handler MUST run BEFORE the production offscreen bridge
@@ -349,6 +353,35 @@ chrome.runtime.onMessage.addListener((rawMessage, _sender, sendResponse) => {
sendResponse({ hasStream: currentStream !== null });
return false;
}
if (op === 'get-display-surface') {
// Plan 01-13 Wave 3A A3 contract — return the active track's
// displaySurface so the harness can verify the offscreen-hooks
// monkey-patched getSettings() correctly reports 'monitor'.
// Production code in src/offscreen/recorder.ts enforces this same
// invariant (tears down + throws 'wrong-display-surface' otherwise),
// so if recording is live the value is guaranteed monitor — the
// harness asserts === 'monitor' for explicit empirical verification.
try {
if (currentStream === null) {
sendResponse({ displaySurface: null });
return false;
}
const track = currentStream.getVideoTracks()[0];
if (track === undefined) {
sendResponse({ displaySurface: null });
return false;
}
const settings = track.getSettings();
const displaySurface = settings.displaySurface ?? null;
sendResponse({ displaySurface });
} catch (err) {
sendResponse({
ok: false,
error: err instanceof Error ? err.message : String(err),
});
}
return false;
}
sendResponse({ ok: false, error: 'unknown-op' });
return false;
});