From 78031e77827e2082c4f2b043ea2dab4a5f23d305 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 May 2026 16:08:08 +0200 Subject: [PATCH] =?UTF-8?q?feat(02-03):=20meta.json=20=E2=80=94=20urls[]?= =?UTF-8?q?=20+=20schemaVersion=20(D-P2-02=20+=20D-P2-03;=20replaces=20url?= =?UTF-8?q?:string)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/shared/types.ts SessionMetadata: REPLACE `url: string` with `urls: string[]`; ADD `schemaVersion: string` as the first field. Total 8 fields. Field-emission order follows source-declaration order (TypeScript object-literal insertion order; JSON.stringify emits in insertion order per ECMA-262). Docstring cites D-P2-02 + D-P2-03 + Plan 02-01 Task 3 planner-resolved 8th field decision + F2 empty-array permission. - src/background/index.ts: * Import { initTabUrlTracker, snapshotOpenTabs, getTabUrlsSeen } from './tab-url-tracker'. * Register initTabUrlTracker() at module top-level alongside chrome.downloads.onChanged (Plan 02-02 precedent for D-P2-* feature registration). Defensive try/catch matches the surrounding chrome.* listener pattern; tracker module has its own initialized flag for idempotency. * createArchive: snapshotOpenTabs() before reading getTabUrlsSeen() (DEC-011 Amendment 1 capability — captures tabs opened but never activated). Empty urls[] emitted faithfully per F2 (no fake extension-origin sentinel; logger.warn for diagnostic visibility on whole-desktop-no-tab sessions). * metadata literal: schemaVersion: '2' first, urls (not url), 8 fields total. ECMA-262 insertion-order guarantee + JSON.stringify deliver the canonical wire shape. - Always-on charter preserved: createArchive does NOT call clearTabUrlsSeen() — tracker continues accumulating across saves (Plan 01-09 Amendment 3 invariant). Verification: - npx tsc --noEmit → clean. - npm run build → clean (dist/assets/index.ts-8LkXuqac.js 378.82 kB, ~+2 kB vs pre-Task-2 baseline for the new tab-url-tracker module). - npx vitest run → 171/171 GREEN (was 163 GREEN / 8 RED; +8 GREEN net). - Tier-1 grep gate: 13/13 GREEN unchanged. Closes 8 RED tests: - tests/background/meta-json-urls-schema.test.ts Tests 1+2 (Tests 3+4+5 flipped in Task 1). - tests/build/strict-meta-json-validation.test.ts Tests 1+3+8 (Tests 2, 4, 5, 6, 7 remain GREEN regression guards). --- src/background/index.ts | 55 ++++++++++++++++++++++++++++++++++++++++- src/shared/types.ts | 30 ++++++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/background/index.ts b/src/background/index.ts index 0de9b4a..5311aba 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -8,6 +8,12 @@ import type { VideoBufferResponse } from '../shared/types'; import { remuxSegments } from './webm-remux'; +// Plan 02-03 D-P2-02 — tab-url tracker for meta.urls (multi-tab context). +// initTabUrlTracker is called from initialize(); snapshotOpenTabs + +// getTabUrlsSeen are called from createArchive() at SAVE time. The +// always-on charter (Plan 01-09 Amendment 3) is preserved — no +// clearTabUrlsSeen call in the save flow. +import { initTabUrlTracker, snapshotOpenTabs, getTabUrlsSeen } from './tab-url-tracker'; import JSZip from 'jszip'; // ─── Plan 01-13: NO SW-side test hook gate (Approach B) ────────────── @@ -709,10 +715,43 @@ async function createArchive( // the running version once manifest.json bumps to 1.0.1+. The Chrome // runtime API is always available in the SW context, so no fallback // is needed. + // + // Plan 02-03 D-P2-02 + D-P2-03 amendment (2026-05-20; closes audit P1 #10): + // - `url: string` REPLACED by `urls: string[]` sourced from the + // tab-url tracker (chrome.tabs.onActivated + onUpdated event-driven + // accumulation + snapshotOpenTabs SAVE-time defensive enumeration + // via chrome.tabs.query({})). + // - `schemaVersion: '2'` ADDED to mark the schema-breaking cutover. + // - Field-emission order follows the SessionMetadata interface line + // order — TypeScript preserves object-literal insertion order, and + // JSON.stringify emits in insertion order per ECMA-262. + // + // DEC-011 Amendment 1: with `tabs` permission granted, snapshot every + // currently-open tab's URL at SAVE time as a defensive fallback. This + // captures tabs the operator OPENED during the 30 s window but never + // activated (so the onActivated listener never fired for them). Dedup + // + order preservation is handled by the tracker itself. + try { + await snapshotOpenTabs(); + } catch (err) { + logger.warn('snapshotOpenTabs failed at SAVE time (continuing with tracker state):', err); + } + const urls = getTabUrlsSeen(); + if (urls.length === 0) { + // Meaningful state per F2 (plan-checker iteration 1): whole-desktop + // recording with NO browser tabs open at SAVE time. The empty array + // IS the canonical representation — NO fake extension-origin URL + // inserted. tests/background/meta-json-urls-schema.test.ts pins this + // contract. + logger.warn( + 'createArchive: tabUrlsSeen is empty after snapshotOpenTabs — emitting meta.urls=[] (whole-desktop session with no browser tabs)', + ); + } const manifest = chrome.runtime.getManifest(); const metadata: SessionMetadata = { + schemaVersion: '2', timestamp: new Date().toISOString(), - url: new URL(chrome.runtime.getURL('')).origin, + urls, userAgent: navigator.userAgent, extensionVersion: manifest.version, videoBufferSeconds: 30, @@ -1171,6 +1210,20 @@ try { logger.warn('chrome.notifications.onClicked.addListener failed:', e); } +// Plan 02-03 D-P2-02 — tab-url tracker bootstrap. +// Registers chrome.tabs.onActivated + chrome.tabs.onUpdated listeners that +// maintain an internal Set of URLs observed during the SW's lifetime. +// Feeds meta.urls in createArchive (closes audit P1 #10). Defensive +// try/catch matches the surrounding chrome.* listener registration +// pattern; idempotency is handled by the tracker module itself (an +// initialized flag prevents double-registration if some future caller +// invokes initTabUrlTracker more than once). +try { + initTabUrlTracker(); +} catch (e) { + logger.warn('initTabUrlTracker failed:', e); +} + // chrome.downloads.onChanged: D-P2-01 (P0-6 fix) — revoke-on-terminal-state. // Closes the URL.revokeObjectURL lifecycle by routing terminal download // state transitions (`complete` / `interrupted`) into a REVOKE_DOWNLOAD_URL diff --git a/src/shared/types.ts b/src/shared/types.ts index 72f9ba7..4f0f1e6 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -130,10 +130,36 @@ export interface UserEvent { meta?: Record; } -// Метаданные сессии +// Метаданные сессии. +// +// Phase 2 Plan 02-03 — D-P2-02 + D-P2-03 schema-breaking amendment +// (2026-05-20; closes audit P1 #10): +// +// - `url: string` REPLACED by `urls: string[]`. Captures the operator's +// multi-tab context during the rolling 30 s recording window — not +// just the active-at-save tab. Fed by the new +// `src/background/tab-url-tracker.ts` module via chrome.tabs.onActivated +// + chrome.tabs.onUpdated listeners + a SAVE-time +// chrome.tabs.query({}) snapshot (DEC-011 Amendment 1 grants the +// `tabs` permission). Empty array IS permitted (F2 — whole-desktop- +// no-tab session). +// +// - `schemaVersion: string` ADDED as the 8th field. Value '2' marks the +// D-P2-02 url→urls cutover; future schema bumps increment. The 8th +// field name was planner-suggested in Plan 02-01 Task 3 and ratified +// here as the lockstep for tests/build/strict-meta-json-validation.test.ts +// EXPECTED_KEYS. +// +// Total field count: 8 (per D-P2-03 strict exact-shape rule). +// +// Field-emission order follows source-declaration order: TypeScript object +// literals preserve insertion order, and JSON.stringify emits in insertion +// order per ECMA-262 §9.1.11.1 — so the meta.json output mirrors this +// interface line-by-line. export interface SessionMetadata { + schemaVersion: string; timestamp: string; - url: string; + urls: string[]; userAgent: string; extensionVersion: string; videoBufferSeconds: number;