From 4b5f393a7420541723a07243b83cdaa7e28948de Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 06:03:19 +0900 Subject: [PATCH 1/3] test(mv3): require bounded history mutation lifecycle --- tests/test_mv3_history_mutation_contract.py | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/test_mv3_history_mutation_contract.py diff --git a/tests/test_mv3_history_mutation_contract.py b/tests/test_mv3_history_mutation_contract.py new file mode 100644 index 00000000..8eb0e2cf --- /dev/null +++ b/tests/test_mv3_history_mutation_contract.py @@ -0,0 +1,54 @@ +"""Fail-first contract for bounded Manifest V3 history mutation compatibility.""" + +from __future__ import annotations + +import json +import pathlib +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +FIXTURE = ROOT / "tests" / "fixtures" / "mv3_basic" + + +class ManifestV3HistoryMutationContractTests(unittest.TestCase): + """Require one controlled add/read/delete history lifecycle in real Chromium.""" + + def test_fixture_declares_history_permission(self) -> None: + """The controlled extension must explicitly request history authority.""" + + manifest = json.loads((FIXTURE / "manifest.json").read_text(encoding="utf-8")) + self.assertIn("history", manifest["permissions"]) + + def test_service_worker_executes_bounded_history_mutation_lifecycle(self) -> None: + """Compatibility evidence must require add/read/delete, not search alone.""" + + worker = (FIXTURE / "service_worker.js").read_text(encoding="utf-8") + for expected in ( + "exerciseHistoryMutation", + "chrome.history.addUrl", + "chrome.history.search", + "chrome.history.deleteUrl", + "historyMutationReady", + ): + with self.subTest(expected=expected): + self.assertIn(expected, worker) + + def test_history_mutation_is_bound_to_controlled_fixture_url_and_cleanup(self) -> None: + """The fixture must not mutate arbitrary history and must remove test state.""" + + worker = (FIXTURE / "service_worker.js").read_text(encoding="utf-8") + for expected in ( + 'parsed.protocol !== "http:"', + 'parsed.hostname !== "127.0.0.1"', + 'parsed.pathname !== "/page.html"', + "finally", + "chrome.history.deleteUrl", + ): + with self.subTest(expected=expected): + self.assertIn(expected, worker) + self.assertNotIn("_error.message", worker) + self.assertNotIn("String(_error)", worker) + + +if __name__ == "__main__": + unittest.main() From eae7e8f20fb21b3d928376830a197c12e0b99b4c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 06:05:04 +0900 Subject: [PATCH 2/3] feat(mv3): exercise bounded history mutation lifecycle --- tests/fixtures/mv3_basic/service_worker.js | 64 ++++++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/tests/fixtures/mv3_basic/service_worker.js b/tests/fixtures/mv3_basic/service_worker.js index 55ded0c4..3b61c3d5 100644 --- a/tests/fixtures/mv3_basic/service_worker.js +++ b/tests/fixtures/mv3_basic/service_worker.js @@ -154,6 +154,62 @@ async function exerciseBookmarkMutation(sender) { return bookmarkMutationReady; } +async function exerciseHistoryMutation(sender) { + const sourceUrl = sender?.tab?.url; + if (typeof sourceUrl !== "string") { + return false; + } + + let parsed; + try { + parsed = new URL(sourceUrl); + } catch (_error) { + return false; + } + if ( + parsed.protocol !== "http:" || + parsed.hostname !== "127.0.0.1" || + parsed.pathname !== "/page.html" || + parsed.username !== "" || + parsed.password !== "" + ) { + return false; + } + + const historyUrl = new URL("history-entry.html", sourceUrl).href; + let historyMutationReady = false; + try { + await chrome.history.addUrl({ url: historyUrl }); + const items = await chrome.history.search({ + text: historyUrl, + startTime: 0, + maxResults: 10, + }); + historyMutationReady = + Array.isArray(items) && items.some((item) => item?.url === historyUrl); + } catch (_error) { + historyMutationReady = false; + } finally { + try { + await chrome.history.deleteUrl({ url: historyUrl }); + const remainingItems = await chrome.history.search({ + text: historyUrl, + startTime: 0, + maxResults: 10, + }); + if ( + !Array.isArray(remainingItems) || + remainingItems.some((item) => item?.url === historyUrl) + ) { + historyMutationReady = false; + } + } catch (_error) { + historyMutationReady = false; + } + } + return historyMutationReady; +} + async function exerciseCoreApis(sender) { const tabId = sender?.tab?.id; if (!Number.isInteger(tabId)) { @@ -187,12 +243,8 @@ async function exerciseCoreApis(sender) { const bookmarkMutationReady = await exerciseBookmarkMutation(sender); const bookmarksReady = bookmarkMutationReady; - const historyItems = await chrome.history.search({ - text: "", - startTime: 0, - maxResults: 10, - }); - const historyReady = Array.isArray(historyItems); + const historyMutationReady = await exerciseHistoryMutation(sender); + const historyReady = historyMutationReady; const downloadResult = await exerciseDownload(sender); const downloadsReady = downloadResult.ready; From b0d9c905fd7a50128eb1dde643b8a3a0f9cb1dc8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 06:07:05 +0900 Subject: [PATCH 3/3] docs(changelog): record bounded history compatibility proof --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4bd39c..039b5e62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Authoritative product documentation graph spanning PRD, TRD, ADR lifecycle/index, product-wide UML, conceptual ERD, requirement/decision traceability, threat modeling, product-wide test strategy, operability, API/protocol, release/rollback, and current primary-source standards doctoring, with machine-checkable repository contracts that keep conversation-derived future work distinct from protected-main implementation claims. - Purpose-bound data-governance and privacy baseline that rejects both blanket masking and ambient raw-value propagation, defines field-scoped just-in-time disclosure, opaque-handle/trusted-broker boundaries, model/provider/region policy, retention/deletion/residency/break-glass controls, truthful CSAP/SOC 2 readiness language, and machine-checkable documentation contracts without inventing an OriginWeave-owned production database. - Proposed product-wide target-architecture ADRs for the Rust control plane, isolated execution modes, typed actions, semantic observation/stale-node authority, prompt-injection and secret separation, resource-governor priority, provenance evidence, browser/protocol adapters, crawler policy, and hourly automation operational closure; these remain Proposed rather than shipped claims until protected review and merge. +- Pinned real-Chromium Manifest V3 compatibility evidence for a controlled history add/read/delete lifecycle confined to the ephemeral loopback fixture profile, with cleanup verification and no OriginWeave Agent history-authority claim. ### Changed