feat(02-04): harness A24 — empirical Blob URL download verification (D-P2-01 closes P0-6)

Wire A24 into the Plan 01-13 Approach B UAT harness as the binding empirical
gate for D-P2-01. A24 verifies end-to-end that SAVE_ARCHIVE → chrome.downloads.
download receives a `blob:` URL prefix (NOT `data:application/zip;base64,`),
closing audit P0-6 functionally. The Plan 02-02 unit tests pin the wire-format
at the SW↔offscreen boundary; A24 pins it at the chrome.downloads platform
boundary through a real Chrome instance.

Strategy: chrome.downloads.onCreated listener captures the URL cross-realm.
The plan's <action> block proposed a chrome.downloads.download monkey-patch
installed in the harness page realm — but that intercepts only same-realm
calls, missing the SW's call. The canonical cross-realm capture pattern is
chrome.downloads.onCreated (fires for any download initiated by any extension
realm, with the full DownloadItem including .url). Documented as a deviation
from the plan's pseudo-code in SUMMARY.md (Rule 1 — bug fix vs the pseudo-code
strategy; same A24 contract verified, correct mechanism).

Files modified:
- tests/uat/extension-page-harness.ts (+~150 lines): assertA24 + A24_* constants
- tests/uat/lib/harness-page-driver.ts (+~30 lines): driveA24 page.evaluate wrapper
- tests/uat/harness.test.ts (+~10 lines): import driveA24, append to drivers list

Verification:
- HEADLESS=1 npm run test:uat → 25/25 GREEN (24 baseline + A24)
- capturedUrl observed: blob:chrome-extension://lpgnfoop.../...
- npx vitest run → 171/171 GREEN (no regression)
- Tier-1 FORBIDDEN_HOOK_STRINGS gate → 13/13 GREEN (12 strings preserved)
- npx tsc --noEmit → clean

Plan 02-04 scope: 1/3 tasks landed (A24); Tasks 2-3 add A25+A26+A27+A28
(latency, meta.json shape, multi-tab strict, REQ-archive-layout strict).
This commit is contained in:
2026-05-20 16:41:36 +02:00
parent 3821e5c402
commit 4ae73250fa
3 changed files with 219 additions and 4 deletions

View File

@@ -89,6 +89,8 @@ import {
driveA22,
// Plan 01-14 — picker-narrowing constraint
driveA23,
// Plan 02-04 Task 1 — D-P2-01 empirical Blob URL verification
driveA24,
getManifestVersion,
} from './lib/harness-page-driver';
import {
@@ -256,8 +258,8 @@ async function assertA0_GrepGate(): Promise<{
* @returns Process exit code: 0 on 15/15 GREEN, 1 on any failure.
*/
async function main(): Promise<number> {
process.stdout.write('\nMokosh Plan 01-13 + 01-14 — UAT harness orchestrator\n');
process.stdout.write('Architecture: A0 pre-flight + extension-internal page driver (A1..A14, A23)\n');
process.stdout.write('\nMokosh Plan 01-13 + 01-14 + 02-04 — UAT harness orchestrator\n');
process.stdout.write('Architecture: A0 pre-flight + extension-internal page driver (A1..A14, A15..A17, A18..A22, A23, A24)\n');
process.stdout.write('='.repeat(72) + '\n');
// A0 pre-flight (no Chrome launch needed; runs against built dist/).
@@ -373,6 +375,15 @@ async function main(): Promise<number> {
// Chrome ≥ 119 picker-narrowing semantics). Independent of A14 —
// no new getDisplayMedia call, no new state change.
{ name: 'A23', drive: driveA23 },
// Plan 02-04 Task 1 A24: D-P2-01 empirical Blob URL verification.
// Installs chrome.downloads.onCreated listener cross-realm, dispatches
// SAVE_ARCHIVE, captures the download URL, asserts the `blob:` prefix
// (closes audit P0-6 end-to-end through a real Chrome instance +
// the offscreen mint round-trip + chrome.downloads platform call).
// A24 does its OWN setupFreshRecording + SAVE because the listener
// must be installed pre-dispatch. After A24 the recording stays alive
// for any chained Plan 02-04 Tasks 2-3 assertions (Phase 2 closure).
{ name: 'A24', drive: driveA24 },
];
const buffers = { swConsole: handles.swConsole, offConsole: handles.offConsole };