From 054708a5e957075fd2ed062a91b81a8f6a563f2a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 16 Aug 2026 16:47:57 +0000 Subject: [PATCH 1/2] test(mv3): require bounded bookmark mutation diagnostics Bookmark create/get/remove failures previously collapsed to ready/missing, so operators could not tell source rejection from leftover residue. Require allow-listed stage tokens, sanitize raw Chrome errors, and record the current chrome.bookmarks APA citation as compatibility evidence only. Co-authored-by: Seongho Bae --- CHANGELOG.md | 2 + docs/DOCUMENTATION_FITNESS.md | 2 +- docs/PRD.md | 4 +- docs/TRD.md | 2 +- docs/doctoring.md | 6 + docs/doctoring/mv3-compatibility.md | 8 +- .../evidence/2026-08-10-active-pr-maturity.md | 2 +- docs/traceability/README.md | 2 +- scripts/ci/run_mv3_compatibility.py | 19 +++ tests/fixtures/mv3_basic/content_script.js | 2 + tests/fixtures/mv3_basic/service_worker.js | 44 ++++--- tests/test_mv3_bookmark_mutation_contract.py | 121 ++++++++++++++++++ 12 files changed, 191 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b52bada3e..420f51a66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Changed +- Classified Manifest V3 bookmark create/get/remove failures as allow-listed stage tokens so raw Chrome bookmark errors and fixture titles cannot enter runner evidence. +- Recorded the current Chrome Extensions `chrome.bookmarks` primary reference in APA 7th form and stated that the active bookmark-mutation lane proves one controlled loopback lifecycle in pinned Chromium, not Agent bookmark authority. - Classified Manifest V3 real-click post-condition failures as a fixed mismatch token so page-controlled WebDriver text cannot enter runner exception text. - Recorded the current Chrome Extensions `chrome.downloads` primary reference in APA 7th form and stated that the active downloads lane proves one controlled loopback payload in pinned Chromium, not Agent filesystem authority. - Separated logical origin authority from resolved network destination authority; an origin grant no longer implies permission to connect to every resolver result. diff --git a/docs/DOCUMENTATION_FITNESS.md b/docs/DOCUMENTATION_FITNESS.md index 69f603252..11b5d5dd7 100644 --- a/docs/DOCUMENTATION_FITNESS.md +++ b/docs/DOCUMENTATION_FITNESS.md @@ -55,7 +55,7 @@ Protected main already proves a pinned-Chromium baseline for service worker, con - #43: controlled downloads; - #49: per-trial ephemeral profile isolation; -- #56: bookmark create/read/delete cleanup; +- #56: bookmark create/read/delete cleanup with allow-listed stage diagnostics; - #59: history add/read/delete/absence verification; - #60: trial-local unpacked-extension `1.0.0` → `1.0.1` update with explicit schema migration; and - #61: real content-script isolated-world evidence in which the page main world retains a `page` sentinel while the content script independently retains an `extension` sentinel. diff --git a/docs/PRD.md b/docs/PRD.md index 40539a28f..4b409f2dd 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -183,7 +183,7 @@ public-crawl purpose | ID | Requirement | Status | Implementation evidence / note | |---|---|---|---| | PRD-COMP-001 | Chromium is the compatibility kernel; OriginWeave does not reimplement Blink or V8 | Accepted architecture | ADR 0001 | -| PRD-COMP-002 | Maintain a Manifest V3 compatibility matrix and representative extension test farm | Planned | Partial protected-main pinned-Chromium evidence covers service worker, content script, storage, DNR, tabs, windows, scripting, commands, side panel, bookmarks, history, restart and repeatability; active PR #43 adds bounded real downloads evidence; issue #27 still owns the complete matrix/release acceptance | +| PRD-COMP-002 | Maintain a Manifest V3 compatibility matrix and representative extension test farm | Planned | Partial protected-main pinned-Chromium evidence covers service worker, content script, storage, DNR, tabs, windows, scripting, commands, side panel, bookmarks, history, restart and repeatability; active PR #43 adds bounded real downloads evidence; active PR #56 adds bounded bookmark mutation with allow-listed stage diagnostics; issue #27 still owns the complete matrix/release acceptance | | PRD-COMP-003 | Chromium-specific integrations remain behind versioned adapters | Planned | Adapter strategy ADR 0107 | | PRD-COMP-004 | Headless runtime remains independently usable without the interactive browser UI | Planned | Modular architecture target | @@ -267,7 +267,7 @@ public-crawl purpose | PRD-EXT-001 | Manifest V3 remains the extension compatibility baseline | Accepted architecture | Official Chrome platform baseline; real pinned-Chromium evidence exists on protected main | | PRD-EXT-002 | Upstream extension APIs are preserved where possible | Accepted architecture | Chromium-kernel strategy; current protected-main compatibility lane exercises multiple real MV3 APIs | | PRD-EXT-003 | Extension access to agent authority requires separate signed policy grant | Planned | Protected-main extension authority foundation exists, but the complete managed-extension/native-messaging/enterprise runtime contract remains open under issue #27; Proposed ADR 0013 does not itself make this shipped | -| PRD-EXT-004 | Compatibility tests cover install/update, worker lifecycle, scripts, storage, DNR, messaging, download, side panel and isolation | Planned | Protected-main suite already covers worker/content/storage/DNR/tabs/windows/scripting/commands/side panel/bookmarks/history/restart/repeatability; active PR #43 adds downloads; install/update/native messaging/enterprise isolation and release-wide matrix remain open under issue #27 | +| PRD-EXT-004 | Compatibility tests cover install/update, worker lifecycle, scripts, storage, DNR, messaging, download, side panel and isolation | Planned | Protected-main suite already covers worker/content/storage/DNR/tabs/windows/scripting/commands/side panel/bookmarks/history/restart/repeatability; active PR #43 adds downloads; active PR #56 adds bookmark create/get/remove diagnostics; install/update/native messaging/enterprise isolation and release-wide matrix remain open under issue #27 | ### 9.10 Crawler and capture policy diff --git a/docs/TRD.md b/docs/TRD.md index 3e8030012..bb5292833 100644 --- a/docs/TRD.md +++ b/docs/TRD.md @@ -339,7 +339,7 @@ Generic network evidence retains bounded names and canonical locators while valu The complete compatibility program is **Planned** under issue #27, while partial real-browser evidence exists on protected main. OriginWeave preserves Chromium's extension implementation rather than rebuilding Chrome APIs in Rust. Agent authority remains separate from ordinary extension permissions. Proposed ADR 0013 documents this separation but is not Accepted design authority until reviewed/integrated accordingly. -Protected-main pinned-Chromium evidence currently exercises service-worker lifecycle, content scripts, storage, declarativeNetRequest, tabs, windows, scripting, commands, side panel, bookmarks, history, restart persistence and repeatability. Active PR #43 adds a bounded real `chrome.downloads` path and allowlisted download-stage failure evidence. Installation/update, native messaging, managed-extension/enterprise policy, broader isolation, Web Store and release-wide compatibility remain outside the current protected-main claim. +Protected-main pinned-Chromium evidence currently exercises service-worker lifecycle, content scripts, storage, declarativeNetRequest, tabs, windows, scripting, commands, side panel, bookmarks, history, restart persistence and repeatability. Active PR #43 adds a bounded real `chrome.downloads` path and allowlisted download-stage failure evidence. Active PR #56 adds a bounded `chrome.bookmarks` create/get/remove lifecycle with allow-listed stage diagnostics. Installation/update, native messaging, managed-extension/enterprise policy, broader isolation, Web Store and release-wide compatibility remain outside the current protected-main claim. ## 14. Prompt-injection and model boundary diff --git a/docs/doctoring.md b/docs/doctoring.md index 8030e1156..5c58f2bf7 100644 --- a/docs/doctoring.md +++ b/docs/doctoring.md @@ -12,6 +12,10 @@ The 1 June 2026 WebDriver BiDi Working Draft defines a bidirectional remote-cont The current Chrome Extensions Downloads API documents the `downloads` manifest permission and `chrome.downloads` methods that initiate, monitor, search, and inspect downloads. That living vendor reference is API semantics only. OriginWeave treats a successful controlled loopback download in pinned Chromium as compatibility evidence for one declared surface, not as Agent filesystem authority, general download persistence, or a claim that every Downloads method is supported. +### Manifest V3 bookmarks compatibility + +The current Chrome Extensions Bookmarks API documents the `bookmarks` manifest permission and Promise-returning `chrome.bookmarks.create`, `chrome.bookmarks.get`, and `chrome.bookmarks.remove` methods. Bookmark node identifiers are strings unique within one browser profile. That living vendor reference is API semantics only. OriginWeave treats one controlled loopback create → get → remove lifecycle plus allow-listed stage diagnostics as compatibility evidence, not as Agent bookmark capability or ambient human-profile bookmark authority. + ### Manifest V3 click post-condition diagnostics W3C WebDriver Get Element Text returns the rendered text content of a located element. That value is page-controlled data, not a trusted diagnostic token. The Manifest V3 compatibility runner therefore compares the fixture output against the exact expected `clicked` token and, on mismatch, raises only the classified message `real click post-condition mismatch`. Raw element text must not enter exception text, trial evidence, or logs. @@ -106,6 +110,8 @@ Autio, C., Schwartz, R., Dunietz, J., Jain, S., Stanley, M., Tabassi, E., Hall, Bonica, R., Cotton, M., Haberman, B., & Vegoda, L. (2017). *Updates to the special-purpose IP address registries* (RFC 8190). Internet Engineering Task Force. https://doi.org/10.17487/RFC8190 +Chrome for Developers. (n.d.). *chrome.bookmarks*. Google. Retrieved August 16, 2026, from https://developer.chrome.com/docs/extensions/reference/api/bookmarks + Chrome for Developers. (n.d.). *chrome.downloads*. Google. Retrieved August 16, 2026, from https://developer.chrome.com/docs/extensions/reference/api/downloads Chromium Authors. (n.d.). *Proxy support in Chrome* [Source documentation]. Chromium. https://chromium.googlesource.com/chromium/src/+/a3e71ebfa307d8760eb68b777e2998a869940092/net/docs/proxy.md diff --git a/docs/doctoring/mv3-compatibility.md b/docs/doctoring/mv3-compatibility.md index 7fe360bec..554c22a9b 100644 --- a/docs/doctoring/mv3-compatibility.md +++ b/docs/doctoring/mv3-compatibility.md @@ -22,7 +22,7 @@ This matrix separates protected-main executable evidence from active, non-shippe | `declarativeNetRequest` | **PROTECTED_MAIN** | Controlled local rule blocks its fixture request in pinned Chromium. | No claim for every DNR rule/action combination. | | `tabs`, `windows`, `scripting`, `commands`, `sidePanel` | **PROTECTED_MAIN** | Each declared API is exercised in real Chromium and required by the repeatability gate. | Chrome API permission does not become Agent capability. | | Bookmarks read compatibility | **PROTECTED_MAIN** | Protected-main fixture exercises the declared bookmarks surface. | Ambient human-profile bookmark authority is not granted. | -| Bookmarks create/read/delete lifecycle | **ACTIVE_PR #56** | Controlled synthetic bookmark is created, read back, and removed in the ephemeral compatibility profile. | Compatibility only; no Agent bookmark capability. | +| Bookmarks create/read/delete lifecycle | **ACTIVE_PR #56** | Controlled synthetic bookmark is created, read back, and removed in the ephemeral compatibility profile, with allow-listed stage diagnostics. | Compatibility only; no Agent bookmark capability. | | History read compatibility | **PROTECTED_MAIN** | Protected-main fixture exercises bounded history search in the isolated profile. | No model-visible browsing-history content or default-profile access. | | History add/read/delete lifecycle | **ACTIVE_PR #59** | Controlled synthetic loopback visit is added, exactly read back, deleted in `finally`, and required to be absent afterward. | Compatibility only; no Agent history capability. | | Downloads | **ACTIVE_PR #43** | Controlled loopback payload is downloaded and validated through pinned Chromium. | No general download persistence, unsafe filename, or Agent filesystem authority claim. | @@ -34,6 +34,10 @@ This matrix separates protected-main executable evidence from active, non-shippe The release-quality capability matrix must remain coupled to executable evidence. Adding a row to documentation never creates support; declaring a new supported capability must first add a realistic regression test and pinned-Chromium proof. Conversely, if a declared protected-main capability regresses, the release gate must fail rather than silently downgrading the matrix. +## Bookmarks API primary evidence + +For bookmark compatibility specifically, the current official Chrome Extensions API documents the `bookmarks` manifest permission and Promise-returning `chrome.bookmarks.create`, `chrome.bookmarks.get`, and `chrome.bookmarks.remove` methods. Bookmark node identifiers are strings unique within one browser profile. This living vendor reference establishes API semantics only. Active PR #56 exercises one controlled loopback create → get → remove lifecycle through pinned Chromium and retains only allow-listed stage diagnostics. That proof is not Agent bookmark capability, ambient human-profile bookmark authority, or a release claim that every `chrome.bookmarks` method works. + ## History API primary evidence For history compatibility specifically, the current official Chrome Extensions API documents the `history` manifest permission and Promise-returning `chrome.history.addUrl`, `chrome.history.search`, and `chrome.history.deleteUrl` methods. This living vendor reference establishes API semantics only. OriginWeave release evidence continues to depend on the exact pinned Chromium fixture and exact-head CI result rather than inferring compatibility from documentation. @@ -68,6 +72,8 @@ Chrome for Developers. (2023, May 2). *The extension service worker lifecycle*. Chrome for Developers. (n.d.). *chrome.declarativeNetRequest*. Google. Retrieved August 9, 2026, from https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest +Chrome for Developers. (n.d.). *chrome.bookmarks*. Google. Retrieved August 16, 2026, from https://developer.chrome.com/docs/extensions/reference/api/bookmarks + Chrome for Developers. (n.d.). *chrome.downloads*. Google. Retrieved August 16, 2026, from https://developer.chrome.com/docs/extensions/reference/api/downloads Chrome for Developers. (n.d.). *chrome.history*. Google. Retrieved August 11, 2026, from https://developer.chrome.com/docs/extensions/reference/api/history diff --git a/docs/evidence/2026-08-10-active-pr-maturity.md b/docs/evidence/2026-08-10-active-pr-maturity.md index 71353dca2..e9d3868f4 100644 --- a/docs/evidence/2026-08-10-active-pr-maturity.md +++ b/docs/evidence/2026-08-10-active-pr-maturity.md @@ -31,7 +31,7 @@ This dated appendix records volatile implementation evidence that must not be em | #53 | Authoritative in-process sensitive-handle revocation state | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on #46 at exact head `86ce4bc1c11c270dc532593d673c42bd6f623d74`; CI and CodeRabbit are green. It adds typed first-revocation-wins state but no durable broker, cross-process transactionality, protected-value resolution, KMS, or persistence. | | #54 | Recheck resolution freshness at socket use | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on #50 at exact head `ec81031c537f2b662910c1ce78c7ae0e0bfc9c1e`; CI and CodeRabbit are green. `connect_at` revalidates freshness immediately before socket I/O and the compatibility path derives elapsed monotonic time; no resolver, DNS lookup, proxy/PAC or wall-clock authority is added. | | #55 | Bind opaque sensitive-value handle use to a non-transferable audience | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on exact #53 head `86ce4bc1c11c270dc532593d673c42bd6f623d74`. Test-only head `95f0f1e418024f5dbe7aa613e5fd1e9d88a9417a` and CI run `31419991170` proved a real regression: audience binding had caused a revoked handle with later mismatched policy state to return `ScopeMismatch` instead of authoritative `Revoked`. Current exact head `8d3ccf0a3b99fd9789210dd9798b422431fab7d8` restores revocation precedence, retains audience binding, and adds a synchronized one-use concurrency regression. CI run `31421061134` passes repository contracts, rustfmt, locked workspace check, all workspace tests, strict Clippy, rustdoc and exact owned production function/line/region/branch coverage; CodeRabbit exact-head status is success. A future trusted broker must still derive the audience from authenticated workload/service identity. | -| #56 | Real pinned-Chromium bookmark mutation compatibility | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on #43. Exact head `e1099e35ac000c7bf87ea75666cfdd928a386370` aligns the fixture and repository contracts with the bounded create → get → remove bookmark lifecycle; CI run `31427219564`, Manifest V3 Compatibility run `31427220684`, and CodeRabbit exact-head status all succeed. This is compatibility evidence only: it grants no OriginWeave Agent capability and does not complete issue #27's full extension matrix. | +| #56 | Real pinned-Chromium bookmark mutation compatibility | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on #43. Exact head `82403ecac1d9395871b6b05df150c47a8bbbc749` aligns the fixture and repository contracts with the bounded create → get → remove bookmark lifecycle; CI run `31959457551` includes a successful pinned-Chromium MV3 fixture job. Successor work on this lane adds allow-listed bookmark stage diagnostics so raw Chrome errors cannot enter runner evidence. This is compatibility evidence only: it grants no OriginWeave Agent capability and does not complete issue #27's full extension matrix. | | #57 | Typed semantic-node query over bounded observation evidence | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on exact #52 head `94fd284fe41746eeba9edc05d9753903b1c41ebf`. Test-only head `d0cd133f5be62fff99612d5b08aa4cf08ce2f29f` and CI run `31429065905` intentionally proved the missing public query boundary by failing compilation on absent `SemanticNodeQuery`/`SemanticNodeQueryError`. Current exact head `b4fa49953cbbb21c879a3340e264a6e132e41634` implements bounded exact role, accessible-name and typed-action selection against already validated `SemanticNodeObservation` values, with no CSS/XPath/raw DOM selector language, arbitrary JavaScript, browser I/O or action authority. CI run `31429995885`, Manifest V3 Compatibility run `31429997851`, and CodeRabbit exact-head status succeed. The PR remains Draft because #52/#40 are active prerequisites. | | #58 | Authority-bound semantic-node action target | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on #57. Current exact head `efe440c7a609cac187faacfa03a4df904a99386f` accepts only an advertised `NodeActionKind`, carries the exact OriginWeave-owned node handle, and delegates immediate-use session/context/origin/document-epoch validation to the browser authority boundary. CI run `31431277478`, Manifest V3 Compatibility run `31431277521`, and CodeRabbit exact-head status succeed. This remains descriptive execution input, not policy authorization, business-risk classification, browser I/O or action success. | | #59 | Real pinned-Chromium history mutation compatibility | **IMPLEMENTED_ON_ACTIVE_PR** | Draft stacked on #56. Test-only head `4b5f393a7420541723a07243b83cdaa7e28948de` and CI run `31432051381` established the intended repository-contract RED because controlled `history.addUrl`/`deleteUrl` lifecycle support was absent. Current exact head `b0d9c905fd7a50128eb1dde643b8a3a0f9cb1dc8` adds loopback-only add → exact readback → delete → absence verification. CI run `31432338572`, Manifest V3 Compatibility run `31432338759`, and CodeRabbit exact-head status succeed, including exact owned production function/line/region/branch coverage. Compatibility evidence only; no Agent history capability. | diff --git a/docs/traceability/README.md b/docs/traceability/README.md index e30b9eda1..edb8a04aa 100644 --- a/docs/traceability/README.md +++ b/docs/traceability/README.md @@ -69,7 +69,7 @@ ADR lifecycle is separate and remains `Proposed`, `Accepted`, `Superseded`, `Dep | Human interaction outranks inference/background collection | PARTIAL | `ARCHITECTURE.md`; PRD-RES-002 | Deterministic resource mitigation/CPU-worker admission foundations exist; platform telemetry/actuation remain Planned | | Structured observation precedes raw HTML/screenshot fallback | ACCEPTED_ARCHITECTURE | PRD-OBS-003; TRD Section 7 | Active PR #52 supplies a non-shipped bounded semantic value primitive; real browser observation and fallback adapters remain Planned | | WebDriver BiDi / CDP / WebMCP / MCP are adapters, not internal authority | ACCEPTED_ARCHITECTURE | PRD Section 9.8; TRD Section 12 | Protocol adapter implementation remains Planned/active under issue #28; active PR #40 may not be called shipped | -| Manifest V3 compatibility is preserved upstream where practical | PARTIAL | ADR 0001; issue #27; Proposed ADR 0013 | Protected main has pinned real-Chromium compatibility evidence for service worker/content script/storage/DNR/tabs/windows/scripting/commands/side panel/bookmarks/history/restart/repeatability; active PR #43 adds real bounded downloads evidence; full issue #27 matrix remains incomplete | +| Manifest V3 compatibility is preserved upstream where practical | PARTIAL | ADR 0001; issue #27; Proposed ADR 0013 | Protected main has pinned real-Chromium compatibility evidence for service worker/content script/storage/DNR/tabs/windows/scripting/commands/side panel/bookmarks/history/restart/repeatability; active PR #43 adds real bounded downloads evidence; active PR #56 adds bookmark mutation with allow-listed stage diagnostics; full issue #27 matrix remains incomplete | | Extension permission does not imply OriginWeave Agent capability | PARTIAL | protected-main extension authority kernel; Proposed ADR 0013 | Core extension-to-Agent authority isolation exists on protected main; complete managed-extension/native-messaging/enterprise release policy remains incomplete | | WARC/PROV-oriented durable evidence adapters | PLANNED | ADR 0003; PRD-EVD-005 | Source/provenance kernel foundation exists; persistence/export adapters remain Planned | | Origin Map visualizes value/action provenance | PLANNED | PRD-EVD-004; this traceability record | No shipped UI claim | diff --git a/scripts/ci/run_mv3_compatibility.py b/scripts/ci/run_mv3_compatibility.py index 614c0e736..be73d28d9 100644 --- a/scripts/ci/run_mv3_compatibility.py +++ b/scripts/ci/run_mv3_compatibility.py @@ -56,6 +56,7 @@ "commands", "sidePanel", "bookmarks", + "bookmarksDiagnostic", "history", "downloads", "downloadsDiagnostic", @@ -77,6 +78,19 @@ "download-not-evaluated", } ) +BOOKMARK_DIAGNOSTIC_VALUES = frozenset( + { + "bookmark-source-rejected", + "bookmark-create-rejected", + "bookmark-get-missing", + "bookmark-id-mismatch", + "bookmark-title-mismatch", + "bookmark-url-mismatch", + "bookmark-remove-rejected", + "bookmark-complete-ready", + "bookmark-not-evaluated", + } +) class CompatibilitySurfaceError(RuntimeError): @@ -109,6 +123,8 @@ def _safe_surface_value(key: str, value: str) -> str: return value if value.isdecimal() and len(value) <= 20 else "invalid" if key == "downloadsDiagnostic": return value if value in DOWNLOAD_DIAGNOSTIC_VALUES else "unexpected" + if key == "bookmarksDiagnostic": + return value if value in BOOKMARK_DIAGNOSTIC_VALUES else "unexpected" return value if value in SURFACE_EVIDENCE_VALUES else "unexpected" @@ -260,6 +276,8 @@ def _wait_for_extension_evidence( commands: document.documentElement.dataset.originweaveCommands || "missing", sidePanel: document.documentElement.dataset.originweaveSidePanel || "missing", bookmarks: document.documentElement.dataset.originweaveBookmarks || "missing", + bookmarksDiagnostic: + document.documentElement.dataset.originweaveBookmarksDiagnostic || "bookmark-not-evaluated", history: document.documentElement.dataset.originweaveHistory || "missing", downloads: document.documentElement.dataset.originweaveDownloads || "missing", downloadsDiagnostic: @@ -280,6 +298,7 @@ def _wait_for_extension_evidence( "commands": "ready", "sidePanel": "ready", "bookmarks": "ready", + "bookmarksDiagnostic": "bookmark-complete-ready", "history": "ready", "downloads": "ready", "downloadsDiagnostic": "download-complete-ready", diff --git a/tests/fixtures/mv3_basic/content_script.js b/tests/fixtures/mv3_basic/content_script.js index b70d1a27f..352dc6393 100644 --- a/tests/fixtures/mv3_basic/content_script.js +++ b/tests/fixtures/mv3_basic/content_script.js @@ -26,6 +26,8 @@ document.documentElement.dataset.originweaveCommands = response?.commands ?? "missing"; document.documentElement.dataset.originweaveSidePanel = response?.sidePanel ?? "missing"; document.documentElement.dataset.originweaveBookmarks = response?.bookmarks ?? "missing"; + document.documentElement.dataset.originweaveBookmarksDiagnostic = + response?.bookmarksDiagnostic ?? "bookmark-not-evaluated"; document.documentElement.dataset.originweaveHistory = response?.history ?? "missing"; document.documentElement.dataset.originweaveDownloads = response?.downloads ?? "missing"; document.documentElement.dataset.originweaveDownloadsDiagnostic = diff --git a/tests/fixtures/mv3_basic/service_worker.js b/tests/fixtures/mv3_basic/service_worker.js index a8cec9575..d20e40168 100644 --- a/tests/fixtures/mv3_basic/service_worker.js +++ b/tests/fixtures/mv3_basic/service_worker.js @@ -102,14 +102,14 @@ async function exerciseDownload(sender) { async function exerciseBookmarkMutation(sender) { const sourceUrl = sender?.tab?.url; if (typeof sourceUrl !== "string") { - return false; + return { ready: false, diagnostic: "bookmark-source-rejected" }; } let parsed; try { parsed = new URL(sourceUrl); } catch (_error) { - return false; + return { ready: false, diagnostic: "bookmark-source-rejected" }; } if ( parsed.protocol !== "http:" || @@ -118,40 +118,50 @@ async function exerciseBookmarkMutation(sender) { parsed.username !== "" || parsed.password !== "" ) { - return false; + return { ready: false, diagnostic: "bookmark-source-rejected" }; } const title = "OriginWeave MV3 compatibility bookmark"; let bookmarkId; try { const created = await chrome.bookmarks.create({ title, url: sourceUrl }); - if (typeof created?.id !== "string" || created.id.length === 0) { - return false; + const createdId = created?.id; + if (typeof createdId !== "string" || createdId.length === 0) { + return { ready: false, diagnostic: "bookmark-create-rejected" }; } - bookmarkId = created.id; + bookmarkId = createdId; } catch (_error) { - return false; + return { ready: false, diagnostic: "bookmark-create-rejected" }; } + let diagnostic = "bookmark-get-missing"; let bookmarkMutationReady = false; try { const nodes = await chrome.bookmarks.get(bookmarkId); - bookmarkMutationReady = - Array.isArray(nodes) && - nodes.length === 1 && - nodes[0]?.id === bookmarkId && - nodes[0]?.title === title && - nodes[0]?.url === sourceUrl; + if (!Array.isArray(nodes) || nodes.length !== 1) { + diagnostic = "bookmark-get-missing"; + } else if (nodes[0]?.id !== bookmarkId) { + diagnostic = "bookmark-id-mismatch"; + } else if (nodes[0]?.title !== title) { + diagnostic = "bookmark-title-mismatch"; + } else if (nodes[0]?.url !== sourceUrl) { + diagnostic = "bookmark-url-mismatch"; + } else { + diagnostic = "bookmark-complete-ready"; + bookmarkMutationReady = true; + } } catch (_error) { + diagnostic = "bookmark-get-missing"; bookmarkMutationReady = false; } finally { try { await chrome.bookmarks.remove(bookmarkId); } catch (_error) { + diagnostic = "bookmark-remove-rejected"; bookmarkMutationReady = false; } } - return bookmarkMutationReady; + return { ready: bookmarkMutationReady, diagnostic }; } async function exerciseCoreApis(sender) { @@ -184,8 +194,8 @@ async function exerciseCoreApis(sender) { const sidePanelOptions = await chrome.sidePanel.getOptions({ tabId }); const sidePanelReady = sidePanelOptions?.path === "side_panel.html"; - const bookmarkMutationReady = await exerciseBookmarkMutation(sender); - const bookmarksReady = bookmarkMutationReady; + const bookmarkResult = await exerciseBookmarkMutation(sender); + const bookmarksReady = bookmarkResult.ready; const historyItems = await chrome.history.search({ text: "", @@ -204,6 +214,7 @@ async function exerciseCoreApis(sender) { commands: commandsReady ? "ready" : "missing", sidePanel: sidePanelReady ? "ready" : "missing", bookmarks: bookmarksReady ? "ready" : "missing", + bookmarksDiagnostic: bookmarkResult.diagnostic, history: historyReady ? "ready" : "missing", downloads: downloadsReady ? "ready" : "missing", downloadsDiagnostic: downloadResult.diagnostic, @@ -233,6 +244,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { commands: "missing", sidePanel: "missing", bookmarks: "missing", + bookmarksDiagnostic: "bookmark-not-evaluated", history: "missing", downloads: "missing", downloadsDiagnostic: "download-not-evaluated", diff --git a/tests/test_mv3_bookmark_mutation_contract.py b/tests/test_mv3_bookmark_mutation_contract.py index 4c121b19f..c5e41f752 100644 --- a/tests/test_mv3_bookmark_mutation_contract.py +++ b/tests/test_mv3_bookmark_mutation_contract.py @@ -2,12 +2,27 @@ from __future__ import annotations +import importlib.util import json import pathlib import unittest ROOT = pathlib.Path(__file__).resolve().parents[1] FIXTURE = ROOT / "tests" / "fixtures" / "mv3_basic" +RUNNER = ROOT / "scripts" / "ci" / "run_mv3_compatibility.py" +DOCTORING = ROOT / "docs" / "doctoring" / "mv3-compatibility.md" +ROOT_DOCTORING = ROOT / "docs" / "doctoring.md" + + +def _load_runner_module(): + """Load the compatibility runner without invoking its command-line entry point.""" + + spec = importlib.util.spec_from_file_location("originweave_mv3_runner", RUNNER) + if spec is None or spec.loader is None: + raise AssertionError("unable to load the MV3 compatibility runner") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module class ManifestV3BookmarkMutationContractTests(unittest.TestCase): @@ -50,6 +65,112 @@ def test_bookmark_mutation_is_bound_to_controlled_fixture_url_and_cleanup(self) self.assertNotIn("_error.message", worker) self.assertNotIn("String(_error)", worker) + def test_bookmark_failures_emit_only_bounded_stage_diagnostics(self) -> None: + """Fixture diagnostics must name a reviewed stage without retaining raw browser errors.""" + + worker = (FIXTURE / "service_worker.js").read_text(encoding="utf-8") + content = (FIXTURE / "content_script.js").read_text(encoding="utf-8") + for expected in ( + "bookmark-source-rejected", + "bookmark-create-rejected", + "bookmark-get-missing", + "bookmark-id-mismatch", + "bookmark-title-mismatch", + "bookmark-url-mismatch", + "bookmark-remove-rejected", + "bookmark-complete-ready", + "bookmarksDiagnostic", + ): + with self.subTest(expected=expected): + self.assertIn(expected, worker) + self.assertIn("originweaveBookmarksDiagnostic", content) + self.assertNotIn("created.id", worker) + self.assertNotIn("nodes[0].url", worker) + self.assertNotIn("_error.message", worker) + self.assertNotIn("String(_error)", worker) + + def test_content_script_and_runner_require_bookmark_diagnostics_on_every_pass(self) -> None: + """The compatibility report must retain a classified bookmark stage on every trial.""" + + content = (FIXTURE / "content_script.js").read_text(encoding="utf-8") + runner = RUNNER.read_text(encoding="utf-8") + self.assertIn("originweaveBookmarks", content) + self.assertIn("originweaveBookmarksDiagnostic", content) + self.assertIn('"bookmarks": surfaces["bookmarks"] == "ready"', runner) + self.assertIn('"bookmarksDiagnostic": "bookmark-complete-ready"', runner) + + def test_runner_preserves_only_reviewed_bookmark_diagnostic_tokens(self) -> None: + """Runner failure evidence must retain stage tokens while rejecting raw diagnostics.""" + + runner = _load_runner_module() + approved = { + "bookmark-source-rejected", + "bookmark-create-rejected", + "bookmark-get-missing", + "bookmark-id-mismatch", + "bookmark-title-mismatch", + "bookmark-url-mismatch", + "bookmark-remove-rejected", + "bookmark-complete-ready", + "bookmark-not-evaluated", + } + self.assertIn("bookmarksDiagnostic", runner.SURFACE_EVIDENCE_KEYS) + self.assertEqual(runner.BOOKMARK_DIAGNOSTIC_VALUES, frozenset(approved)) + for token in approved: + with self.subTest(token=token): + self.assertEqual( + runner._safe_surface_value("bookmarksDiagnostic", token), token + ) + + approved_error = runner.CompatibilitySurfaceError( + { + "bookmarks": "missing", + "bookmarksDiagnostic": "bookmark-source-rejected", + } + ) + approved_evidence = runner._failure_evidence(approved_error) + self.assertEqual( + approved_evidence["observed"]["bookmarksDiagnostic"], + "bookmark-source-rejected", + ) + + raw_bookmark_title = "OriginWeave MV3 compatibility bookmark" + raw_browser_error = "Error: secret bookmark failure" + for raw in (raw_bookmark_title, raw_browser_error): + with self.subTest(raw=raw): + error = runner.CompatibilitySurfaceError( + { + "bookmarks": "missing", + "bookmarksDiagnostic": raw, + } + ) + evidence = runner._failure_evidence(error) + self.assertEqual( + evidence["observed"]["bookmarksDiagnostic"], "unexpected" + ) + self.assertNotIn(raw, repr(evidence)) + + def test_doctoring_records_bookmarks_api_primary_citation(self) -> None: + """The living Chrome Bookmarks API reference must stay distinct from Agent authority.""" + + doctoring = DOCTORING.read_text(encoding="utf-8") + root_doctoring = ROOT_DOCTORING.read_text(encoding="utf-8") + changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8") + for expected in ( + "chrome.bookmarks", + "https://developer.chrome.com/docs/extensions/reference/api/bookmarks", + "allow-listed stage diagnostics", + "no Agent bookmark capability", + ): + with self.subTest(expected=expected): + self.assertIn(expected, doctoring) + self.assertIn("*chrome.bookmarks*", root_doctoring) + self.assertIn( + "https://developer.chrome.com/docs/extensions/reference/api/bookmarks", + root_doctoring, + ) + self.assertIn("chrome.bookmarks", changelog) + if __name__ == "__main__": unittest.main() From 3083a8430f8971540f4ffe5d291955b6eb13b31f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 17 Aug 2026 11:53:01 +0900 Subject: [PATCH 2/2] fix(mv3): restore bookmark citation in canonical doctoring Restore the root doctoring decision trace and APA 7 primary Chrome Bookmarks API citation required by the bookmark-mutation contract, while preserving the current extension-origin/expiry and other protected-main doctoring evidence. This fixes the exact-head repository-contract failure without weakening the test or duplicating stale generic claims. --- docs/doctoring.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/doctoring.md b/docs/doctoring.md index 14bbca978..04ae737a4 100644 --- a/docs/doctoring.md +++ b/docs/doctoring.md @@ -12,6 +12,10 @@ The 1 June 2026 WebDriver BiDi Working Draft defines a bidirectional remote-cont The current Chrome Extensions Downloads API documents the `downloads` manifest permission and `chrome.downloads` methods that initiate, monitor, search, and inspect downloads. That living vendor reference is API semantics only. OriginWeave treats a successful controlled loopback download in pinned Chromium as compatibility evidence for one declared surface, not as Agent filesystem authority, general download persistence, or a claim that every Downloads method is supported. +### Manifest V3 bookmarks compatibility + +The current Chrome Extensions Bookmarks API documents the `bookmarks` manifest permission and Promise-returning `chrome.bookmarks.create`, `chrome.bookmarks.get`, and `chrome.bookmarks.remove` methods. Bookmark node identifiers are strings unique within one browser profile. That living vendor reference is API semantics only. OriginWeave treats one controlled loopback create → get → remove lifecycle plus allow-listed stage diagnostics as compatibility evidence, not as Agent bookmark capability or ambient human-profile bookmark authority. + ### Manifest V3 WebDriver transport-protocol diagnostics RFC 9112 defines the HTTP/1.1 status-line and the requirement that a message body match the announced framing. A malformed status-line or an incomplete body is a recoverable parser failure, not a trusted diagnostic payload. W3C WebDriver carries commands over that HTTP transport. The Manifest V3 compatibility runner therefore converts `http.client.HTTPException` subclasses such as `BadStatusLine` and `IncompleteRead` into the classified message `WebDriver transport protocol failure`. Raw status-line text, partial body bytes, paths, URLs, or tokens must not enter exception text, trial evidence, or logs. @@ -120,6 +124,8 @@ Barth, A. (2011). *The web origin concept* (RFC 6454). Internet Engineering Task Bonica, R., Cotton, M., Haberman, B., & Vegoda, L. (2017). *Updates to the special-purpose IP address registries* (RFC 8190). Internet Engineering Task Force. https://doi.org/10.17487/RFC8190 +Chrome for Developers. (n.d.). *chrome.bookmarks*. Google. Retrieved August 16, 2026, from https://developer.chrome.com/docs/extensions/reference/api/bookmarks + Chrome for Developers. (n.d.). *chrome.downloads*. Google. Retrieved August 16, 2026, from https://developer.chrome.com/docs/extensions/reference/api/downloads Chromium Authors. (n.d.). *Proxy support in Chrome* [Source documentation]. Chromium. https://chromium.googlesource.com/chromium/src/+/a3e71ebfa307d8760eb68b777e2998a869940092/net/docs/proxy.md