Table of Contents
Provenance: Privacy:Privacy Sandbox
Back to Measuring the Privacy Sandbox. Corpus-wide selection and extraction notes are on corpus. This page is the query log, the folding rules with their measured residue, the browser probes with their unedited output, and the record of what could not be established.
No ~~DISCUSSION~~ block: comments belong on the content page. This is the convention the existing provenance: pages follow and this page keeps it.
Run record
- Run date: 2026-09-01 (UTC). Removal status refreshed 2026-09-27; everything about that refresh is in Removal-status refresh, 2026-09-27, and it supersedes the milestone statements elsewhere on this page.
- Authoring agent: Claude Opus 5, executing the drain item
privacy:privacy_sandbox (new). Two background research sub-agents (sonnet) for the currency and tooling passes; every load-bearing fact they returned was re-fetched and re-verified by the authoring agent before it reached the page — see Sub-agent findings that did not survive verification, which is the part of this log with the most value. - Corpus at run time: 5,859 extracted papers, 2010–2026, seven venues (CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf, IEEE S&P). The full-text sweep runs over the 5,869 papers that have a rendered
paper.cols.txt; the 10-paper difference is papers with text but no extraction record, and it is why the page names 5,869 as the full-text denominator and 5,859 as the extraction denominator rather than using one number for both. - Read-only inputs under
/workspace/publications_dataset/data/. Nothing was written there. - Browser measurements: Chromium 151.0.7922.34 as shipped with Playwright 1.62.1, Linux container, headless. Puppeteer was pointed at the same binary via
chromium.executablePath()because the container's own/usr/bin/chromiumcannot be driven (the crashpad-flag problem documented in the workdir README). (2026-09-27: not the same binary.chromium.executablePath()is the full Chromium, and Playwright's default is chrome-headless-shell; see What the 2026-09-01 page said that did not hold. Debian's Chromium can in fact be driven; see Milestone probe, 2026-09-27.) - This is a new page, not an extension. See Why a new page and not a broadened neighbour.
- Wiki saves through
scripts/dw.mjs(JSON-RPC). Bibliography saved with--if-rev 1788205676.
Why a new page and not a broadened neighbour
| Neighbour | What it already covers | What it does not |
|---|---|---|
| cookies | The 2025-04-22 reversal, in one paragraph, correctly | Nothing about any replacement API. No mention of Topics, Protected Audience, Attribution Reporting, Shared Storage, CHIPS or RWS |
| browser_storage | Chrome's storage partitioning since M115, CHIPS named as “opt-in per cookie” in one table cell | No CHIPS adoption figure, no ads APIs |
| stateful_stateless | Playwright's ThirdPartyStoragePartitioning opt-out, the 2025-04-22 reversal | No Privacy Sandbox API |
| cookie_syncing, server_side_tracking | Both carry the “third-party cookies are not disappearing” correction | Neither mentions the replacement APIs |
Grepped every page in the privacy:, programming: and design: namespaces for privacy sandbox, topics api, protected audience, fledge, attribution reporting, CHIPS, storage access, related website, trust token, fenced frame, shared storage, bounce tracking and partition. The only Privacy Sandbox hits anywhere on the site were the 2025-04-22 reversal (three pages) and one developers.google.com/privacy-sandbox URL cited as the source for Chrome's storage partitioning. Nothing on the site covered the replacement APIs. Broadening cookies was rejected because that page is about classifying cookies, its own namespace page already flags it as the shortest of the ten children, and a status-and-instrumentation page for eleven APIs would have swamped it.
Population and queries
All counts are papers, never tuples. Sentinels are never counted as answers. Every figure below is reproduced by scripts/report_privacy_sandbox.mjs; its unedited output is in Report script output.
| Query | Denominator | Result | On the page? |
|---|---|---|---|
Full text names /Privacy\s+Sandbox/i at least once | 5,869 papers with paper.cols.txt | 43 (0.7%) | yes |
| Same, five or more times | 5,869 | 9 (0.2%) | yes |
| Names at least one folded API family | 5,869 | 67 (1.1%) | yes |
| Topics family (incl. FLoC) | 5,869 | 22 | yes |
| UA Client Hints / UA reduction | 5,869 | 10 | yes |
| Protected Audience family (incl. FLEDGE, TURTLEDOVE) | 5,869 | 8 | yes |
| Privacy Sandbox on Android | 5,869 | 7 | yes |
| CHIPS | 5,869 | 5 | yes |
| Private State Tokens (incl. Trust Tokens) | 5,869 | 5 | yes |
| Fenced Frames | 5,869 | 4 | yes |
| Related Website Sets (incl. First-Party Sets) | 5,869 | 4 | yes |
| Attribution Reporting | 5,869 | 3 | yes |
| Shared Storage | 5,869 | 3 | yes |
| Private Aggregation | 5,869 | 2 | yes |
| Storage Access API | 5,869 | 2 | yes |
| FedCM | 5,869 | 1 | yes |
| IP Protection | 5,869 | 0 | yes |
| Bounce Tracking Mitigations, by that name | 5,869 | 0 | yes |
| Web crawls 2023–2026 naming an API family | 332 web crawls | 24 (7.2%) | yes |
| Web crawls 2024–2026 naming an API family | 235 web crawls | 18 (7.7%) | yes |
| Web crawls 2025–2026 naming an API family | 153 web crawls | 11 (7.2%) | yes |
| Chrome/Chromium-stating web crawls 2023–2026 | 129 | 11 (8.5%) | no |
| Chrome/Chromium-stating web crawls 2024–2026 | 86 | 8 (9.3%) | yes |
“Web crawl” is crawlConfig != null OR studyTypes contains automated-web-crawl, AND platforms contains web — the same definition lib.mjs uses for POPULATIONS.webCrawled.
The Chrome row's denominator is not “papers that drove Chrome”. It is papers that stated a browser containing chrom. crawlConfig.browsers is stated by only 47.2% of the 1,120 crawling papers, so 86 is a lower bound and 9.3% is a share of the ones that told you. The page says so.
Folding and residue
detection.phenomenon and classification.resourceName were not usable here: nothing in the extraction schema has a field for “measured the Topics API”, and the free-text fields agree run-to-run on only about 20% of exact strings. The topic's reach can therefore only be established from full text. scripts/ps_fold.mjs holds 15 ordered families, each an alternation of the current name and every former name, applied to whitespace-collapsed text with soft hyphens rejoined.
Every regex below was measured against the corpus before it was accepted or rejected. The rejected ones are the useful half of this section.
| Rejected pattern | Papers it matched | Why rejected |
|---|---|---|
/\bCHIPS\b/ unqualified | 231 | Silicon. Rowhammer and GPU papers |
/\bCHIPS\b/ within 200 chars of “partition” | 8 | DRAM bank partitioning; 3 of the 8 were silicon |
/\bthe Topics\b/ | 216 | Ordinary English |
/interest\s+group/ bare | 234 | Political science, federated learning, recommender systems |
/\bIPA\b/ | 53 | Rarely Interoperable Private Attribution |
/\bPPA\b/ | 23 | “Preference Profiling Attack”, among others |
/IP Protection/ unqualified | 31 | Intellectual property |
/k-anonymity/ | 97 | Generic privacy term |
/Privacy Budget/ unqualified | 106 | Generic differential-privacy term |
/SPARROW|DOVEKEY|PARAKEET/ | 24 | Matches “sparrow” and “parakeet” in unrelated papers |
/bounce\s+tracking/ | 12 | The phenomenon, not the Chrome countermeasure. Covered by cookie_syncing |
/Aggregation Service/ bare | 2 | A 2022 outage paper and a 2012 analytics paper |
/\bFLoC\b/ before a year | 3 | “FLoC 2018” is the Federated Logic Conference |
/\bFLEDGE\b/i | 2 | “full-fledge crawling system” — the hyphen is a word boundary, so \b does not save you |
/First[- ]Party Sets/i | 1 | “the first party sets the cookies” is a verb phrase |
/attribution reporting/i bare, lower case | 1 | Apple's SKAdNetwork “delays and anonymises attribution reporting to advertisers” |
Two of these needed a structural fix rather than a narrower alternation:
- The CHIPS family is the only case-sensitive family in the folder. Lowercase
chipsis silicon, and a WPA3 paper that says “anti-clogging cookie” a few hundred characters from “chips” lands in the family if the acronym is matched case-insensitively — Dragonblood did exactly that. The report script therefore honours each family's own regex flags instead of forcinggi. This removed 1 false positive and the family went 6 → 5. - Protected Audience had to drop bare
interest groupentirely; the ad-tech phrasings (ad interest group,joinAdInterestGroup,runAdAuction,leaveAdInterestGroup,interest-group bidding/auction) are the whole family. This took the count from 234 to 11 — and then to 8, because\bFLEDGE\bwith aniflag matches “a full-fledge crawling system” and “a full-fledge experience”. The Protected Audience, Related Website Sets and Attribution Reporting families are now all case-sensitive, for the same reason CHIPS is. - Attribution Reporting is the subtlest of the three. Lowercase “attribution reporting” is ordinary ad-tech English, and a 2026 PETS paper used it of Apple's SKAdNetwork. But papers also write “Attribution reporting API” and “privacy sandbox attribution reporting api”, so a purely case-sensitive rule loses two of the three true positives. The accepted rule is: the capitalised proper name in any position, or the phrase followed by “API” in any case. Family count 4 → 3.
Residue that remains, stated rather than hidden. Two kinds:
- False positives. The folded families still admit passing mentions. Of the 67 papers that name at least one family, 11 were judged to actually measure a Privacy Sandbox mechanism — the page's table — plus one,
bahrami2025_cookieguard, that supplies CHIPS context without measuring the attribute. That is a precision of roughly 16% for “names a family” as a proxy for “measures the thing”, and it is why the page publishes the family counts as reach and the measured results as a hand-audited table, never as a percentage of the family counts. The per-family lists are printed in full in the report output below so that anyone can redo that audit and disagree with it. - A known false negative, accepted deliberately. Pinning
FLEDGEto upper case also drops2024/USENIX/web-platform-threats-automated-detection-of-web-security-issues-with-wpt, which listsfledgein lower case as one of the web-platform-tests directories it exercises. That is a genuine reference to the API. It is excluded because the only way to keep it is to re-admit “full-fledge”, and one true positive is not worth two false ones. It is recorded here rather than silently lost.
Evidence quotes checked
scripts/ps_quotecheck.mjs locates every evidence.quote behind the 14 papers in the measured-results table against data/fulltext/<year>/<venue>/<slug>/paper.cols.txt. Whitespace is collapsed, soft hyphens rejoined; three match levels are reported and a NEAR is never treated as a PASS.
| Level | Count | Meaning |
|---|---|---|
| PASS (all three variants) | 91 | Found verbatim, or after stripping punctuation and spaces, or — for a quote containing an … elision — with every fragment of 12+ characters present in the paper's own order |
| NEAR | 40 | 80%+ of its 6-character-and-longer tokens present, but not contiguous |
| FAIL | 0 | Not located |
| Total | 131 |
The 40 NEARs are a rendering artefact, not a quote problem, and the cause is documented: the .cols repair does not fully de-interleave every two-column PDF. The Fledging paper is the clearest case — the extraction quotes “We detected the use of FLEDGE APIs in 9.5% (n = 6,682) of the 70K sites that we visited during the measurement period”, and the .cols rendering reads “…FLEDGE APIs in cannot combine that interest with other information about 9.5% (n = 6,682) of the 70K sites that we visited during the the person…” — the sentence is there, with the facing column woven through it.
Every figure the page publishes was additionally located by hand with a targeted regex over the collapsed text, and the surrounding sentence read:
| Figure on the page | Sentence found in paper.cols.txt |
|---|---|
| 9.5% (n = 6,682) of 70K | “We detected the use of FLEDGE APIs in … 9.5% (n = 6,682) of the 70K sites that we visited during the … measurement period” |
| 11,941 of 11,959 auctions | “We observed a total of 11,959 auctions for ad spaces during our measurement period, almost all of which (n = 11,941) were run by Google Ad Manager” |
| 15,935 interest groups | “We observed a total of 15,935 interest groups” |
| 30.90% (n = 94,673) of 306,407 | “while we observed 306,407 calls to join an interest group, 30.90% (n = 94,673) attempted to create an interest group with an expiration time greater than 30 days” |
| 4,628 / 3,903 / 3,585 per snapshot | “While 4,628 sites used FLEDGE in June, this number decreased to 3,903 sites in July, and further dropped to 3,585 sites in September” |
| Tranco top 70K | “we perform a crawl of the top 70K domains from the Tranco list” |
| 10.1% (1,311) of 12,955 | “approximately 10.1% (1,311) of the 12,955 sites we were able to reach did invoke the Shared Storage API in some way” |
| 1,250 (9.6%) selectURL, four domains | “1,250 of these pages (9.6%) made at least one call to selectURL … All of these interactions can be traced back to four unique domains” |
| securepubads 1,222 / ep3 177 / crcldu 46 / optable 17 | “securepubads.g.doubleclick.net and ep3.adtrafficquality.google … using Shared Storage on 1,222 and 177 websites respectively. crcldu.com (46 websites) and ads.optable.co (17 websites)” |
| 289 domains, 245 (85%), 61% | “we were left with 289 domains … we were able to obtain 245 valid attestation files, meaning that 85% of the domains that we checked were hosting an attestation file … Of the attestation files we collected, 61% …” |
| 48.2% in Disconnect | “Of the domains that have a valid attestation file 48.2% appeared in the Disconnect list. Similarly, 56.4% of the domains with access to the Shared Storage API appeared in the Disconnect list” |
| 41 sets, 92.7% associated, 26 March 2024 | “As of the most recent RWS list in the dataset (26 March 2024), there were 41 sets; of these, 22% had one or more service sites; 14.6% had one or more ccTLD sites; and 92.7% had one or more associated sites” |
| 58.8% of 114 PRs | “58.8% of all pull requests closed without being merged”; “Across the 114 pull requests” |
| 3,177 tracking cookies, 40 (1.3%), 26 | “among 3,177 unique tracking cookies, only 40 (1.3%) are partitioned, of which 26 are accompanied by non-partitioned tracking cookies from the same t…” |
| 79,898 cookies, 521 partitioned | “out of 79,898 unique stored cookies, 521 are marked partitioned” |
| >50% after 3 weeks, 95% after 4 | “the number of users in our dataset that could be uniquely identified by FLoC IDs is more than 50% after 3 weeks and more than 95% after 4 weeks” |
| 10%+ of history for 30% of users | “reconstruct 10% or more of the browsing history for 30% of its users using Generative Adversarial Networks” |
| 15–17% pool of 1,000, <0.2% FP | “the probability of correct re-identification can reach 15 − 17%, considering a pool of 1,000 users”; “false positives being negligible (less than 0.2%)” |
| 35–42% non-2-anonymous at 100,000 | “even in the case of a population of 100, 000 personas, we still observe 35−42% of them being non-2-anonymous” |
| 0.4% / 57% / 75%, 250k users | “0.4% of the 250k users we simulate are re-identified … 57% of users with stable interests are uniquely re-identified when their browsing activity has been observed for 15 epochs, increasing to 75% after 30 epochs” |
| 25% / 49% / 94% noise removal | “about 25% of the noisy topics on single websites in one-shot scenarios”; extraction prevalence “49% after 15 epochs and 94% after 30 epochs” |
| >90% of domains, <10% screenshot diff | “We find that more than 90% of domains exhibit less than a 10% screenshot difference when cookies are disabled” |
| 7,490 domains crawled | “we arrive at a final list of 7,490 successfully crawled domains” |
| Chrome 117, September 2023 (FLEDGE GA) | “officially released to the public and integrated into the stable Chrome browser as of version 117.0.5938.89 in mid September 2023” |
One figure was reworded because the paper and the extraction disagree. bahrami2025_cookieguard`s extraction records cross-domain cookie overwriting as “31.5% of websites”; the paper's own sentence says “approximately 32% of websites”. Neither number is published on this page — the paper is cited only for CHIPS context — but the disagreement is recorded here because the next run may want that figure.
Browser measurements
Four probes, all against Chromium 151.0.7922.34 (Playwright 1.62.1) on 2026-09-01, all committed under sandbox/. Their real, unedited output follows.
sandbox/ps_probe.mjs— feature detection and Permissions-Policy, four launch configurations.sandbox/ps_call_probe.mjs— calls each API from an HTTPS origin, onlocalhostand on a host-resolver-mapped domain that is definitely not enrolled, with and without the ads-API override and the enrollment override.sandbox/ps_cdp_probe.mjs— turns on the CDPStoragetracking toggles and records what arrives.sandbox/ps_cdp_surface.mjs— enumerates theStoragedomain from the shipping build's own/json/protocoland fromChromeDevTools/devtools-protocolmaster.
- ps_probe-output.txt
probe run: 2026-09-01 Playwright default (headless): 151.0.7922.34 Playwright + PrivacySandboxAdsAPIsOverride: 151.0.7922.34 Puppeteer default (headless): Chrome/151.0.7922.34 Puppeteer + PrivacySandboxAdsAPIsOverride: Chrome/151.0.7922.34 expression Playwright default (headle Playwright + PrivacySandbo Puppeteer default (headles Puppeteer + PrivacySandbox --------------------------------- -------------------------- -------------------------- -------------------------- -------------------------- document.browsingTopics function function function function navigator.joinAdInterestGroup function function function function navigator.runAdAuction function function function function navigator.leaveAdInterestGroup function function function function window.sharedStorage object object object object sharedStorage.selectURL function function function function HTMLFencedFrameElement function function function function document.hasPrivateToken function function function function document.hasRedemptionRecord function function function function document.requestStorageAccess function function function function document.requestStorageAccessFor function function function function window.IdentityCredential (FedCM) function function function function navigator.userAgentData object object object object navigator.cookieDeprecationLabel undefined undefined undefined undefined cookieStore object object object object PP:browsing-topics true true true true PP:join-ad-interest-group true true true true PP:run-ad-auction true true true true PP:attribution-reporting true true true true PP:shared-storage true true true true PP:private-aggregation true true true true HTMLImageElement.attributionSrc yes yes yes yes HTMLAnchorElement.attributionSrc yes yes yes yes
- ps_call_probe-output.txt
=== localhost, Playwright default (151.0.7922.34) === document.browsingTopics() RETURNED [] navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() RETURNED undefined sharedStorage.worklet.addModule() OperationError: Rejecting load of https://localhost:8443/w.js due to unexpected MIME type. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 === probe.example (NOT enrolled), Playwright default (151.0.7922.34) === document.browsingTopics() RETURNED [] navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() RETURNED undefined sharedStorage.worklet.addModule() OperationError: Rejecting load of https://probe.example:8444/w.js due to unexpected MIME type. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 === probe.example + PrivacySandboxAdsAPIsOverride (151.0.7922.34) === document.browsingTopics() RETURNED [] navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() RETURNED undefined sharedStorage.worklet.addModule() OperationError: Rejecting load of https://probe.example:8444/w.js due to unexpected MIME type. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 === probe.example + enrollment override for that origin (151.0.7922.34) === document.browsingTopics() RETURNED [] navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() RETURNED undefined sharedStorage.worklet.addModule() OperationError: Rejecting load of https://probe.example:8444/w.js due to unexpected MIME type. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200
- ps_cdp_probe-output.txt
toggle OK Storage.setInterestGroupTracking toggle OK Storage.setInterestGroupAuctionTracking toggle OK Storage.setSharedStorageTracking toggle FAIL Storage.setAttributionReportingTracking: cdpSession.send: Protocol error (Storage.setAttributionReportingTracking): 'Storage.setAtt == API calls from the page == joinAdInterestGroup ok undefined runAdAuction ok null sharedStorage.set ok undefined sharedStorage.addModule ok undefined sharedStorage.run ok undefined == CDP events received (7) == Storage.interestGroupAuctionEventOccurred {"eventTime":1788282880.844885,"type":"started","uniqueAuctionId":"BAA2639A8868AB941264DB3DB6CB5BED","auctionConfig":{"auctionSignals":{"pending":false,"value":null},"decisionLogicURL":"https://probe.example:8444/decide. Storage.interestGroupAuctionNetworkRequestCreated {"type":"bidderJs","requestId":"06A218F8B3F436C7E8D3F44A2D2806C3","auctions":["BAA2639A8868AB941264DB3DB6CB5BED"]} Storage.interestGroupAuctionNetworkRequestCreated {"type":"sellerJs","requestId":"C09A1952CA56CC58940B85D959CCD3A3","auctions":["BAA2639A8868AB941264DB3DB6CB5BED"]} Storage.sharedStorageAccessed {"accessTime":1788282880.902479,"scope":"window","method":"set","mainFrameId":"B3037C0FA13BA8CE8A07D1020A778F25","ownerOrigin":"https://probe.example:8444","ownerSite":"https://probe.example","params":{"key":"k","value": Storage.sharedStorageAccessed {"accessTime":1788282880.904117,"scope":"window","method":"addModule","mainFrameId":"B3037C0FA13BA8CE8A07D1020A778F25","ownerOrigin":"https://probe.example:8444","ownerSite":"https://probe.example","params":{"scriptSourc Storage.sharedStorageAccessed {"accessTime":1788282880.912412,"scope":"window","method":"run","mainFrameId":"B3037C0FA13BA8CE8A07D1020A778F25","ownerOrigin":"https://probe.example:8444","ownerSite":"https://probe.example","params":{"operationName":"p Storage.sharedStorageWorkletOperationExecutionFinished {"finishedTime":1788282880.913208,"executionTime":1050,"method":"run","operationId":"0","workletTargetId":"EBB22A0151158B1D33AB96EBCA57DF2F","mainFrameId":"B3037C0FA13BA8CE8A07D1020A778F25","ownerOrigin":"https://probe.e
- ps_cdp_surface-output.txt
== Chromium 151.0.7922.34 — Storage domain == commands (12): getInterestGroupDetails, setInterestGroupTracking, setInterestGroupAuctionTracking, getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets events (5): interestGroupAccessed, interestGroupAuctionEventOccurred, interestGroupAuctionNetworkRequestCreated, sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished == devtools-protocol master — Storage domain == commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets events (0): (none)
What the call probe does and does not license. It shows that on this build, from a non-enrolled origin, every call returns a benign empty value rather than throwing. It does not establish that the APIs were functionally active — an empty Topics array is also the correct answer for a profile with no history. That ambiguity is the finding, and the page states it that way rather than as “the APIs work without enrollment”.
The non-Chrome probe failed and is not on the page as a measurement. sandbox/ps_nonchrome_probe.mjs exists but Playwright's Firefox build will not launch in this container (RenderCompositorSWGL failed mapping default framebuffer), and it timed out at 180 s. The page's Firefox and Safari statements therefore rest on Bugzilla and vendor documentation, and the page says so in its own methodology section.
External sources, verified and rejected
Everything below was fetched on 2026-09-01, not recalled. Two sonnet sub-agents did a first pass; the URLs, quotes, counts and milestone numbers were then re-fetched by the authoring agent, which is how the errors in the next section were caught.
| Source | Load-bearing fact | Verification | Decision |
|---|---|---|---|
privacysandbox.google.com/blog/update-on-plans-for-privacy-sandbox-technologies | The 2025-10-17 retirement of ten technologies | Fetched; the list and the CHIPS/FedCM/PST carve-out quoted verbatim; page footer reads “Last updated 2025-10-17 UTC” | Used, quoted |
privacysandbox.google.com/overview/status | Per-feature status: Continue to support / Deprecate and remove / Discontinue / Do not launch; CHIPS default in Chrome 114; Storage Access default in Chrome 119 | Fetched; page states “Last updated: August 14, 2026” | Used |
privacysandbox.google.com/private-advertising/enrollment | “We will no longer accept new account creation and enrollment” | Fetched, quoted verbatim | Used, quoted on the content page. A first draft only implied the consequence; a review pass pointed out the sentence was never actually written, so the quote and the --privacy-sandbox-enrollment-overrides workaround are now both on the page |
chromestatus.com/api/v0/features/<id> × 19 | Ship milestones (M114/M115/M116/M108) and removal milestones (M152/M153/M155), all with status Proposed | Fetched individually through the JSON API | Used for ship milestones. Superseded for removal status on 2026-09-27: the 19 ids were not recorded, the 22 used since are in scripts/external_checks_privacy_sandbox.mjs, Fenced Frames moved to M156, and there was never a Topics removal entry |
chromiumdash.appspot.com/fetch_releases and fetch_milestone_schedule | Chrome stable = M152 since 2026-08-25; M153 stable_date 2026-09-08; M114 2023-05-30; M115 2023-07-18; M116 2023-08-15 | Fetched, parsed | Used |
gov.uk/cma-cases/investigation-into-googles-privacy-sandbox-browser-changes | Case opened 2021-01-07, closed 2025-10-17 | Fetched; “Case state: Closed” read off the page | Used |
github.com/GoogleChrome/related-website-sets → related_website_sets.JSON | 70 sets, 320 member domains, 66 (94.3%) with an associated site | Fetched and counted in this run | Used |
GitHub API on privacysandbox/attestation and patcg-individual-drafts/topics | Both archived: true; last pushes 2026-01-22 and 2025-11-07 | Fetched 2026-09-01 after a review pass flagged that the page treated these as live feeds | Used. The archival of the topics repo is also the reason there is no taxonomy v3 |
ChromeDevTools/devtools-protocol commit history, bisected | The CDP Storage domain carried setAttributionReportingTracking, setAttributionReportingLocalTestingMode, sendPendingAttributionReports and four attributionReporting* events. Present in the descriptor at 2026-04-01, absent at 2026-04-08 | Fetched five dated snapshots (2026-01-01, 02-01, 03-01, 03-15, 04-01) and four in April, resolving each date to a commit SHA through the GitHub commits API and pulling json/browser_protocol.json at that SHA | Used. This changed the page's advice: Attribution Reporting is measurable with first-class CDP events on a pre-April-2026 build, which the first draft denied |
chromiumdash.appspot.com/fetch_milestone_schedule for M146–M151 | Branch points: M147 2026-03-09, M148 2026-04-06 | Fetched | Used as an inference, labelled as one. M147 should carry the ARA surface; M148 branched inside the removal window and is a coin flip. Not tested — this run had only Chromium 151 |
blink-dev, “Intent to Deprecate and Remove: Topics API” (thread _R85yctz4Rs) | “Deprecate in M144 and then remove in M150”; “The Topics API was deprecated in Chrome-144 with a plan to remove it in Chrome-150. Currently the usage is 4.9% of page loads”; the phased M150 field-trial / M152 stub plan, quoted verbatim on the page | Fetched 2026-09-01. Thread opened 2025-11-07; the phased-plan message is dated 2026-06-12; later messages 2026-06-25 and 2026-07-08 | Used. A first draft asserted “the removal dates have already slipped twice” on a sub-agent's word; the primary source shows something more precise — deprecation landed at M144 on schedule, and it is the removal that moved M150 → M152 → M153. The page was rewritten to say that. It also turned the speculative field-trial hedge into a sourced fact |
Chrome Platform Status motivation fields for Topics, Shared Storage, Protected Audience, RWS, Attribution Reporting | Chrome's own usage telemetry: Topics 13% then 4.9% of page loads; Shared Storage ~11% of page loads; Protected Audience joinAdInterestGroup() down ~100x and runAdAuction() down >10x with “virtually none” of auctions having winners; RWS 71 sets and requestStorageAccessFor ~0.95% of page loads; ARA no figure | Fetched 2026-09-01 through the JSON API | Used, with the caveat foregrounded. These are unaudited vendor telemetry with no stated methodology and a page load unit. They are on the page because they are the only post-April-2025 adoption numbers in existence, and the page says exactly that. The 71-vs-70 RWS discrepancy is published rather than reconciled |
MDN header reference pages for Attribution-Reporting-Eligible, Attribution-Reporting-Register-Source, Attribution-Reporting-Register-Trigger, Sec-Browsing-Topics, Observe-Browsing-Topics | All five exist; Sec-Browsing-Topics “fails silently” and is deleted for an unenrolled caller | All five fetched, HTTP 200; the silent-failure sentence quoted verbatim from the Sec-Browsing-Topics page | Used. Sec-Shared-Storage-Writable was checked and returns 404 on MDN, so it is not on the page |
GitHub API on GoogleChrome/related-website-sets and GoogleChrome/ip-protection | Both archived: true; last pushes 2025-11-21 and 2025-11-03 | Fetched | Used |
github.com/privacysandbox/attestation → enrollment_report.csv | 326 rows; Topics 249, Protected Audience 237, Attribution Reporting 228, Shared Storage 205, Private Aggregation 193; 25 rows with no Chrome API | Fetched and counted in this run | Used |
patcg-individual-drafts/topics → taxonomy_v2.md | 469 topics; no v3 exists | Fetched and counted | Used |
The local Chromium's PrivacySandboxAttestationsPreloaded/privacy-sandbox-attestations.dat | 263 sites, manifest version 2025.7.18.0 | Read off disk and counted | Used |
w3.org/TR/attribution/ | “Attribution Level 1”, W3C Working Draft | Fetched | Used |
| Bugzilla REST, bug 1979093 | “Remove checkbox in settings for feature that was never enabled”, RESOLVED FIXED, target milestone 143 Branch, last changed 2025-08-21 | Fetched through the REST API | Used, with the tension flagged on the page — the bug summary says the feature “was never enabled”, which does not sit easily with the 2024 reporting about Firefox 128. Not resolved; both stated |
Crossref, 10.1007/978-3-031-85960-1_8 | Zöllner, Feldmann, Dao, A First Look at Cookies Having Independent Partitioned State, PAM 2025, pp. 182–196 | Fetched | Cited as existing; no figure quoted, because Springer serves no abstract to a plain fetch and the paper was not read |
chromedevtools.github.io rendered docs | CDP Storage domain contents | Page is JS-rendered; a plain fetch returns no protocol data | Rejected. Used the shipping browser's own /json/protocol and the devtools-protocol master JSON instead |
privacysandbox.google.com/overview/api-availability and /overview/availability | Regional gating of the ads APIs | Both return HTTP 404 on 2026-09-01; no replacement page found | Not established. Published on the page as an explicit unknown |
Vendor and SEO material (segwise.ai, usercentrics.com, seers.ai, unisignin.com, chrome-flags.com, superchargebrowser.com, browsercalendar.com, fosspost.org, rottenwifi.com, cookie-script.com, windowsforum.com) | Privacy Sandbox shutdown dates, Chrome release dates, CHIPS explainers | Several carried dates that could not be reconciled with the chromestatus entries | Rejected, all of them. Every date on the page comes from chromestatus, chromiumdash or Google's own pages |
Trade press (martech.org, adexchanger.com) | The retirement and the CMA release | Directionally correct but secondary | Rejected as a citation; used only as a pointer to the primary sources, which were then fetched |
Sub-agent findings that did not survive verification
The two research sub-agents were told to fetch rather than recall and largely did. Three of their conclusions were still wrong, and all three would have put a false statement on the page. Recording them because they are the same class of error the next run will make.
| Sub-agent claim | What verification found |
|---|---|
| “Private Aggregation was already removed in Chrome 152 (stable 2026-08-25, the version currently shipping)” | Chrome Platform Status feature 4683382919397376 reports status Proposed, desktop milestone 152. Proposed is not shipped. Chromium 151 exposes the API; nothing was verified as removed. The page says “removal proposed, M152” |
“The live CDP spec contains zero interestGroup*, attributionReporting* or sharedStorage* events in the Storage domain” | True of devtools-protocol master — which tracks tip-of-tree, not the shipping build. Chromium 151's own /json/protocol has 12 such commands and 5 such events, and this run received live sharedStorageAccessed and interestGroupAuctionEventOccurred events from it. The page reports both columns and dates each |
“None of these APIs are on by default in any Chrome profile … because they require the PrivacySandboxAdsAPIsOverride base::Feature” | Measured false. All surfaces are present and all six Permissions-Policy features return true under Playwright's and Puppeteer's default flags in Chromium 151, and adding the override changed nothing. The page reports the measurement |
Two further sub-agent claims were accepted but softened: that chrome://topics-internals and friends are gone (circumstantial source-tree evidence only — the page does not mention them at all rather than assert either way), and the Safari PCM status (weakly sourced; the page does not claim PCM is removed).
What the corpus does not establish
- Nothing about Attribution Reporting in deployment. 3 of 5,869 papers name it, none instrument it.
- No independent adoption measurement after February 2025. The two crawl-based papers are from 2023 and February 2025; the retirement was announced in October 2025. Chrome's own removal notes claim usage collapsed and give numbers, now quoted on the page; this corpus cannot confirm or refute them, and the party that produced them is the party being measured.
- No CHIPS adoption study inside the seven venues.
rasaii2025_crumbsgives a 1.3% figure as a side measurement; the dedicated study is a PAM 2025 paper outside the corpus. - Regional gating of the ads APIs. Both Google availability pages 404. A DMA-driven gate is plausible but plausible is not measured. A first draft asserted that FLoC's 2021 origin trial excluded the EEA; that came from a sub-agent citing Wikipedia, no primary source for it could be found in this run, and it was removed from the page. What
berke2022_privacydoes say, and what the page now carries instead: the trial ran Chrome 89 to 91, spring to autumn 2021, for users with at least seven domains in their history, with k = 2000 giving 33,872 cohorts of which 2.3% were deemed sensitive. Published on the page as an explicit unknown with an instruction to test it rather than assume. - Whether
chrome://*-internalspages are still reachable in Chrome 152 stable. Not tested: this container has no Chrome stable, only Playwright's Chromium 151. - Whether the CHIPS default-on milestone is M114. Chrome Platform Status and Google's status page agree on M114 for CHIPS but disagree for the Storage Access API (M115 versus “Chrome 119 and higher”). The page carries both and tells the reader to test if it matters.
Judgement calls
- A new page, not a broadened cookies. Reasoning in Why a new page and not a broadened neighbour.
- Title “Measuring the Privacy Sandbox”, not “The Privacy Sandbox”. The page is about instrumentation and denominators, not about explaining the APIs; the specs do that better and the wiki's own rule is not to teach the platform.
- The retirement is the page's lead, not a footnote. A reader who takes only one sentence away should take “these are being removed and your browser version is the measurement”. The alternative — leading with the API descriptions — would have reproduced the 2022 roadmap, which is the failure mode the task spec warns about.
- Removal milestones are reported as proposed, not as fact. (2026-09-27: superseded. The page now reports what the binaries do; see Judgement calls, 2026-09-27.) They are the
desktopmilestone on a Chrome Platform Status entry whose status string isProposed. Two of them (M144, M150) had already slipped once before landing on M152/M153. The page dates them and does not promise them. - Published the “7.7% of recent web crawls” silence figure even though it is a keyword proxy, because the direction is unambiguous at that magnitude and the confound (a paper can measure the platform without naming an API) can only inflate the true figure slightly, not reverse it. The page names the proxy, and now also names the opposite confound a review pass raised: the denominator includes crawls with no reason to touch an ads API, so the figure is an upper bound on non-engagement rather than a defect rate.
- Did not publish the “Privacy Budget” or “k-anonymity” counts at all: both regexes are dominated by generic differential-privacy usage and no qualified version was found that was worth the space.
- Did not publish a figure from the PAM 2025 CHIPS paper. It is outside the corpus, Springer would not serve its abstract to this run, and nobody here read it.
- Kept the
.colsNEARs visible rather than tuning the matcher until they disappeared. Forty NEARs with a documented cause is a more honest artefact than zero NEARs from a looser matcher.
Bibliography
Six entries added to bibliography before the page was saved, duplicate-checked by key and by DOI/URL against a fresh ?do=export_raw of the live page (718 → 724 entries, zero duplicate keys):
berke2022_privacy, turati2023_locality, calderonio2024_fledging, alvim2024_privacy, mcquistin2024_first, nisenoff2025_exploiting.
Generated with scripts/bibgen.mjs. Two needed hand-completion because PETS and USENIX index records carry no authors: the PoPETs authors came from petsymposium.org/popets/2023/popets-2023-0101.php and the USENIX authors from the venue landing page, both fetched with a browser User-Agent. Reused without change: jha2023_topics, beugin2024_interest, ali2023_navigating, lin2024_browsing, rasaii2025_crumbs, bahrami2025_cookieguard, kancherla2025_least.
After each save, literature:bibliography and the page were purged (?purge=true) and the rendered DOM re-checked, counting only inside the <!-- wikipage start --> … <!-- wikipage stop --> markers so that the theme's own chrome is not counted.
Counts below are for privacy_sandbox only. This page's own heading and table counts are deliberately not asserted, because every edit to this paragraph changes them and a self-referential count can never be right for long; what is asserted about this page is that it renders 12 downloadable file blocks (7 until the 2026-09-27 refresh added 5) and zero red links.
| Count | privacy_sandbox |
|---|---|
| citation markers in the wiki source | 33 |
| distinct citekeys among them | 13 |
bibtex_citekey spans in the rendered DOM | 66 |
<dt> entries in the rendered bibtex_references list | 13 |
<table> in the rendered body | 13 (12 before the 2026-09-27 refresh) |
<h1>–<h4> in the rendered body | 24 (23 before) |
<WRAP> boxes | 6 (5 before) |
wikilink2 (red links) | 0 |
downloadable <file> blocks | 1 |
Read the first three rows together, because two reviewers independently got them wrong and then disagreed with each other. The bibtex plugin emits two bibtex_citekey spans per source marker — an anchor and a link — so 33 markers render as 66 spans, and neither number is the reference count: 33 markers over 13 distinct keys give 13 entries in the reference list. The rendered heading count is one higher than the source heading count because ~~DISCUSSION~~ contributes its own heading. And a source-side count is not a substitute for a rendered one on either page: the embedded script output here contains ==- and ##-prefixed lines that a naive source-side heading regex picks up as headings (44 against a real 22).
Two earlier versions of this paragraph were wrong — first 58/22, then 60/21/4, both counted with an unscoped grep and both before the last round of edits. A review pass could not reproduce either. It is recorded rather than quietly corrected because a provenance page whose own self-check does not reproduce is worse than one that has none, and because getting a trivial count wrong twice is the honest illustration of why the rest of the page is scripted.
Folding script
- ps_fold.mjs
// Privacy Sandbox API name folder for full-text probes. // // Why this exists: the Privacy Sandbox APIs have been renamed at least once // each (FLoC -> Topics, TURTLEDOVE -> FLEDGE -> Protected Audience, Trust // Tokens -> Private State Tokens, First-Party Sets -> Related Website Sets), // so a paper published in 2022 and a paper published in 2026 name the same // mechanism differently. A count of "Protected Audience papers" that does not // fold in "FLEDGE" is wrong by an order of magnitude in this corpus. // // Each family is an ordered list of ALTERNATIVE spellings. Every regex here is // deliberately narrow, because the wide version is what produces a confident // wrong number: // // * `\bCHIPS\b` alone matched 231 papers — silicon, not cookies. It is only // counted inside a 200-character window of "cookie" or "partition", or // when the acronym is spelled out. // * `\bIPA\b` alone matched papers about Instructions Per Access and the // International Phonetic Alphabet; `\bPPA\b` matched "Preference // Profiling Attack". Both bare acronyms are dropped; only the spelled-out // names count. // * `IP Protection` unqualified is intellectual property. It is only counted // next to Chrome / Privacy Sandbox / proxy. // * `the Topics` (without "API") matched ordinary English and is dropped. // * `Privacy Budget` is a generic differential-privacy term; it is only // counted next to fingerprint / Chrome / Sandbox, and even then the page // does not publish the figure (see the provenance page). // // Every regex is applied to whitespace-collapsed text with soft hyphens // rejoined, because a PDF line break inside "Protected Audi-\nence" otherwise // hides the match. export const FAMILIES = [ { key: 'topics', label: 'Topics API', also: 'FLoC (predecessor, withdrawn 2022)', // `FLoC 2018` is the Federated Logic Conference and appears in the // references of formal-methods papers, so the acronym is only counted when // it is not immediately followed by a year and not next to "Logic". re: /Topics\s+API|browsingTopics|Federated\s+Learning\s+of\s+Cohorts|\bFLoC\b(?!\s*(?:'?\d{2,4}|Logic))(?<!Logic\s+Conference,?\s)/i, }, { key: 'protected-audience', label: 'Protected Audience', also: 'FLEDGE, TURTLEDOVE', // NOT case-insensitive. `/\bFLEDGE\b/i` matches "full-fledge crawling // system" and "a full-fledge experience" — the hyphen is a word boundary, // so \b does not help. Two papers (de-Kodi WWW 2020, Android SmartTVs // USENIX 2021) were in this family until the acronyms were pinned to // upper case. Found by a review pass on 2026-09-01, not by the author. // "interest group" bare also matched 234 papers (political science, // federated learning, recommender systems); only ad-tech phrasing counts. re: /[Pp]rotected [Aa]udience|FLEDGE|TURTLEDOVE|joinAdInterestGroup|runAdAuction|leaveAdInterestGroup|ad interest group|interest[- ]group (?:bidding|auction)/, }, { key: 'attribution-reporting', label: 'Attribution Reporting API', also: 'Conversion Measurement API', // The bare lowercase noun phrase is generic ad-tech English: a 2026 PETS // paper used it of *Apple's* SKAdNetwork ("delays and anonymises // attribution reporting to advertisers"). Two forms are accepted and // nothing else: the capitalised proper name, in any position, and the // phrase followed by "API" in any case (papers write "Attribution // reporting API" and "privacy sandbox attribution reporting api"). re: /Attribution Reporting|[Aa]ttribution[- ][Rr]eporting [Aa][Pp][Ii]|Conversion Measurement API|attributionsrc|attributionSrc|aggregatable attribution/, }, { key: 'private-aggregation', label: 'Private Aggregation / Aggregation Service', also: '', // Bare "Aggregation Service" matched a 2022 outage paper and a 2012 // web-analytics paper; it is only counted next to the Privacy Sandbox. re: /Private\s+Aggregation\s+API|aggregatable\s+report|Aggregation\s+Service\b(?=[\s\S]{0,200}?(?:Privacy\s+Sandbox|aggregatable|Shared\s+Storage|Attribution\s+Reporting))|(?:Privacy\s+Sandbox|aggregatable|Shared\s+Storage|Attribution\s+Reporting)[\s\S]{0,200}?Aggregation\s+Service\b/i, }, { key: 'shared-storage', label: 'Shared Storage API', also: '', re: /Shared\s+Storage\s+API|sharedStorage|selectURL/i, }, { key: 'fenced-frames', label: 'Fenced Frames', also: '', re: /Fenced\s+Frames?|fencedframe/i, }, { key: 'chips', label: 'CHIPS (partitioned cookies)', also: 'the Partitioned cookie attribute', // NOT case-insensitive, deliberately: lowercase "chips" is silicon, and a // WPA3 paper that says "anti-clogging cookie" a few lines from "chips" // lands in the family if the acronym is folded to lower case. The // spelled-out name and the attribute are matched case-insensitively via // explicit character classes. // Qualifiers measured: the bare acronym is never counted alone, and // "partition" is NOT enough of a qualifier — DRAM bank partitioning put // three Rowhammer/GPU papers in the family. re: /[Cc]ookies [Hh]aving [Ii]ndependent [Pp]artitioned [Ss]tate|CHIPS(?=[\s\S]{0,200}?[Cc]ookie)|[Cc]ookie[\s\S]{0,200}?CHIPS|[Pp]artitioned attribute|[Pp]artitioned cookies?/, }, { key: 'storage-access', label: 'Storage Access API', also: '', re: /Storage\s+Access\s+API|requestStorageAccess/i, }, { key: 'rws', label: 'Related Website Sets', also: 'First-Party Sets', // NOT case-insensitive. Lowercase "the first party sets the cookies" is a // verb phrase, and it put a 2021 WWW cookie-swapping paper in this family. re: /Related Website Sets?|First[- ]Party Sets/, }, { key: 'private-state-tokens', label: 'Private State Tokens', also: 'Trust Tokens', re: /Private\s+State\s+Tokens?|Trust\s+Tokens?/i, }, { key: 'fedcm', label: 'FedCM', also: 'Federated Credential Management', re: /\bFedCM\b|Federated\s+Credential\s+Management/i, }, { key: 'uach', label: 'User-Agent Client Hints / UA reduction', also: '', re: /User[- ]Agent\s+Client\s+Hints|Sec-CH-UA|User[- ]Agent\s+[Rr]eduction/i, }, { key: 'ip-protection', label: 'IP Protection', also: 'Gnatcatcher', re: /Gnatcatcher|Willful\s+IP\s+Blindness|IP\s+Protection[\s\S]{0,120}?(?:Chrome|Privacy\s+Sandbox|proxy)|(?:Chrome|Privacy\s+Sandbox)[\s\S]{0,120}?IP\s+Protection/i, }, { key: 'bounce-mitigations', label: 'Bounce Tracking Mitigations (the Chrome feature)', also: '', // Deliberately NOT `bounce tracking`: that is the phenomenon, measured by // 12 papers and covered by privacy:cookie_syncing. This family counts the // Chrome countermeasure by name. re: /Bounce\s+Tracking\s+Mitigation/i, }, { key: 'ps-android', label: 'Privacy Sandbox on Android', also: 'SDK Runtime', re: /Privacy\s+Sandbox\s+on\s+Android|SDK\s+Runtime/i, }, ]; // The umbrella term itself. Unambiguous: nothing else in a security corpus is // called a "Privacy Sandbox". export const UMBRELLA = /Privacy\s+Sandbox/i; // Rejected patterns, kept here so the next run does not re-add them. Each was // measured against the corpus and produced the paper count in the comment. export const REJECTED = [ ['/\\bCHIPS\\b/ unqualified', 231, 'silicon; Rowhammer and GPU papers'], ['/\\bCHIPS\\b/ within 200 chars of "partition"', 8, 'DRAM bank partitioning — 3 of the 8 were silicon'], ['/\\bthe Topics\\b/', 216, 'ordinary English'], ['/\\bIPA\\b/', 53, 'shared with Interoperable Private Attribution only rarely'], ['/\\bPPA\\b/', 23, '"Preference Profiling Attack", among others'], ['/IP Protection/ unqualified', 31, 'intellectual property'], ['/k-anonymity/', 97, 'generic privacy term, not Privacy Sandbox specific'], ['/Privacy Budget/ unqualified', 106, 'generic differential-privacy term'], ['/SPARROW|DOVEKEY|PARAKEET/', 24, 'matches "sparrow"/"parakeet" in unrelated papers'], ['/interest\\s+group/ (bare)', 234, 'political science, federated learning, recommender systems'], ['/bounce\\s+tracking/', 12, 'the phenomenon, not the Chrome countermeasure — see privacy:cookie_syncing'], ['/\\bFLoC\\b/ before a year', 3, '"FLoC 2018" is the Federated Logic Conference'], ['/Aggregation Service/ (bare)', 2, 'a 2022 outage paper and a 2012 analytics paper, neither about the Sandbox'], ['/\\bFLEDGE\\b/i', 2, '"full-fledge crawling system" — the hyphen is a word boundary'], ['/First[- ]Party Sets/i', 1, '"the first party sets the cookies" is a verb phrase'], ['/attribution reporting/i (bare, lowercase)', 1, "Apple's SKAdNetwork \"anonymises attribution reporting\""], ]; export function collapse(raw) { return raw.replace(/-\n/g, '').replace(/\s+/g, ' '); }
Report script output
Produced by node scripts/report_privacy_sandbox.mjs. Unedited.
- report_privacy_sandbox-output.txt
# privacy:privacy_sandbox — every published figure # generated by scripts/report_privacy_sandbox.mjs DENOMINATOR (full-text queries): 5869 papers with a paper.cols.txt across 7 venues (CCS, IMC, NDSS, PETS, USENIX Sec, TheWebConf, IEEE S&P), 2010-2026. 2025-2026 are provisional: CCS/IMC 2026 not held, IEEE S&P/WWW 2026 under-selected. ## 1. Reach: how much of the corpus is about this at all papers naming "Privacy Sandbox" at all : 43 (0.7% of 5869) papers naming >=1 API family (folded) : 67 (1.1%) papers naming "Privacy Sandbox" >=5 times : 9 (0.2%) This is the page's central limitation and it is printed first on purpose: the Privacy Sandbox literature inside these seven venues is a few dozen papers, and the double-digit set that measures anything is smaller still. ### Papers naming "Privacy Sandbox" 5+ times, most-naming first 48x 2025 CCS exploiting-the-shared-storage-api 18x 2024 USENIX fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv 14x 2023 NDSS navigating-murky-waters-automated-browser-feature-testing-for-uncovering-tracking-vectors 12x 2026 PETS the-pet-paradox-how-amazon-instrumentalises-pets-in-sidewalk-to-entrench-its-inf 11x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 6x 2026 NDSS snpeek-side-channel-analysis-for-privacy-applications-on-confidential-vms 5x 2024 PETS overprofiling-analysis-on-major-internet-players 5x 2025 CCS fingerprinting-sdks-for-mobile-apps-and-where-to-find-them-understanding-the-mar 5x 2025 PETS user-centric-textual-descriptions-of-privacy-enhancing-technologies-for-ad-track ## 2. Papers per API family (denominator 5869 papers with full text) Folded through scripts/ps_fold.mjs: former names counted with current ones. API family Former names Papers Share ------------------------------------------------ ---------------------------------- ------ ----- Topics API FLoC (predecessor, withdrawn 2022) 22 0.4% User-Agent Client Hints / UA reduction - 10 0.2% Protected Audience FLEDGE, TURTLEDOVE 8 0.1% Privacy Sandbox on Android SDK Runtime 7 0.1% CHIPS (partitioned cookies) the Partitioned cookie attribute 5 0.1% Private State Tokens Trust Tokens 5 0.1% Fenced Frames - 4 0.1% Related Website Sets First-Party Sets 4 0.1% Attribution Reporting API Conversion Measurement API 3 0.1% Shared Storage API - 3 0.1% Private Aggregation / Aggregation Service - 2 0.0% Storage Access API - 2 0.0% FedCM Federated Credential Management 1 0.0% IP Protection Gnatcatcher 0 0.0% Bounce Tracking Mitigations (the Chrome feature) - 0 0.0% ### Per-family paper lists (all of them, so nothing is hidden by a top-N cut) -- Topics API (22 papers) -- 162x 2022 CCS privacy-limitations-of-interest-based-advertising-on-the-web-a-post-mortem-empir 109x 2024 CCS the-privacy-utility-trade-off-in-the-topics-api 94x 2023 PETS locality-sensitive-hashing-does-not-guarantee-privacy-attacks-on-googles-floc-an 93x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 72x 2023 PETS on-the-robustness-of-topics-api-to-a-re-identification-attack 33x 2025 CCS exploiting-the-shared-storage-api 22x 2025 PETS user-centric-textual-descriptions-of-privacy-enhancing-technologies-for-ad-track 10x 2024 PETS overprofiling-analysis-on-major-internet-players 8x 2026 PETS gaze3p-gaze-based-prediction-of-user-perceived-privacy 4x 2022 IMC what-factors-affect-targeting-and-bids-in-online-advertising-a-field-measurement 4x 2024 USENIX fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv 3x 2022 IMC measuring-uid-smuggling-in-the-wild 2x 2025 IMC a-permissions-odyssey-a-systematic-study-of-browser-permissions-on-modern-websit 2x 2025 NDSS cascading-spy-sheets-exploiting-the-complexity-of-modern-css-for-email-and-browser-fingerprinting 2x 2025 PETS understanding-regional-filter-lists-efficacy-and-impact 2x 2026 NDSS cross-boundary-mobile-tracking-exploring-java-to-javascript-information-diffusion-in-webviews 1x 2023 PETS usability-and-enforceability-of-global-privacy-control 1x 2024 PETS the-devil-is-in-the-details-detection-measurement-and-lawfulness-of-server-side 1x 2024 PETS website-data-transparency-in-the-browser 1x 2025 PETS unmasking-the-shadows-a-cross-country-study-of-online-tracking-in-illegal-movie 1x 2025 USENIX websites-global-privacy-control-compliance-at-scale-and-over-time 1x 2026 NDSS are-your-sites-truly-isolated-automatically-detecting-logic-bugs-in-site-isolation-implementations -- Protected Audience (8 papers) -- 150x 2024 USENIX fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv 26x 2023 NDSS navigating-murky-waters-automated-browser-feature-testing-for-uncovering-tracking-vectors 11x 2025 CCS exploiting-the-shared-storage-api 7x 2024 CCS the-privacy-utility-trade-off-in-the-topics-api 5x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 2x 2022 IMC what-factors-affect-targeting-and-bids-in-online-advertising-a-field-measurement 1x 2025 NDSS you-can-rand-but-you-cant-hide-a-holistic-security-analysis-of-google-fuchsias-and-gvisors-network-stack 1x 2026 PETS the-pet-paradox-how-amazon-instrumentalises-pets-in-sidewalk-to-entrench-its-inf -- Attribution Reporting API (3 papers) -- 2x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 2x 2025 IMC a-permissions-odyssey-a-systematic-study-of-browser-permissions-on-modern-websit 1x 2026 NDSS snpeek-side-channel-analysis-for-privacy-applications-on-confidential-vms -- Private Aggregation / Aggregation Service (2 papers) -- 6x 2024 USENIX fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv 4x 2025 CCS exploiting-the-shared-storage-api -- Shared Storage API (3 papers) -- 162x 2025 CCS exploiting-the-shared-storage-api 1x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 1x 2026 NDSS are-your-sites-truly-isolated-automatically-detecting-logic-bugs-in-site-isolation-implementations -- Fenced Frames (4 papers) -- 21x 2025 CCS exploiting-the-shared-storage-api 12x 2024 USENIX fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv 3x 2023 NDSS navigating-murky-waters-automated-browser-feature-testing-for-uncovering-tracking-vectors 1x 2026 NDSS are-your-sites-truly-isolated-automatically-detecting-logic-bugs-in-site-isolation-implementations -- CHIPS (partitioned cookies) (5 papers) -- 31x 2025 PETS intractable-cookie-crumbs-unveiling-the-nexus-of-stateful-banner-interaction-and 11x 2023 IMC understanding-the-privacy-risks-of-popular-search-engine-advertising-systems 4x 2025 IMC cookieguard-characterizing-and-isolating-the-first-party-cookie-jar 2x 2025 WWW least-privilege-access-for-persistent-storage-mechanisms-in-web-browsers 1x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi -- Storage Access API (2 papers) -- 2x 2024 IMC a-first-look-at-related-website-sets 2x 2025 PETS tracking-without-borders-studying-the-role-of-webviews-in-bridging-mobile-and-we -- Related Website Sets (4 papers) -- 63x 2024 IMC a-first-look-at-related-website-sets 2x 2022 PETS who-knows-i-like-jelly-beans-an-investigation-into-search-privacy 2x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 1x 2021 NDSS whos-hosting-the-block-party-studying-third-party-blockage-of-csp-and-sri -- Private State Tokens (5 papers) -- 23x 2023 NDSS navigating-murky-waters-automated-browser-feature-testing-for-uncovering-tracking-vectors 1x 2024 IMC browser-polygraph-efficient-deployment-of-coarse-grained-browser-fingerprints-fo 1x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 1x 2024 USENIX fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv 1x 2025 NDSS you-can-rand-but-you-cant-hide-a-holistic-security-analysis-of-google-fuchsias-and-gvisors-network-stack -- FedCM (1 papers) -- 3x 2025 IEEE-SP only-as-strong-as-the-weakest-link-on-the-security-of-brokered-single-sign-on-on -- User-Agent Client Hints / UA reduction (10 papers) -- 12x 2024 IEEE-SP tabbed-out-subverting-the-android-custom-tab-security-model 6x 2026 PETS an-improved-entropy-measure-for-web-browser-fingerprinting-risk 3x 2025 PETS how-unique-is-whose-web-browser-the-role-of-demographics-in-browser-fingerprinti 2x 2023 PETS your-drm-can-watch-you-too-exploring-the-privacy-implications-of-browsers-mis-im 2x 2025 CCS exploiting-the-shared-storage-api 2x 2025 IMC a-permissions-odyssey-a-systematic-study-of-browser-permissions-on-modern-websit 1x 2022 USENIX phish-in-sheeps-clothing-exploring-the-authentication-pitfalls-of-browser-finger 1x 2023 USENIX account-security-interfaces-important-unintuitive-and-untrustworthy 1x 2024 USENIX arcanum-detecting-and-evaluating-the-privacy-risks-of-browser-extensions-on-web 1x 2025 CCS hidden-in-plain-bytes-investigating-interpersonal-account-compromise-with-data-e -- IP Protection (0 papers) -- -- Bounce Tracking Mitigations (the Chrome feature) (0 papers) -- -- Privacy Sandbox on Android (7 papers) -- 5x 2024 USENIX exploring-covert-third-party-identifiers-through-external-storage-in-the-android 4x 2023 USENIX union-under-duress-understanding-hazards-of-duplicate-resource-mismediation-in-a 3x 2024 PETS interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi 2x 2025 PETS your-signal-their-data-an-empirical-privacy-analysis-of-wireless-scanning-sdks-i 1x 2023 PETS usability-and-enforceability-of-global-privacy-control 1x 2024 CCS the-privacy-utility-trade-off-in-the-topics-api 1x 2025 CCS fingerprinting-sdks-for-mobile-apps-and-where-to-find-them-understanding-the-mar ## 3. By year: when the corpus started noticing Year papers PS-named share any-API share ----- ------ -------- ----- ------- ----- 2010 118 0 0.0% 0 0.0% 2011 116 0 0.0% 0 0.0% 2012 151 0 0.0% 0 0.0% 2013 125 0 0.0% 0 0.0% 2014 165 0 0.0% 0 0.0% 2015 190 0 0.0% 0 0.0% 2016 182 0 0.0% 0 0.0% 2017 232 0 0.0% 0 0.0% 2018 254 0 0.0% 0 0.0% 2019 402 0 0.0% 0 0.0% 2020 402 1 0.2% 1 0.2% 2021 380 1 0.3% 2 0.5% 2022 546 4 0.7% 7 1.3% 2023 720 7 1.0% 12 1.7% 2024 701 13 1.9% 17 2.4% 2025* 770 13 1.7% 21 2.7% 2026* 415 4 1.0% 7 1.7% * 2025-2026 provisional (CCS/IMC 2026 not held; IEEE S&P/WWW 2026 under-selected). ## 4. Regexes measured and REJECTED, with the paper count each produced Kept so the next run does not re-add them. Rejected pattern Papers it matched Why rejected ------------------------------------------- ----------------- -------------------------------------------------------------------------- /\bCHIPS\b/ unqualified 231 silicon; Rowhammer and GPU papers /\bCHIPS\b/ within 200 chars of "partition" 8 DRAM bank partitioning — 3 of the 8 were silicon /\bthe Topics\b/ 216 ordinary English /\bIPA\b/ 53 shared with Interoperable Private Attribution only rarely /\bPPA\b/ 23 "Preference Profiling Attack", among others /IP Protection/ unqualified 31 intellectual property /k-anonymity/ 97 generic privacy term, not Privacy Sandbox specific /Privacy Budget/ unqualified 106 generic differential-privacy term /SPARROW|DOVEKEY|PARAKEET/ 24 matches "sparrow"/"parakeet" in unrelated papers /interest\s+group/ (bare) 234 political science, federated learning, recommender systems /bounce\s+tracking/ 12 the phenomenon, not the Chrome countermeasure — see privacy:cookie_syncing /\bFLoC\b/ before a year 3 "FLoC 2018" is the Federated Logic Conference /Aggregation Service/ (bare) 2 a 2022 outage paper and a 2012 analytics paper, neither about the Sandbox /\bFLEDGE\b/i 2 "full-fledge crawling system" — the hyphen is a word boundary /First[- ]Party Sets/i 1 "the first party sets the cookies" is a verb phrase /attribution reporting/i (bare, lowercase) 1 Apple's SKAdNetwork "anonymises attribution reporting" ## 5. The measuring papers: population, instrument, and measured result Denominator per row is the paper's OWN population, printed in the table. Every prevalence string below is verbatim from detection[].prevalence. ### CCS 2022 — Privacy Limitations of Interest-based Advertising on The Web: A Post-mortem Empirical Analysis of Google's FLoC. mechanism: FLoC population: comScore Web Behavior Database n=93808 other (pre-existing-dataset) | comScore Web Behavior Database n=2073405 other (pre-existing-dataset) | 2017 U.S. Census Bureau Current Population Survey n=? other (pre-existing-dataset) | 2017 U.S. Census Bureau American Community Survey n=? other (pre-existing-dataset) temporal: existing-dataset 2017-01..2017-12 crawlConfig: null (no crawl-config object extracted) * unique FLoC cohort ID sequences technique : Counted unique sequences across four weekly cohort recomputations. metric : fraction of samples uniquely identifiable PREVALENCE: More than 50% after 3 weeks and 95% after 4 weeks. * fingerprinting-enhanced cohort unicity technique : Combined cohort ID sequences with U.S. state as a weak fingerprint proxy. metric : fraction of uniquely identifiable samples PREVALENCE: (null) * demographic browsing differences technique : Compared top-domain visit-frequency distributions using chi-square tests. metric : chi-square test significance PREVALENCE: p < 0.0001 for every tested demographic subpopulation and domain set. * demographic t-closeness violations technique : Compared cohort demographic frequencies with population frequencies across representative panels. metric : fraction of cohorts violating t-closeness PREVALENCE: No greater than random chance for race or income. ### PETS 2023 — Locality-Sensitive Hashing Does Not Guarantee Privacy! Attacks on Google's FLoC and the MinHash Hierarchy System mechanism: FLoC population: MovieLens n=125000 documents (pre-existing-dataset) | Porto Taxi dataset n=30000 other (top-n) temporal: existing-dataset ?..? | existing-dataset ?..? crawlConfig: null (no crawl-config object extracted) * FLoC browsing-history reconstruction technique : GAN-generated histories filtered by integer programming to match SimHash metric : fraction of target history reconstructed PREVALENCE: more than 10% of browsing history for at least 30% of users * MinHash trajectory narrowing technique : Partitioned checkpoints by comparing vehicle and checkpoint MinHash signatures metric : ratio of possibly visited checkpoints PREVALENCE: around 10% ± 5% of checkpoints on average * SimHash pre-image generation technique : Linear integer program maximizing a matching subset of domains metric : success rate and runtime PREVALENCE: 100% success at length 5, declining to 11% at length 25 * MinHash checkpoint identification technique : Classified checkpoints as certainly absent, likely present, or unknown metric : possible-checkpoint set size PREVALENCE: 805.5 ± 439.7 checkpoints out of 7744 ### PETS 2023 — On the Robustness of Topics API to a Re-Identification Attack mechanism: Topics population: EasyPIMS n=268 human-participants (convenience) | EasyPIMS browsing-history dataset n=50976 websites (pre-existing-dataset) temporal: passive-collection 2022..2022 crawlConfig: null (no crawl-config object extracted) * cross-site re-identification technique : Match denoised reconstructed profiles across two simulated website audiences metric : probability of correct re-identification PREVALENCE: 15–17% for a pool of 1,000 users * profile uniqueness technique : Compute k-anonymity of denoised reconstructed profiles metric : fraction of users that are non-2-anonymous PREVALENCE: 35–42% with a population of 100,000 personas * false re-identification technique : Exact profile matching against a wrong user across simulated websites metric : false-positive probability PREVALENCE: less than 0.2% ### CCS 2024 — The Privacy-Utility Trade-off in the Topics API. mechanism: Topics population: AOL search logs dataset n=436005 human-participants (pre-existing-dataset) | AOL search logs dataset with Citizen Lab classification n=211313 human-participants (pre-existing-dataset) | AOL search logs dataset with Google Topics classification n=198023 human-participants (pre-existing-dataset) temporal: existing-dataset 2006-03-01..2006-05-31 crawlConfig: null (no crawl-config object extracted) * Third-party-cookie re-identification technique : Quantitative information-flow channel modeling of cookies and browsing histories. metric : average-case Bayes vulnerability PREVALENCE: Analysts using third-party cookies always succeeded on average. * Topics API user re-identification technique : Computed Bayes leakage and average- and max-case channel capacities. metric : multiplicative Bayes leakage PREVALENCE: Complete-pipeline leakage was 0.0028% and 0.0162% of cookie leakage for Citizen Lab and Google v1. * Topic popularity inference technique : Kronecker-product channel and counting-query analysis over reported topics. metric : probability of correctly counting topic occurrences PREVALENCE: With 10 individuals, correct counting probability was 0.56% for both taxonomy sizes. * Distinguishing genuine from random topics technique : IBA gain function evaluated on bounded-noise and differential-privacy channels. metric : posterior IBA expected gain PREVALENCE: Posterior expected gain was 95.8% for Citizen Lab and 96.2% for Google v1. ### PETS 2024 — Interest-disclosing Mechanisms for Advertising are Privacy-Exposing (not Preserving) mechanism: Topics population: CrUX top-list n=1000000 websites (top-n) | Tranco n=1000000 websites (top-n) | Google static mapping n=9254 domains (pre-existing-dataset) | Mozilla-reported browsing-history distribution n=52000 human-participants (pre-existing-dataset) | synthetic dataset generated from published browsing distributions n=250000 other (random) | CrUX top-list n=10000 websites (top-n) temporal: existing-dataset 2022-12..2023-06 crawlConfig: null (no crawl-config object extracted) * Topics noise removal technique : Repeated-topic checks plus a ten-domain occurrence threshold metric : accuracy, precision, TPR, FPR, and proportion of noisy topics removed PREVALENCE: about 25% of noisy topics in one-shot scenarios; 49% after 15 epochs and 94% after 30 epochs * Cross-site re-identification technique : Match users across two advertisers by shared genuine top-five topics metric : proportion of users uniquely or probabilistically re-identified PREVALENCE: 0.4% uniquely re-identified in one-shot; 57% after 15 epochs and 75% after 30 epochs * Topics classification utility technique : Compare Topics predictions with Google mapping, manual judgments, and Cloudflare categories metric : accuracy, balanced accuracy, Jaccard, Dice, overlap, and at-least-one-correct ratio PREVALENCE: at least one topic matched in 57% of categorized top-million domains * Subdomain topic manipulation technique : Prepend topic-associated WordNet words to top-site domains metric : number of successful targeted additions and untargeted eliminations PREVALENCE: more than 114 targeted additions for half of the initial 10k domains ### USENIX 2024 — Fledging Will Continue Until Privacy Improves: Empirical Analysis of Google's Privacy-Preserving Targeted Advertising mechanism: Protected Audience population: Tranco n=70000 domains (top-n) | custom seed list n=500 domains (purposive) temporal: live-crawl 2023-06..2023-09 crawlConfig: browsers=["Chrome"] headless=not-stated stateful=stateless consent=no-interaction depth=landing-plus-subpages * FLEDGE API adoption technique : Instrumented API calls during automated domain crawling. metric : share of visited sites PREVALENCE: 9.5% (n = 6,682) of 70,000 sites * Google auction dominance technique : Logged and attributed observed FLEDGE auctions to sellers. metric : share of observed auctions PREVALENCE: Google Ad Manager ran 11,941 of 11,959 auctions (99.8%) * Interest-group creation technique : Captured browser API calls and inspected Chrome Interest Group storage. metric : number of observed interest groups PREVALENCE: 15,935 interest groups * Interest-group persistence attempts technique : Inspected expiration values in joinInterestGroup calls. metric : share of join calls exceeding the limit PREVALENCE: 30.90% (n = 94,673) attempted expiration times greater than 30 days * FLEDGE privacy attacks technique : Controlled web pages, code review, documentation study, and custom exploits. metric : number of attacks PREVALENCE: 12 novel attacks across tracking, cross-site leakage, service disruption, and pollution * Owner-sniffing attack performance technique : Repeated Puppeteer experiments over lists of candidate owners. metric : completion time and accuracy PREVALENCE: Approximately 4.2 seconds for 500 owners; fully accurate in all runs ### CCS 2025 — Exploiting the Shared Storage API. mechanism: Shared Storage population: Google's Privacy Sandbox Enrollment Report and privacy-sandbox-attestations.dat n=289 domains (exhaustive) | Tranco n=1000000 domains (top-n) | Tranco n=15000 domains (stratified) | Disconnect Tracker Protection list n=? domains (pre-existing-dataset) temporal: active-probing late December 2024..late December 2024 | live-crawl early February 2025..early February 2025 | existing-dataset December 25, 2024..December 25, 2024 crawlConfig: browsers=["Chrome"] headless=not-stated stateful=stateless consent=no-interaction depth=single-target-page * network timing covert channel technique : Delayed worklet execution and server-side request timing metric : bits leaked per page PREVALENCE: 33-bit identifiers feasible with delays of 0.36–1.82 seconds * worklet crash covert channel technique : Conditional worklet crashes and observing missing requests metric : identifier recovery delay PREVALENCE: Random identifiers consistently recovered with approximately 0.5-second reload delays * queue timing covert channel technique : Flooded Shared Storage queues and measured returned-call timing metric : 5,000 API calls PREVALENCE: Two-second flooding intervals produced observable return-time differences * iframe caching covert channel technique : Compared cached and uncached iframe resource requests metric : requests per 33-bit identifier PREVALENCE: 66 requests needed to read a 33-bit identifier * data leakage over time technique : Repeated selectURL reads after daily budget resets metric : bits leaked per day PREVALENCE: Up to 12 bits per day * attestation-file availability technique : HTTP retrieval followed by manual URL inspection metric : share of checked domains hosting valid files PREVALENCE: 245 of 289 domains (85%) hosted valid attestation files * Shared Storage API usage technique : Puppeteer crawl with Chrome developer-tools capture metric : share of reachable websites PREVALENCE: 1,311 of 12,955 reachable sites (10.1%) invoked the API; 1,250 used selectURL * Shared Storage access among attested domains technique : Parsed collected attestation files metric : share of valid attestation files PREVALENCE: 149 of 245 files (61%) reported Shared Storage access ### IMC 2024 — A First Look at Related Website Sets. mechanism: Related Website Sets population: Related Website Sets list n=146 websites (exhaustive) | Tranco n=200 websites (random) | Related Website Sets GitHub pull requests n=114 other (exhaustive) temporal: existing-dataset ?..2024-03-30 | live-crawl 2024-05..2024-05 crawlConfig: null (no crawl-config object extracted) * Perceived website relatedness technique : Timed participant judgments for 20 website pairs. metric : classification accuracy and response counts PREVALENCE: 36.8% of 114 same-set responses incorrectly identified websites as unrelated * RWS set composition technique : Counted sites and subsets in the 26 March 2024 list. metric : share of sets containing associated sites PREVALENCE: 92.7% of 41 sets had one or more associated sites * RWS pull-request outcomes technique : Analyzed GitHub pull-request final states. metric : share closed without merging PREVALENCE: 58.8% of 114 pull requests were closed without being merged * Second-level-domain similarity technique : Computed Levenshtein distance between member and primary SLDs. metric : median edit distance and exact-match share PREVALENCE: Median edit distance was 7; 9.3% of associated SLDs were identical * HTML similarity technique : Compared CSS, HTML structure, and joint similarity scores. metric : median joint HTML similarity score PREVALENCE: Median joint HTML similarity was 0.04 * Participant relatedness cues technique : Surveyed factors participants used in judgments. metric : percentage selecting each factor PREVALENCE: Branding elements were used by 66.7%; domain names by 57.1% ### NDSS 2023 — Navigating Murky Waters: Automated Browser Feature Testing for Uncovering Tracking Vectors mechanism: cross-browser storage vectors population: custom browser-version list n=126 other (purposive) temporal: active-probing 2019-07..2022-04 crawlConfig: browsers=["Brave","Chrome","Edge","Firefox","Safari","Opera","Tor"] headless=headful stateful=stateless consent=no-interaction depth=single-target-page * Browser mechanisms usable as tracking vectors technique : Write and read 32-bit identifiers across browser contexts metric : number of mechanisms and browser versions PREVALENCE: 21 mechanisms across 126 versions of seven browsers; all latest tested versions were vulnerable to at least one technique * Third-party tracking vectors technique : Automated cross-origin and embedded-context read/write tests metric : number of mechanisms PREVALENCE: 13 mechanisms enabled third-party tracking * Private-browsing isolation bypass technique : Cross-session identifier reads in private browsing contexts metric : number of mechanisms PREVALENCE: two mechanisms bypassed private-browsing isolation * History sniffing via favicons technique : Compared cached favicon loads with Performance API requests metric : qualitative attack demonstration PREVALENCE: Chrome and Safari supported novel favicon history-sniffing attacks ### IMC 2024 — Browsing without Third-Party Cookies: What Do You See? mechanism: third-party-cookie blocking population: Tranco n=10000 domains (top-n) | Tranco n=255 websites (top-n) temporal: live-crawl ?..? crawlConfig: browsers=["Firefox"] headless=headless stateful=stateful consent=no-interaction depth=landing-plus-subpages * Cookie-notice tracking-cookie violations technique : Clicked accept and reject controls, then counted injected cookies metric : number and percentage of websites retaining tracking cookies PREVALENCE: 100 (39%) of 255 websites continued using tracking cookies after rejection * Third-party-cookie rendering effects technique : Compared controlled baseline, control, and cookie-disabled crawl groups metric : BCE screenshot difference and frequency-vector DiD PREVALENCE: More than 90% of domains exhibited less than a 10% screenshot difference * Website-content changes technique : Compared screenshot, image, text, and link feature vectors metric : Jaccard distance and difference in distance PREVALENCE: The frequency-vector DiD distribution was concentrated and symmetric at 0 * Advertisement-request prevalence technique : Counted requests to domains listed in EasyList metric : average advertisement requests versus average BCE screenshot difference PREVALENCE: Positive correlation across categorized website types ### PETS 2025 — Intractable Cookie Crumbs: Unveiling the Nexus of Stateful Banner Interaction and Tracking Cookies mechanism: CHIPS population: Tranco n=20000 domains (top-n) | Tranco n=20000 domains (random) | Tranco n=6518 websites (purposive) temporal: live-crawl 2024-07..2024-07 | live-crawl 2024-08..2024-08 | live-crawl 2025-01..2025-01 crawlConfig: browsers=["Firefox v121.0","Chrome"] headless=not-stated stateful=both consent=accept-and-reject depth=landing-page-only * intractable cookie transmission technique : Compare cookies in accepted-phase Cookie Jar with HTTP-request cookies before rejection. metric : share of websites sending at least one PREVALENCE: around 50% of websites; approximately 45%–55% across runs * banner rejection effect technique : Reject banners, reload pages, and compare sent-cookie counts. metric : average reduction after reload PREVALENCE: websites stop sending approximately 25% of intractable cookies after reload * GPC effect technique : Repeat stateless rejection runs with the GPC signal enabled. metric : average reduction in intractable cookies PREVALENCE: approximately 30%, with an additional 32% after subsequent rejection * banner-type differences technique : Compare intractable-cookie counts for CMP, native, and cookie-paywall banners. metric : relative cookie count PREVALENCE: CMP banners set 6.91 times more intractable cookies than native banners * cookie persistence technique : Analyze actual Cookie Jar expiration times and renewal counts. metric : share exceeding ten days PREVALENCE: nearly 60% of cookies have expiration exceeding 10 days * cookie synchronization technique : Inspect cookie values transferred through redirection URL parameters. metric : share synchronized at least once PREVALENCE: 76 of 2,545 unique intractable cookies, or 3% * partitioned-cookie adoption technique : Chrome stateful crawl records partitioned and non-partitioned cookie attributes. metric : share of tracking cookies partitioned PREVALENCE: 40 of 3,177 tracking cookies, or 1.3%; 26 had accompanying non-partitioned cookies ### IMC 2025 — $CookieGuard: $ Characterizing and Isolating the First-Party Cookie Jar. mechanism: CHIPS (context) population: Universal Tranco list n=20000 websites (top-n) | Tranco top 10k list n=10000 websites (top-n) | Tranco top 10k list n=100 websites (random) temporal: live-crawl ?..? crawlConfig: browsers=["Chrome"] headless=not-stated stateful=not-stated consent=not-stated depth=landing-plus-subpages * cross-domain cookie exfiltration technique : Match cookie-derived identifiers against third-party outbound request query strings. metric : share of websites and unique cookie pairs PREVALENCE: 55.7% of websites; 5.9% of 82,000 unique cookie pairs * cross-domain cookie overwriting technique : Track later writes by scripts whose domains differ from cookie creators. metric : share of websites and unique cookie pairs PREVALENCE: 31.5% of websites; 2.72% of unique cookie pairs * cross-domain cookie deletion technique : Track delete operations by non-owning scripts against attributed cookies. metric : share of websites and unique cookie pairs PREVALENCE: 6.3% of websites; 1.8% of unique cookie pairs * third-party main-frame scripts technique : Identify script inclusion and classify script URLs with filter lists. metric : share of websites PREVALENCE: 93.3% of successfully crawled websites * CookieGuard access-control effectiveness technique : Compare cross-domain actions with and without the browser extension. metric : percentage reduction in affected sites PREVALENCE: 82.2% overwriting, 86.2% deletion, and 83.2% exfiltration reductions * website breakage technique : Manual comparison of functionality with and without CookieGuard. metric : percentage of websites with minor or major breakage PREVALENCE: 11% major SSO breakage; 3% major functionality breakage * CookieGuard runtime overhead technique : Paired Selenium visits with and without the extension. metric : page-load time and with/no-extension ratio PREVALENCE: average overhead of 0.3 seconds across 8,171 paired sites * cross-domain DOM modification technique : Pilot analysis of scripts modifying elements outside their ownership. metric : share of sites PREVALENCE: 9.4% of sites ### WWW 2025 — Least Privilege Access for Persistent Storage Mechanisms in Web Browsers. mechanism: CHIPS (context) population: Tranco n=10000 websites (top-n) | Tranco top 10K websites n=100 websites (purposive) temporal: live-crawl March 2024..April 2024 crawlConfig: browsers=["Nightly Firefox version 98.0a1"] headless=not-stated stateful=not-stated consent=not-stated depth=landing-page-only * Third-party cookie accesses technique : Instrumented cookie APIs logged script and host domains. metric : share of all cookie accesses PREVALENCE: 89.84% of all cookie accesses * Third-party localStorage accesses technique : Instrumented web-storage APIs logged script and host domains. metric : share of all localstorage accesses PREVALENCE: 90.98% of all localstorage accesses * Third-party IndexedDB accesses technique : Instrumented IndexedDB APIs logged script and host domains. metric : share of all IndexedDB accesses PREVALENCE: 72.49% of all IndexedDB accesses * Host-created cookies accessed by third parties technique : Grouped accesses by creator and subsequent accessor domains. metric : share of third-party-accessed cookies PREVALENCE: 20.14% of all cookies accessed by third-party scripts * Host-created localStorage accessed by third parties technique : Grouped accesses by creator and subsequent accessor domains. metric : share of host-created localStorage objects PREVALENCE: 76% of all localstorage objects created by first-party scripts * Functionality breakage technique : Manual comparison of instrumented and vanilla browser behavior. metric : number of affected websites PREVALENCE: 100 manually analyzed websites ### IMC 2023 — Understanding the Privacy Risks of Popular Search Engine Advertising Systems. mechanism: CHIPS (context) population: Google Trends and MovieLens n=500 other (random) temporal: live-crawl ?..? crawlConfig: browsers=["Chrome"] headless=not-stated stateful=stateless consent=accept-all depth=landing-page-only * bounce tracking technique : Traced URL navigation chains and validated Location and status-code headers. metric : share of ad clicks PREVALENCE: 4% on Bing, 86% on Qwant, and 100% on Google, DuckDuckGo, and StartPage * tracker requests technique : Matched browser network-request URLs against EasyList and EasyPrivacy. metric : share of destination pages PREVALENCE: 93% of ad destination pages included tracker and privacy-harming resources * UID smuggling technique : Filtered query parameters, cookies, and localStorage values using heuristics and manual inspection. metric : share of ad clicks with identifiers in destination query parameters PREVALENCE: 80% Bing, 94% Google, 68% DuckDuckGo, 92% StartPage, and 53% Qwant * first-party reidentification technique : Inspected cookies and localStorage for unique identifiers across queries and visits. metric : presence of stored user identifiers PREVALENCE: Google and Bing stored identifiers; the other search engines did not * post-click ad information collection technique : Recorded network requests after ad clicks before navigation away. metric : search engines exhibiting collection PREVALENCE: All five search engines recorded additional user or click information ## 6. The silence: recent web crawls that never mention any of these APIs web crawls published 2023-2026: 332 of those, naming >=1 Privacy Sandbox API family: 24 (7.2%) web crawls published 2024-2026: 235 of those, naming >=1 Privacy Sandbox API family: 18 (7.7%) web crawls published 2025-2026*: 153 of those, naming >=1 Privacy Sandbox API family: 11 (7.2%) Population "web crawl": crawlConfig != null OR studyTypes contains automated-web-crawl, AND platforms contains 'web'. This is the same definition lib.mjs uses for POPULATIONS.webCrawled. ## 7. Of the web crawls that drove Chrome/Chromium, how many name an API Chrome/Chromium web crawls 2023-2026: 129 naming >=1 Privacy Sandbox API family: 11 (8.5%) Chrome/Chromium web crawls 2024-2026: 86 naming >=1 Privacy Sandbox API family: 8 (9.3%) NOTE the sentinel rule: crawlConfig.browsers is stated by only 47.2% of the 1,120 crawling papers, so this denominator is "papers that SAID they drove Chrome", not "papers that drove Chrome".
Quote-check output
Produced by node scripts/ps_quotecheck.mjs. Unedited.
- ps_quotecheck-output.txt
== CCS 2022 privacy-limitations-of-interest-based-advertising-on-the-web-a-post-mortem-empir PASS detection/unique FLoC cohort ID sequences PASS* detection/fingerprinting-enhanced cohort unicity (matched after stripping punctuation/spaces) NEAR detection/demographic browsing differences (11/11 long tokens present) quote: For each of the chi-square tests of independence described in Section 5.1.1, we find a statistically significant difference in domain visit frequency PASS detection/demographic t-closeness violations NEAR population/comScore Web Behavior Database (7/7 long tokens present) quote: Before preprocessing, the sessions dataset includes 93,808 machines. NEAR population/comScore Web Behavior Database (7/7 long tokens present) quote: The resulting preprocessed dataset contains 2,073,405 machine-week records. PASS population/2017 U.S. Census Bureau Current Population Sur NEAR population/2017 U.S. Census Bureau American Community Sur (9/9 long tokens present) quote: We also use 2017 census data from the U.S. Census Bureau American Community Survey (ACS) and Current Population Survey (CPS) PASS temporal/existing-dataset == PETS 2023 locality-sensitive-hashing-does-not-guarantee-privacy-attacks-on-googles-floc-an PASS* detection/FLoC browsing-history reconstruction (matched after stripping punctuation/spaces) PASS detection/MinHash trajectory narrowing NEAR detection/SimHash pre-image generation (3/3 long tokens present) quote: We vary the SimHash bit length from 5 to 25 in increments of 5. NEAR detection/MinHash checkpoint identification (7/7 long tokens present) quote: A set of checkpoints found by our attack has on average 805.5 ± 439.7 checkpoints. Recall that the total number of checkpoint is 7744. NEAR population/MovieLens (4/4 long tokens present) quote: The training set contains 120 000 histories and the test set 5000 histories. PASS population/Porto Taxi dataset NEAR temporal/existing-dataset (5/5 long tokens present) quote: To evaluate our attacks, we instead use the MovieLens dataset [14]. PASS temporal/existing-dataset == PETS 2023 on-the-robustness-of-topics-api-to-a-re-identification-attack PASS detection/cross-site re-identification NEAR detection/profile uniqueness (5/5 long tokens present) quote: even in the case of a population of 100, 000 personas, we still observe 35−42% of them being non-2-anonymous. PASS detection/false re-identification PASS population/EasyPIMS PASS population/EasyPIMS browsing-history dataset PASS temporal/passive-collection == CCS 2024 the-privacy-utility-trade-off-in-the-topics-api PASS* detection/Third-party-cookie re-identification (matched after stripping punctuation/spaces) PASS* detection/Topics API user re-identification (matched after stripping punctuation/spaces) NEAR detection/Topic popularity inference (10/10 long tokens present) quote: For instance, for a population of only 10 individuals, the chance of an analyst correctly counting the occurrence of a topic t is as low as 0.56% for PASS detection/Distinguishing genuine from random topics PASS population/AOL search logs dataset PASS… population/AOL search logs dataset with Citizen Lab class (2 elided fragments, all present in order) PASS… population/AOL search logs dataset with Google Topics cla (2 elided fragments, all present in order) PASS temporal/existing-dataset == PETS 2024 interest-disclosing-mechanisms-for-advertising-are-privacy-exposing-not-preservi NEAR detection/Topics noise removal (5/5 long tokens present) quote: we find that (1) about 25% of the noisy topics on single websites in one-shot scenarios PASS detection/Cross-site re-identification PASS detection/Topics classification utility PASS detection/Subdomain topic manipulation PASS population/CrUX top-list NEAR population/Tranco (5/5 long tokens present) quote: the top 1M most visited eTLD+1 from Tranco classified with the Topics API NEAR population/Google static mapping (9/9 long tokens present) quote: Consisting of exactly 9254 domains, Table 2 shows the distribution of topics per individual domain on this static mapping. PASS population/Mozilla-reported browsing-history distribution PASS population/synthetic dataset generated from published bro PASS population/CrUX top-list NEAR temporal/existing-dataset (5/5 long tokens present) quote: we use the latest version of the individual draft proposal of Topics from May 30, 2023 == USENIX 2024 fledging-will-continue-until-privacy-improves-empirical-analysis-of-googles-priv NEAR detection/FLEDGE API adoption (7/7 long tokens present) quote: We detected the use of FLEDGE APIs in 9.5% (n = 6,682) of the 70K sites that we visited during the measurement period. NEAR detection/Google auction dominance (11/11 long tokens present) quote: We observed a total of 11,959 auctions for ad spaces during our measurement period, almost all of which (n = 11,941) were run by Google Ad Manager. PASS detection/Interest-group creation PASS detection/Interest-group persistence attempts PASS detection/FLEDGE privacy attacks PASS… detection/Owner-sniffing attack performance (2 elided fragments, all present in order) NEAR population/Tranco (5/5 long tokens present) quote: Using our automated approach, we perform a crawl of the top 70K domains from the Tranco list [35]. NEAR population/custom seed list (10/10 long tokens present) quote: For our evaluation we gathered a list of 500 domains and used Puppeteer to open a fresh browser instance and join interest groups from a subset of own NEAR temporal/live-crawl (5/5 long tokens present) quote: Our framework collected the API calls on these sites in June and July 2023 ... and again in September 2023 when it was publicly released. == CCS 2025 exploiting-the-shared-storage-api NEAR detection/network timing covert channel (6/6 long tokens present) quote: we find that the maximum delay would be between 0.36 seconds ... and 1.82 seconds ... for a 33-bit long identifier PASS detection/worklet crash covert channel PASS… detection/queue timing covert channel (3 elided fragments, all present in order) NEAR detection/iframe caching covert channel (6/6 long tokens present) quote: this would require 66 requests to read a 33 bit identifier as the two possible resources must be unique for each bit PASS detection/data leakage over time PASS detection/attestation-file availability PASS detection/Shared Storage API usage PASS detection/Shared Storage access among attested domains PASS population/Google's Privacy Sandbox Enrollment Report and NEAR population/Tranco (4/4 long tokens present) quote: we check the rank of these domains in the Tranco list from the same date as the Chrome browser file. PASS population/Tranco PASS… population/Disconnect Tracker Protection list (2 elided fragments, all present in order) PASS temporal/active-probing PASS temporal/live-crawl PASS temporal/existing-dataset == IMC 2024 a-first-look-at-related-website-sets PASS detection/Perceived website relatedness PASS… detection/RWS set composition (2 elided fragments, all present in order) NEAR detection/RWS pull-request outcomes (10/11 long tokens present) quote: the split between approved and closed (without merging) requests has shifted, with 58.8% of all pull requests closed without being merged PASS… detection/Second-level-domain similarity (2 elided fragments, all present in order) PASS detection/HTML similarity PASS detection/Participant relatedness cues PASS population/Related Website Sets list PASS population/Tranco NEAR population/Related Website Sets GitHub pull requests (8/8 long tokens present) quote: Figure 5 shows the cumulative count of pull requests on the Related Website Sets list over time, through to March 30th 2024, comprising 114 requests. NEAR temporal/existing-dataset (6/6 long tokens present) quote: Figure 5 shows the cumulative count of pull requests on the Related Website Sets list over time, through to March 30th 2024 PASS temporal/live-crawl == NDSS 2023 navigating-murky-waters-automated-browser-feature-testing-for-uncovering-tracking-vectors PASS… detection/Browser mechanisms usable as tracking vectors (2 elided fragments, all present in order) NEAR detection/Third-party tracking vectors (9/9 long tokens present) quote: We test 21 browser mechanisms and uncover a slew of tracking vectors, including 13 that enable third-party tracking NEAR detection/Private-browsing isolation bypass (10/10 long tokens present) quote: including 13 that enable third-party tracking and two that bypass the isolation offered by private browsing modes. NEAR detection/History sniffing via favicons (7/7 long tokens present) quote: We then developed a test to link different 3P favicons to the Performance API, and included it in the test suite offered by CanITrack. PASS* population/custom browser-version list (matched after stripping punctuation/spaces) PASS… temporal/active-probing (2 elided fragments, all present in order) == IMC 2024 browsing-without-third-party-cookies-what-do-you-see PASS detection/Cookie-notice tracking-cookie violations PASS detection/Third-party-cookie rendering effects PASS detection/Website-content changes PASS detection/Advertisement-request prevalence PASS population/Tranco PASS population/Tranco PASS* temporal/live-crawl (matched after stripping punctuation/spaces) == PETS 2025 intractable-cookie-crumbs-unveiling-the-nexus-of-stateful-banner-interaction-and PASS detection/intractable cookie transmission PASS detection/banner rejection effect PASS… detection/GPC effect (2 elided fragments, all present in order) PASS detection/banner-type differences PASS detection/cookie persistence NEAR detection/cookie synchronization (6/6 long tokens present) quote: out of 2,545 unique intractable cookies in the RandComb run, 76 (i.e., 3%) are synchronized at least once NEAR detection/partitioned-cookie adoption (9/9 long tokens present) quote: among 3,177 unique tracking cookies, only 40 (1.3%) are partitioned, of which 26 are accompanied by non-partitioned tracking cookies NEAR population/Tranco (5/5 long tokens present) quote: Our target list consists of the landing pages of top-ranked domains. NEAR population/Tranco (4/4 long tokens present) quote: we sample and mix up 20k domains from Tranco's top 50k domains list NEAR population/Tranco (11/11 long tokens present) quote: we conduct a separate stateful run using Chrome on the domains with previously detected banners on them in the RandComb run (i.e., 6,518 in total) PASS… temporal/live-crawl (2 elided fragments, all present in order) PASS… temporal/live-crawl (1 elided fragments, all present in order) PASS… temporal/live-crawl (2 elided fragments, all present in order) == IMC 2025 cookieguard-characterizing-and-isolating-the-first-party-cookie-jar NEAR detection/cross-domain cookie exfiltration (12/12 long tokens present) quote: We find that about 55.7% of websites include at least one script that exfiltrates a cookie it did not set originally. These behaviors impact 5.9% (4,8 NEAR detection/cross-domain cookie overwriting (7/7 long tokens present) quote: We find that approximately 32% of websites include at least one script that overwrites a cookie it did not originally set NEAR detection/cross-domain cookie deletion (13/13 long tokens present) quote: while 6.3% include a script that deletes a cookie it did not create. Overall, 2.72% of all unique cookie pairs are overwritten by non-owning scripts, NEAR detection/third-party main-frame scripts (8/8 long tokens present) quote: We find that 93.3% of the 14,917 successfully crawled websites in our dataset include at least one third-party script in their main frame NEAR detection/CookieGuard access-control effectiveness (13/13 long tokens present) quote: CookieGuard significantly reduces cross-domain actions on first-party cookies: overwriting by 82.2%, deletion by 86.2%, and exfiltration by 83.2%. PASS detection/website breakage PASS detection/CookieGuard runtime overhead PASS detection/cross-domain DOM modification PASS population/Universal Tranco list PASS population/Tranco top 10k list PASS population/Tranco top 10k list PASS temporal/live-crawl == WWW 2025 least-privilege-access-for-persistent-storage-mechanisms-in-web-browsers PASS detection/Third-party cookie accesses PASS detection/Third-party localStorage accesses PASS detection/Third-party IndexedDB accesses PASS detection/Host-created cookies accessed by third parties PASS detection/Host-created localStorage accessed by third pa NEAR detection/Functionality breakage (12/12 long tokens present) quote: We manually analyzed the effect of our solution for 100 websites where third-party scripts access the host-page cookies with the default policy PASS* population/Tranco (matched after stripping punctuation/spaces) NEAR population/Tranco top 10K websites (9/9 long tokens present) quote: We manually saved the top 100 websites in the list of websites that contained at least one third-party script accessing cookies set by the host-page. PASS temporal/live-crawl == IMC 2023 understanding-the-privacy-risks-of-popular-search-engine-advertising-systems PASS detection/bounce tracking PASS detection/tracker requests PASS detection/UID smuggling PASS detection/first-party reidentification PASS detection/post-click ad information collection PASS population/Google Trends and MovieLens PASS temporal/live-crawl == TOTAL 131 quotes: PASS 91, NEAR 40, FAIL 0 PASS = quote found verbatim after whitespace collapse. PASS* = found after also stripping punctuation and spaces PASS… = quote contains an "..." elision; every fragment of >=12 characters was found, in the paper's own order. (a space inside a number, a ligature, an en-dash). NEAR = >=80% of its 6+-character tokens present but not contiguous (column-reading or ligature damage). Read these by hand. FAIL = not located. No figure resting on a FAIL is published.
Review log
Three focused sonnet passes on the published first draft, run in parallel, each told explicitly that the author's context may not be exhaustive and each handed the page text, the report script, its output and these notes. Findings applied below; a generic fable pass follows.
sonnet — figures versus script
| Finding | Disposition |
|---|---|
| By-year table: 2020 says 4 and 2022 says 8 in the “any API family” column; the script says 2 and 7. | Accepted, fixed. Diagnosed: the year table was copied from a run made before the folding regexes were narrowed, and never refreshed. This is exactly the carry-over defect the task spec warns about, committed inside a single sitting. The whole table was regenerated from the current output |
/\bFLEDGE\b/i matches “full-fledge crawling system” in de-Kodi (WWW 2020) and “a full-fledge experience” in Android SmartTVs (USENIX 2021) | Accepted, fixed. Verified both by hand. The Protected Audience family is now case-sensitive; count 11 → 8 |
/First[- ]Party Sets/i matches “the first party sets the cookies” in Cookie Swap Party (WWW 2021) | Accepted, fixed. Verified. RWS family is now case-sensitive; count 6 → 4 (the same fix also dropped a second borderline match) |
| Bare “attribution reporting” matches Apple's SKAdNetwork in the 2026 PETS PET-paradox paper, so the page's “four passing mentions” is three | Accepted, fixed. Verified. The naive case-sensitive fix over-corrected to zero — papers write “Attribution reporting API” and “privacy sandbox attribution reporting api” — so the accepted rule is capitalised-proper-name OR phrase-plus-“API”. Count 4 → 3, and both places on the page that said “four” now say “three” |
The published ps_cdp_probe.mjs snippet produces zero events when run as printed, contradicting the page's “verified end to end” claim; the real script uses a local mock HTTPS origin the snippet omits | Accepted, fixed, and the most useful finding of the three passes. The block now contains sandbox/ps_cdp_probe.mjs in full — mock server, bidding script, decision script, worklet and all — and the surrounding prose says what its real output was and what happens if you point the listeners at a site you do not control. The reviewer also mutation-tested the real script (removing setSharedStorageTracking drops the event count from 7 to 3), which is quoted on the page |
| Provenance says “3 more” CHIPS-context papers; the content page names two, and the third has no CHIPS content of its own | Accepted, fixed. Now one, and it is named |
| Family table not sorted descending by count | Accepted, fixed (had already been caught independently) |
| Rendered heading/citekey counts in this log are close but do not reproduce exactly | Accepted, fixed. Recounted from the rendered DOM after the final save; the numbers in Bibliography are the recount |
Not found by this pass, and worth recording: it verified the silence table against the pre-fix script and reported it correct, which it was — but the four fold fixes then moved it (25/19/12 → 24/18/11). The page was re-derived from the post-fix run, not patched.
sonnet — citations and quotes
| Finding | Disposition |
|---|---|
kancherla2025_least does not support the claim it is cited for. The page said it and bahrami2025_cookieguard “both note that Chrome's partitioning is opt-in per cookie while Firefox and Safari partition by default”. “Safari” and “opt-in” appear zero times in that paper; “CHIPS” appears once, in the reference list | Accepted, fixed. Verified. The sentence now attributes the claim to bahrami2025_cookieguard alone, quotes its actual wording, and says plainly that an earlier draft attached it to the wrong paper |
Five pre-existing same-paper-different-key duplicates elsewhere in literature:bibliography (lerner2016internet/lerner2016_internet, bouhoula2024automated/bouhoula2024_automated, fouad2022my/fouad2022_cookie, bottger2025_regional/boettger2025_regional, ahmad2026_ipfp/ahmad2026_more) | Noted, not acted on. None involves this page's 13 keys and none was introduced by this run. Left for whoever owns the bibliography; recorded here so the next run does not rediscover it as new |
Everything else passed: 13 keys resolve, 724 keys unique, the six new entries match Crossref on authors, order, title, year, venue and DOI, ~34 quantitative claims traced to the sentence in paper.cols.txt, all 8 footnotes verbatim against live sources, and the claim that rasaii2025_crumbs cites the PAM CHIPS paper with a broken DOI independently confirmed (10.1007/978-3-031-55528-2_1 404s; the correct one is 10.1007/978-3-031-85960-1_8).
sonnet — external currency
| Finding | Disposition |
|---|---|
| Nothing wrong. ~65 external claims independently re-fetched; zero out of date | No change. Confirmed specifically: M153 has not shipped (stable_date 2026-09-08, stable_cut is today), no removal entry has moved off Proposed, and the “there is a window” framing is still correct as written |
| A summarising fetch tool mis-read the status page's Discontinue / Do-not-launch groupings; the raw HTML table matches the page exactly | Noted. Recorded as a warning to the next reviewer: on that page, parse the table, do not summarise it |
bugzilla.mozilla.org/show_bug.cgi?id=1979093 returns HTTP 400 to a bare HEAD but 200 to a GET | Noted. Not a dead link; recorded so a future link-checker run does not report it as one |
Also confirmed by this pass and not previously checked here: Playwright 1.62.1 is npm's current latest (2026-07-30) and bundles exactly Chromium 151.0.7922.34; Playwright's chromiumSwitches.ts on main disables ThirdPartyStoragePartitioning and no ads-API feature; the W3C Attribution Level 1 Working Draft is dated 2026-08-28; taxonomy_v3.md returns 404.
fable — generic
Run after the three focused passes and their fixes, with no checklist. It found more than the other three combined, and two of its findings were substantive enough to change what the page tells a reader to do.
| Finding | Disposition |
|---|---|
“Topics and Attribution Reporting have no CDP surface at all, in any Chromium version checked” is misleading, and it misdirects the page's own headline call to action. “Any version checked” was two versions. ARA had a full CDP Storage surface until it was removed from the protocol in April 2026 | Accepted, fixed, and this was the most valuable finding of the whole review. Verified independently by bisecting the devtools-protocol history (present 2026-04-01, absent 2026-04-08). The page now carries a box saying the gap is closable with first-class instrumentation on a pre-April-2026 build, with a one-line check for whether a given binary has it. The CDP table has a new row. The two dead ARA listeners in the published script now carry a comment saying why they are there |
| HTTP headers are missing entirely as an observation surface, and they are arguably the primary one for Attribution Reporting and Topics | Accepted, fixed. New subsection with all five headers, verified against MDN. Added to the “What to Report” list |
Wrapping the attributionSrc IDL setter misses server-rendered attributionsrc content attributes, because the parser does not invoke IDL setters | Accepted, fixed. The page now says not to do it and gives three alternatives. This was a genuine instrument bug in advice the page was giving |
privacysandbox/attestation and patcg-individual-drafts/topics are also archived; the page marked only the RWS repo, and said “two of the three” artefacts are frozen | Accepted, fixed. Verified through the GitHub API: archived, last pushes 2026-01-22 and 2025-11-07. All three rows now carry the archive date, and the “two of the three” sentence is rewritten — with the distinction that the .dat inside a Chromium install still moves with the component updater |
| The enrollment-closure “consequence” this log claimed the page stated was never actually written on the page | Accepted, fixed. The quote and the --privacy-sandbox-enrollment-overrides workaround are now both on the content page. This log's own claim has been corrected |
| Two stale figures in this log's prose: “4 of 5,869 papers name Attribution Reporting” and “the 8.1% silence figure”, both pre-fold-fix | Accepted, fixed. This is the known failure mode where a correction reaches the page but not the log; recording that it happened here too |
| The removal-date slip history is only in this log, not where the reader decides. “Measured in days” is stated as if M153 were firm | Accepted, fixed. The slip history (M144 → M150 → M152/M153/M155) is now a bullet in the top box, and “days” is hedged |
| “They have not been removed yet” rests on a Chromium 151 probe while stable is M152 | Accepted, fixed. The box now says so in one clause |
| Missing hedge: field trials. A code-removal milestone is the latest end of the window; Chrome ramps features down server-side first, and an automation Chromium has no variations seed | Accepted, added, flagged as not established for these specific features |
| The measured-results table has no written inclusion rule, and two of its rows are not Privacy Sandbox measurements | Accepted, fixed. The rule is now stated above the table and the two exceptions are named and justified. The precision sentence in Folding and residue counts the same 11 |
| Missing: user-side gating. Real users hold heterogeneous “Ad privacy” toggle states; a crawler profile is one | Accepted, added to the pitfalls list |
| “A dozen papers … over sixteen years”, after showing 43/67/9 and an 11-row table; and self-referential prose about its own placement; and “the re-identification numbers everyone cites” in a page that proves almost nobody cites them | All three accepted, fixed |
| The silence figure's denominator includes crawls with no reason to touch an ads API | Accepted. The defence was in this log only; half a sentence of it is now on the content page, framing the figure as an upper bound on non-engagement |
| Bounce-tracking dating differs in phrasing from browser_storage | Accepted, fixed by naming both the milestone and the later default-on, and cross-linking |
| The provenance page is honest in tone; its overclaims are the three concrete ones above | Noted. All three fixed |
Rejected: nothing. Every finding from this pass was accepted. That is unusual and is recorded as such: it is evidence the generic slot earns its place, and also that the three focused passes, by having checklists, all missed the same class of defect — advice that is well-sourced and wrong.
Work done after the generic pass, and the second round
Verifying the generic pass's “removal dates have already slipped twice” finding against the blink-dev thread turned up more than the finding itself, and the content page gained a section that no reviewer had seen: “The only post-2025 adoption numbers that exist are Chrome's own”, five rows of Chrome Platform Status motivation telemetry. It is recorded here rather than folded silently into the page because it postdates every review, and because it is exactly the kind of late addition that escapes an audit trail. Its sources are in External sources, verified and rejected; two subsequent re-review passes checked all five quotes verbatim and found no error in them.
Two sonnet re-review passes were then run over the edited pages, one on figures and one on currency and citations, both told the earlier fixes might themselves be wrong.
| Finding | Disposition |
|---|---|
| The DOM self-check numbers in Bibliography are still wrong — “60 citation markers, 10 tables, 21 headings, 4 WRAP boxes” against actual 33 source markers / 12 tables / 23 headings / 5 boxes. Found independently by both re-reviewers, who then disagreed with each other (33 vs 60 markers, 21 vs 22 headings) | Accepted, fixed, and the disagreement diagnosed. The whole paragraph is now a table with the counting method stated, and it explains the trap that caught both of them: the plugin emits two spans per source marker, and ~~DISCUSSION~~ adds a heading. Third time |
“Every call returned a benign empty value rather than throwing” is contradicted by the page's own committed probe output — sharedStorage.worklet.addModule() throws OperationError in all four configurations, and the illustrative code box on the page had silently dropped that line | Accepted, fixed. The eighth call is back in the box, with the reason (the probe server returns text/html for every path, so the worklet module fails a MIME check — an artefact of the probe, not of the API) and a note that it was trimmed for one revision |
The headers table overclaims: Attribution-Reporting-Register-Source names a destination field; MDN documents no “reporting origin” field, which is implicit in the sending origin | Accepted, fixed |
The bisect window is wider than necessary. The removal landed in one commit, 96e032552f, 2026-04-02, “Roll protocol to r1608973”; the last commit with the symbols is 1abe750809, 2026-03-25 | Accepted, fixed. Both re-reviewers found the same commit independently. The page now names it |
The page's “which partition by default” gloss on Firefox and Safari is not literally in bahrami2025_cookieguard | Accepted, fixed. The gloss is now marked as ours and attributed to browser_storage instead |
| The new telemetry section had no provenance trail | Accepted, fixed — this subsection |
--privacy-sandbox-enrollment-overrides could not be confirmed against Chromium source (code search needed auth); corroborated only by Google's own docs and by the fact that this run's own probe passes it without error | Accepted as a limitation, recorded. The switch is used in sandbox/ps_call_probe.mjs and Chromium does not reject it, which is weaker than a source citation |
w3.org/TR/attribution/ returns 403 to curl but 200 to a browser-grade fetch; Bugzilla rejects bare HEAD | Noted. Neither is a dead link; recorded for future link-checkers |
Everything else in both re-reviews checked out: the fold fix lost exactly the one true positive already admitted and no more (179 of 180 case-insensitive extras in the Protected Audience family are “full-fledged”), every regenerated table matches a fresh script run byte for byte, the published code block is byte-identical to the committed script and reproduces its seven events, the mutation test still drops it to three, and all 34 external claims re-fetched today were confirmed verbatim.
Removal-status refresh, 2026-09-27
- Run date: 2026-09-27 (UTC). Authoring agent: Claude Opus 5.5, executing the drain item “privacy_sandbox removal slippage 2026-09-23”. Review passes are logged in Review log, 2026-09-27 refresh.
- Trigger: the external-currency reviewer of advertising (Finding 4, 2026-09-23). It noted that Chrome stable was M154 while chromestatus 6552486106234880 still read Proposed, M153, and asked this page to show the slip. Chrome 150's release notes list no removals, and Topics has no dedicated removal entry.
- Scope: the removal-status statements only. No corpus figure was touched; the report script, folding and quote-check sections above are from 2026-09-01 and were not re-run. The 2026-09-01 browser measurements stay on the content page, now labelled as pre-removal. Where one was wrong, it is corrected (see below).
- Method: rather than restate a third status source, measure the binaries. Every status source was re-fetched, and fourteen builds from 151 to 156 were probed. Four are Google Chrome for Testing; the first draft probed only ten Chromium builds (see the review log).
What the 2026-09-01 page said that did not hold
| Statement on the page until 2026-09-27 | What the refresh found | Change |
|---|---|---|
“The removal milestone for Topics, Protected Audience, Attribution Reporting, Shared Storage, Related Website Sets and requestStorageAccessFor is M153” | For Topics there is no Chrome Platform Status removal entry at all. The chromestatus search for “Topics”, “browsingTopics” and “Deprecate and remove” returns 0 Feature-removal entries naming Topics, out of 53 Feature-removal entries in the last query. The Topics ship entry, 5680923054964736, has no deprecation stage. This log's 2026-09-01 notes say “it is the removal that moved M150 → M152 → M153” without naming the entry the M153 came from. For the rest, M153 is the 460 (removal ship) stage of entries whose summary still reads Proposed. The binaries changed at 152, 153 and 154 | Top box and table rewritten around the measurement; M153 is described as a status label |
| “Removal is a staircase … the current entry says M153” (Topics) | Same: no Topics entry exists to say M153 | Rewritten |
| “Chromium 151.0.7922.34 exposes and answers every one of the APIs” | True of chrome-headless-shell 151, which is what Playwright's headless: true launches and what ps_call_probe.mjs ran. The full Chromium 151 of the same Playwright release, launched through executablePath, rejects sharedStorage.set() (“sharedStorage is disabled”) and runs no auction. Found by accident: the first milestone-probe run passed executablePath: chromium.executablePath() for its baseline and disagreed with the committed ps_call_probe-output.txt | The presence section and “What to Report” now say which kind of binary. Puppeteer's 2026-09-01 row was a full-Chromium presence probe, so it is unaffected |
| Fenced Frames “Removal proposed, M155” | Entry 6366274495053824 now says M156, updated 2026-09-24 | Updated |
| HTTP headers: “no dependence on the milestone that kills the CDP surface” | Attribution-Reporting-Eligible was sent by both 151 builds and by none of the twelve builds from 152 to 156 | Rewritten: the header dies with the API |
CDP table: getRelatedWebsiteSets present at tip-of-tree | devtools-protocol master on 2026-09-27 has only runBounceTrackingMitigations | Row split |
| “the window to measure a deployed Attribution Reporting API in Chrome is days” | The API went inert at 152 (no header, Permissions-Policy feature unknown) | “The window has closed in current Chrome” |
| Topics footnote attributed the plan to “Yao Xia” | The thread names the author “Yao Xiao” (40 occurrences in the fetched HTML, none of “Yao Xia” alone) | Footnote replaced by one quoting the Protected Audience thread's plan message, signed “Paul”, and linking all five threads |
| Timeline: “2026-09-08 M153 stable date: the targeted removal milestone for …” | Replaced by what was measured at each of M152, M153 and M154 | Rows rewritten, two added |
“Puppeteer was pointed at the same Chromium binary via chromium.executablePath(), so this is a comparison of the two libraries' default flag sets, not of two browser builds” (content page and this page's run record) | chromium.executablePath() is the full Chromium; Playwright's default is chrome-headless-shell. The 2026-09-01 comparison was two binaries. Its result stands: presence and Permissions-Policy answers agree in builds [1] and [2]. Missed by this refresh's first draft; the generic reviewer caught it | Both sentences corrected |
Status sources as of 2026-09-27
| Source | What it says | How it was read | Used as |
|---|---|---|---|
chromiumdash fetch_releases / fetch_milestone_schedule | Stable = M154, 154.0.8037.57. Stable dates: M152 2026-08-25, M153 2026-09-08, M154 2026-09-22, M155 2026-10-06, M156 2026-10-20 | JSON API | Dates on the page |
| Chrome Platform Status, 22 per-feature entries | Every removal entry Proposed. Removal stage (460) at desktop 153 for Protected Audience, Attribution Reporting, Shared Storage, RWS and requestStorageAccessFor; 152 for Private Aggregation; 156 for Fenced Frames. No Topics removal entry. The CHIPS WebView entry is WebView-only | Per-feature endpoint, every stages[] element printed. Stage names come from internals/core_enums.py on GoogleChrome/chromium-dashboard main (410 plan, 430 dev trial, 450 deprecation trial, 460 ship, 470 remove code) | “What the status label says” column only. The summary status field is not used as evidence: it says Proposed for removals the release notes list. The stage-460 milestones are quoted, as labels |
developer.chrome.com/release-notes/150–154 | 150: “There are no deprecations or removals in this release.” 151: macOS 12 and a FontFaceSet IDL change. 152: “Remove Private Aggregation API … is deprecated and removed”. 153: five “Deprecate and remove” headings, each worded “planned for deprecation and removal”. 154: no Deprecations section at all. 155/156: 404 (not yet stable) | Fetched with ?hl=en and accept-language: en-US. Without them this run got the release notes in Hebrew and the status page in Spanish; Google's devsite localises by vantage | Release-notes column; the M154 gap is published |
blink-dev intents: Protected Audience k_nubsMb97g, Topics _R85yctz4Rs, Attribution Reporting 4K2RRt6VYCQ, Shared Storage uh5Ke6qyegc, Private Aggregation Ld7avyD0U0Q | The same four-step plan in the first four threads (2026-06-11/12), and “no need to replace it with a stub” for Private Aggregation. September updates only on PA (09-07 question, 09-23 request for 100%, 09-24 correction: “~ 1% usage”, 10% through Oct 12, 100% from Oct 26) and Topics (09-24: 1% on M153+, “current usage is at 2%”, 10% through Oct 15, 100% after). ARA's newest message is 2026-07-01, SS's 2026-07-07, PrivAgg's 2026-07-08 | Google Groups HTML fetched with curl/fetch; tags stripped, whitespace collapsed, needles checked by the script. Message dates are Google Groups' display dates and may be off by one day at a time-zone boundary. “Newest” is the newest date stamp in the server-rendered thread page, not checked against Google Groups' own message count; the script prints the stamp count per thread, and it fails if the newest date moves | Rollout column and the telemetry additions |
privacysandbox.google.com/overview/status | “Last updated: August 14, 2026”, unchanged since 2026-09-01. The Shared Storage row (removed) sits under Deprecate and remove with Fenced Frames (present) | Table parsed, not summarised | Footnote only. It cannot tell a removed API from a present one |
Chrome for Testing (googlechromelabs.github.io/chrome-for-testing) | linux-arm64 builds exist from 153 on: 153.0.8010.52, 154.0.8037.57, 155.0.8059.12, 156.0.8075.0. 151 and 152 are linux64 (x86-64) only | The first pass read only the linux64 column. It downloaded those four x86-64 zips, which fail here (rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2), and concluded “no Google Chrome binary can run on this host”. The external-currency reviewer caught this. The linux-arm64 zips were then downloaded (SHA-256 below) | Used: four probe builds, and the only Google-built ones. The x86-64 zips were deleted |
Debian bookworm-security arm64 chromium + chromium-common 153.0.8010.52-1~deb12u1 and 154.0.8037.57-1~deb12u1 | The two stable releases, built from Chromium source | Downloaded from security.debian.org/debian-security/pool/updates/main/c/chromium/, unpacked with dpkg -x. SHA-256 of the four .deb files are listed below the table. 152.0.7977.82-1~deb12u1 is the container's installed package | Probe builds |
| Playwright prereleases | playwright-core browsers.json maps 1.63.0-alpha-2026-08-23 to headless shell 152.0.7977.54, 1.63.0-alpha-2026-08-29 to 153.0.8010.12, 1.64.0-alpha-1789764292000 to 154.0.8037.0 and 1.64.0-alpha-2026-09-27 to 155.0.8059.12 | npm pack each, read browsers.json, install with that package's own cli.js into a scratch PLAYWRIGHT_BROWSERS_PATH | Probe builds. They are pre-stable builds of each milestone and are labelled as such |
SHA-256 of the Debian packages and the Chrome for Testing zips:
a7bba2726939dfa0484dee3f9c4da46761e32a7b0ee97ffc7040be1582dc5824 chromium_153.0.8010.52-1~deb12u1_arm64.deb 9e5f90c9643dcbe2964e16d5972e4e7f76c18b1cad7845855116a4890895884d chromium-common_153.0.8010.52-1~deb12u1_arm64.deb 7ac805587eadc495567342826ec5043b7a9422a5c52b474e469cb23532e1ee1d chromium_154.0.8037.57-1~deb12u1_arm64.deb 415762c28547f542b7928a093015ed2793de89ff88db47083ee1ef4280bfa435 chromium-common_154.0.8037.57-1~deb12u1_arm64.deb 794441f3254273eb30d710b3eaab3cd1c7bbf1c88a40275470f6cee350881ce5 chrome-for-testing 153.0.8010.52 linux-arm64/chrome-linux-arm64.zip da83171e552650df34272a9c51f62182bae88d467d1ac19c92dd97917dfa0bca chrome-for-testing 154.0.8037.57 linux-arm64/chrome-linux-arm64.zip bf94336ee2e3edb1279da28a74a744958ba10741e5994351610bc19028482b0b chrome-for-testing 155.0.8059.12 linux-arm64/chrome-linux-arm64.zip f10960090a52d557b5bcc2803a30e3a692852589cb4f37649e7f2d724fd4c0a2 chrome-for-testing 156.0.8075.0 linux-arm64/chrome-linux-arm64.zip
Milestone probe, 2026-09-27
sandbox/ps_milestone_probe.mjs runs, in each build, the presence expressions of ps_probe.mjs and the calls of ps_call_probe.mjs. It adds two probes of its own, both added after the generic review. document.featurePolicy.features() membership distinguishes a policy feature the browser no longer knows from one it knows but does not grant; it agrees with allowsFeature() in all 14 builds. A fetch() with browsingTopics: true provokes Sec-Browsing-Topics, which is sent only by the two 151 builds. It lifts both at run time with a regex, so it cannot drift from the published probes. It also runs the auction fixture of ps_cdp_probe.mjs, its bidding, decision and worklet scripts lifted the same way. Evidence comes from the server's request log, because the CDP events that probe relied on no longer exist from 152. Finally it reads each binary's own /json/protocol.
Two things had to be solved to run it, and both are worth the next run knowing:
- Debian's Chromium can be driven by Playwright. It aborts at launch with
chrome_crashpad_handler: --database is required. That is the crash the workdir README records as “Playwright cannot drive /usr/bin/chromium”. PointingexecutablePathat the real binary,/usr/lib/chromium/chromium, is not enough on its own. SettingBREAKPAD_DUMP_LOCATIONin the launch environment fixes it. - Chrome for Testing runs here only from 153, and only as its
linux-arm64build (see the table above). Its four builds agree with Debian's Chromium on every row of the output.
Positive control: build [1], headless shell 151, runs the auction to a urn:uuid and fetches /bid.js, /decide.js, /bid.js. It also sends Attribution-Reporting-Eligible and fetches the Shared Storage worklet. Every “stopped at 152” statement on the content page is a difference from that control in a build of the same kind: headless shell 152.0.7977.54, build [3]. Full Chromium 151, build [2], already fails the auction and Shared Storage rows, so for those two APIs no full-Chromium build shows the change. The first draft said “from M152” without that qualifier, and the figures reviewer caught it.
Controls re-run unchanged the same day: ps_probe.mjs (output identical to 2026-09-01 except the date line), ps_call_probe.mjs (identical), ps_cdp_probe.mjs (the same seven events, differing only in ids and timestamps), ps_cdp_surface.mjs (the 151 column identical; master lost getRelatedWebsiteSets).
- run_ps_milestone_probe.sh
#!/usr/bin/env bash # Exact invocation behind ps_milestone_probe-output.txt (2026-09-27). # Builds: Playwright 1.62.1's bundled pair (chrome-headless-shell and Chromium, # both 151.0.7922.34); Debian bookworm-security arm64 Chromium 152 (installed), # 153.0.8010.52 and 154.0.8037.57 (the two stable releases; .deb unpacked with # dpkg -x under ps0927/deb/, sha256 in the provenance page); and the pair bundled # by playwright-core 1.64.0-alpha-2026-09-27 (155.0.8059.12, beta), installed # with its own cli.js into ps0927/pwbrowsers. Headless shells for 152/153/154 # come from the Playwright prereleases that bundled them (1.63.0-alpha-2026-08-23 # -> 152.0.7977.54, 1.63.0-alpha-2026-08-29 -> 153.0.8010.12, # 1.64.0-alpha-1789764292000 -> 154.0.8037.0): these are pre-stable builds of # each milestone, not the stable patch, and are labelled as such. Google Chrome # for Testing linux-arm64 (published from 153.0.8001.0 on; no 152 exists) for # 153.0.8010.52, 154.0.8037.57 (the stable patches), 155.0.8059.12 (beta) and # 156.0.8075.0, unzipped under ps0927/cft/<version>/ (sha256 in the provenance # page). These are the only Google-branded builds in the ladder. set -euo pipefail W=/workspace/artifacts/wiki export PLAYWRIGHT_BROWSERS_PATH=/workspace/.playwright cd "$W/sandbox" node ps_milestone_probe.mjs \ "headless shell, Playwright 1.62.1 default=/workspace/.playwright/chromium_headless_shell-1234/chrome-linux/headless_shell" \ "Chromium, Playwright 1.62.1=/workspace/.playwright/chromium-1234/chrome-linux/chrome" \ "headless shell, Playwright 1.63.0-alpha-2026-08-23=$W/ps0927/pwbrowsers/chromium_headless_shell-1241/chrome-linux/headless_shell" \ "Chromium, Debian bookworm-security=/usr/lib/chromium/chromium" \ "headless shell, Playwright 1.63.0-alpha-2026-08-29=$W/ps0927/pwbrowsers/chromium_headless_shell-1243/chrome-headless-shell-linux-arm64/chrome-headless-shell" \ "Chromium, Debian bookworm-security=$W/ps0927/deb/153.0.8010.52/usr/lib/chromium/chromium" \ "Google Chrome for Testing=$W/ps0927/cft/153.0.8010.52/chrome-linux-arm64/chrome" \ "headless shell, Playwright 1.64.0-alpha-1789764292000=$W/ps0927/pwbrowsers/chromium_headless_shell-1246/chrome-headless-shell-linux-arm64/chrome-headless-shell" \ "Chromium, Debian bookworm-security=$W/ps0927/deb/154.0.8037.57/usr/lib/chromium/chromium" \ "Google Chrome for Testing=$W/ps0927/cft/154.0.8037.57/chrome-linux-arm64/chrome" \ "Chromium, Playwright 1.64.0-alpha-2026-09-27=$W/ps0927/pwbrowsers/chromium-1247/chrome-linux-arm64/chrome" \ "headless shell, Playwright 1.64.0-alpha-2026-09-27=$W/ps0927/pwbrowsers/chromium_headless_shell-1247/chrome-headless-shell-linux-arm64/chrome-headless-shell" \ "Google Chrome for Testing=$W/ps0927/cft/155.0.8059.12/chrome-linux-arm64/chrome" \ "Google Chrome for Testing=$W/ps0927/cft/156.0.8075.0/chrome-linux-arm64/chrome"
- ps_milestone_probe.mjs
// Has Chrome actually removed the Privacy Sandbox APIs, milestone by milestone? // // Chrome Platform Status can say "Proposed" after a removal has shipped, and // release notes can say "planned for deprecation and removal" in the milestone // that removes it. Neither is a measurement. This asks the binaries. // // Run with one label=executable per argument. This host is aarch64. The builds // used on 2026-09-27 were Playwright's bundled headless shells and Chromium, // Debian bookworm-security's arm64 Chromium packages unpacked with `dpkg -x` // (the real binary is usr/lib/chromium/chromium, not the /usr/bin/chromium // wrapper), and Google Chrome for Testing's linux-arm64 builds (153 onwards): // PLAYWRIGHT_BROWSERS_PATH=/workspace/.playwright node sandbox/ps_milestone_probe.mjs \ // 'Chromium (Debian)=/usr/lib/chromium/chromium' \ // 'Chromium (Debian)=ps0927/deb/153.0.8010.52/usr/lib/chromium/chromium' ... // The exact invocation used for the published output is in run_ps_milestone_probe.sh. // // Debian's binary aborts at launch with "chrome_crashpad_handler: --database is // required" under Playwright; setting BREAKPAD_DUMP_LOCATION gives the handler // its database and the browser starts. That is the whole fix. // // The presence expressions and the calls are read out of ps_probe.mjs and // ps_call_probe.mjs at run time, not copied, so this cannot drift from the // probes the page already publishes. An automation launch has no variations // seed, so what it measures is the code in the binary, never a field trial. import https from 'node:https'; import { execSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { chromium } from 'playwright'; const here = path.dirname(fileURLToPath(import.meta.url)); const lift = (file, name) => { const src = fs.readFileSync(path.join(here, file), 'utf8'); const m = src.match(new RegExp('const ' + name + ' = `([\\s\\S]*?)`;')); if (!m) throw new Error(`could not find const ${name} in ${file}`); return m[1]; }; const PROBE = lift('ps_probe.mjs', 'PROBE'); const CALLS = lift('ps_call_probe.mjs', 'CALLS'); // The auction fixture of ps_cdp_probe.mjs (bidding script, decision script, // worklet), served from an enrolled-by-override public-looking origin. Its CDP // events disappear with the CDP surface, so here the evidence is server-side: // an auction that runs fetches /bid.js and /decide.js. const BID = lift('ps_cdp_probe.mjs', 'BID'); const DEC = lift('ps_cdp_probe.mjs', 'DEC'); const WORKLET = lift('ps_cdp_probe.mjs', 'WORKLET'); // Arguments are label=executable. Label the binary kind: Playwright's default // `headless: true` launches chrome-headless-shell, not Chromium, and the two // do not answer the ads APIs the same way (see the output). const builds = process.argv.slice(2).map((a) => { const i = a.indexOf('='); if (i < 1) throw new Error(`argument must be label=executable, got ${a}`); const [label, exe] = [a.slice(0, i), a.slice(i + 1)]; if (!fs.existsSync(exe)) throw new Error(`no executable at ${exe} (${label})`); return [label, exe]; }); if (builds.length === 0) throw new Error('usage: ps_milestone_probe.mjs label=<chromium-executable> ...'); // localhost over HTTPS, so the page is a secure context. Localhost is exempt // from enrollment, which is what we want here: the question is whether the // code is in the binary, not whether an origin is attested. const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'psms-')); execSync( `openssl req -x509 -newkey rsa:2048 -nodes -keyout ${tmp}/k.pem -out ${tmp}/c.pem -days 2 -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"`, { stdio: 'ignore' } ); // Every request the page causes is logged with the Privacy Sandbox request // headers, so a call that "returns" can be told apart from one that does work: // an auction that runs fetches its decision logic; an attribution-eligible // fetch carries Attribution-Reporting-Eligible. const HDRS = ['sec-browsing-topics', 'attribution-reporting-eligible', 'sec-shared-storage-writable', 'sec-fetch-dest']; let hits = []; const server = https.createServer( { key: fs.readFileSync(`${tmp}/k.pem`), cert: fs.readFileSync(`${tmp}/c.pem`) }, (req, res) => { hits.push(req.url + ' ' + HDRS.filter((h) => h in req.headers).map((h) => `${h}=${req.headers[h]}`).join(' ')); res.writeHead(200, { 'content-type': 'text/html' }); res.end('<!doctype html><meta charset=utf-8><title>ps</title>'); } ); await new Promise((r) => server.listen(8445, '127.0.0.1', r)); execSync( `openssl req -x509 -newkey rsa:2048 -nodes -keyout ${tmp}/k2.pem -out ${tmp}/c2.pem -days 2 -subj "/CN=probe.example" -addext "subjectAltName=DNS:probe.example"`, { stdio: 'ignore' } ); let auctionHits = []; const auctionServer = https.createServer( { key: fs.readFileSync(`${tmp}/k2.pem`), cert: fs.readFileSync(`${tmp}/c2.pem`) }, (req, res) => { auctionHits.push(req.url); const js = { 'content-type': 'application/javascript', 'ad-auction-allowed': 'true' }; if (req.url.startsWith('/bid.js')) return res.writeHead(200, js).end(BID); if (req.url.startsWith('/decide.js')) return res.writeHead(200, js).end(DEC); if (req.url.startsWith('/w.js')) return res.writeHead(200, js).end(WORKLET); if (req.url.startsWith('/ad.html')) return res.writeHead(200, { 'content-type': 'text/html' }).end('<b>ad</b>'); res.writeHead(200, { 'content-type': 'text/html' }).end('<!doctype html><meta charset=utf-8><title>ps</title>'); } ); await new Promise((r) => auctionServer.listen(8444, '127.0.0.1', r)); const AUCTION = `(async () => { const o = {}; const t = async (n, f) => { try { o[n] = 'ok ' + JSON.stringify(await f()); } catch (e) { o[n] = 'ERR ' + String(e.message).slice(0, 110); } }; await t('joinAdInterestGroup', () => navigator.joinAdInterestGroup({ owner: location.origin, name: 'probe', biddingLogicURL: location.origin + '/bid.js', ads: [{ renderURL: location.origin + '/ad.html', metadata: {} }], }, 3600)); await t('runAdAuction', () => navigator.runAdAuction({ seller: location.origin, decisionLogicURL: location.origin + '/decide.js', interestGroupBuyers: [location.origin], resolveToConfig: false, })); return o; })()`; // Two probes this script adds to the lifted ones. allowsFeature() is false both // for a policy feature the browser no longer knows and for one it knows but // does not grant, so print features() membership as well. And ps_call_probe.mjs // never provokes Sec-Browsing-Topics, so ask for it explicitly. const PS_FEATURES = ['browsing-topics', 'join-ad-interest-group', 'run-ad-auction', 'attribution-reporting', 'shared-storage', 'private-aggregation']; const EXTRA = `(async () => { const o = {}; const fp = document.featurePolicy; const known = fp && fp.features ? fp.features() : null; for (const f of ${JSON.stringify(PS_FEATURES)}) o['PP known:' + f] = known ? String(known.includes(f)) : 'no features()'; try { o["fetch(browsingTopics)"] = 'RETURNED ' + (await fetch('/t', { browsingTopics: true })).status; } catch (e) { o["fetch(browsingTopics)"] = (e && e.name ? e.name + ': ' : '') + String(e && e.message || e).slice(0, 150); } return o; })()`; const RE = /interest|shared|attribution|bounce|related|topic/i; const results = []; let port = 9340; for (const [label, exe] of builds) { port += 1; const b = await chromium.launch({ headless: true, executablePath: exe, args: [`--remote-debugging-port=${port}`], env: { ...process.env, BREAKPAD_DUMP_LOCATION: tmp }, }); const version = b.version(); hits = []; const ctx = await b.newContext({ ignoreHTTPSErrors: true }); const p = await ctx.newPage(); await p.goto('https://localhost:8445/', { waitUntil: 'domcontentloaded' }); const presence = await p.evaluate(PROBE); const calls = await p.evaluate(CALLS); const extra = await p.evaluate(EXTRA); for (const [k, v] of Object.entries(extra)) (k.startsWith('PP known:') ? presence : calls)[k] = v; await p.waitForTimeout(2000); const requests = hits.slice(); const proto = await (await fetch(`http://127.0.0.1:${port}/json/protocol`)).json(); const st = proto.domains.find((d) => d.domain === 'Storage'); const cdp = { 'CDP Storage commands': st.commands.map((c) => c.name).filter((n) => RE.test(n)), 'CDP Storage events': st.events.map((e) => e.name).filter((n) => RE.test(n)), }; await b.close(); // Auction phase: the flag set ps_cdp_probe.mjs used on 2026-09-01. const b2 = await chromium.launch({ headless: true, executablePath: exe, env: { ...process.env, BREAKPAD_DUMP_LOCATION: tmp }, args: [ '--host-resolver-rules=MAP probe.example 127.0.0.1', '--ignore-certificate-errors', '--enable-features=PrivacySandboxAdsAPIsOverride', '--privacy-sandbox-enrollment-overrides=https://probe.example:8444', ], }); auctionHits = []; const ctx2 = await b2.newContext({ ignoreHTTPSErrors: true }); const p2 = await ctx2.newPage(); await p2.goto('https://probe.example:8444/', { waitUntil: 'domcontentloaded' }); const auction = await p2.evaluate(AUCTION); await p2.waitForTimeout(2500); auction['server saw'] = auctionHits.filter((u) => u !== '/' && u !== '/favicon.ico').join(' ') || '(nothing but the page)'; await b2.close(); results.push({ label, version, presence, calls, cdp, requests, auction }); } server.close(); auctionServer.close(); const cell = (v) => String(v).replace(/\s+/g, ' ').slice(0, 16).padEnd(16); console.log('probe run:', new Date().toISOString().slice(0, 10)); results.forEach((r, i) => console.log(` [${i + 1}] ${r.version.padEnd(14)} ${r.label}`)); for (const [title, key] of [['Presence (ps_probe.mjs PROBE, plus "PP known:" = featurePolicy.features() membership)', 'presence'], ['Calls on https://localhost (ps_call_probe.mjs CALLS, plus fetch(browsingTopics))', 'calls']]) { const keys = Object.keys(results[0][key]).filter((k) => k !== 'chrome'); for (const r of results.slice(1)) for (const k of Object.keys(r[key])) if (k !== 'chrome' && !keys.includes(k)) keys.push(k); const w = Math.max(...keys.map((k) => k.length)); console.log(`\n== ${title} ==`); console.log('expression'.padEnd(w) + results.map((r, i) => ' ' + cell(`[${i + 1}] ${r.version.split('.')[0]}`)).join('')); console.log('-'.repeat(w) + results.map(() => ' ' + '-'.repeat(16)).join('')); for (const k of keys) console.log(k.padEnd(w) + results.map((r) => ' ' + cell(k in r[key] ? r[key][k] : '(key absent)')).join('')); } console.log('\n== Full call results (untruncated) =='); results.forEach((r, i) => { console.log(`-- [${i + 1}] ${r.version} ${r.label}`); for (const [k, v] of Object.entries(r.calls)) console.log(` ${k.padEnd(38)} ${v}`); }); console.log('\n== Every request the page caused (path + Privacy Sandbox request headers) =='); results.forEach((r, i) => { console.log(`-- [${i + 1}] ${r.version} ${r.label}`); for (const h of r.requests) console.log(` ${h}`); }); console.log('\n== Protected Audience auction with a served bidding and decision script (probe.example, enrollment override) =='); results.forEach((r, i) => { console.log(`-- [${i + 1}] ${r.version} ${r.label}`); for (const [k, v] of Object.entries(r.auction)) console.log(` ${k.padEnd(20)} ${v}`); }); console.log('\n== CDP Storage domain, Privacy Sandbox names (from each binary\'s /json/protocol) =='); results.forEach((r, i) => { console.log(`-- [${i + 1}] ${r.version} ${r.label}`); for (const [k, v] of Object.entries(r.cdp)) console.log(` ${k} (${v.length}): ${v.join(', ') || '(none)'}`); });
Its real, unedited output:
- ps_milestone_probe-output.txt
probe run: 2026-09-27 [1] 151.0.7922.34 headless shell, Playwright 1.62.1 default [2] 151.0.7922.34 Chromium, Playwright 1.62.1 [3] 152.0.7977.54 headless shell, Playwright 1.63.0-alpha-2026-08-23 [4] 152.0.7977.82 Chromium, Debian bookworm-security [5] 153.0.8010.12 headless shell, Playwright 1.63.0-alpha-2026-08-29 [6] 153.0.8010.52 Chromium, Debian bookworm-security [7] 153.0.8010.52 Google Chrome for Testing [8] 154.0.8037.0 headless shell, Playwright 1.64.0-alpha-1789764292000 [9] 154.0.8037.57 Chromium, Debian bookworm-security [10] 154.0.8037.57 Google Chrome for Testing [11] 155.0.8059.12 Chromium, Playwright 1.64.0-alpha-2026-09-27 [12] 155.0.8059.12 headless shell, Playwright 1.64.0-alpha-2026-09-27 [13] 155.0.8059.12 Google Chrome for Testing [14] 156.0.8075.0 Google Chrome for Testing == Presence (ps_probe.mjs PROBE, plus "PP known:" = featurePolicy.features() membership) == expression [1] 151 [2] 151 [3] 152 [4] 152 [5] 153 [6] 153 [7] 153 [8] 154 [9] 154 [10] 154 [11] 155 [12] 155 [13] 155 [14] 156 --------------------------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- document.browsingTopics function function function function function function function function function function function function function function navigator.joinAdInterestGroup function function function function function function function function function function function function function function navigator.runAdAuction function function function function function function function function function function function function function function navigator.leaveAdInterestGroup function function function function function function function function function function function function function function window.sharedStorage object object object object undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined sharedStorage.selectURL function function function function n/a n/a n/a n/a n/a n/a n/a n/a n/a n/a HTMLFencedFrameElement function function function function function function function function function function function function function function document.hasPrivateToken function function function function function function function function function function function function function function document.hasRedemptionRecord function function function function function function function function function function function function function function document.requestStorageAccess function function function function function function function function function function function function function function document.requestStorageAccessFor function function function function function function function undefined undefined undefined undefined undefined undefined undefined window.IdentityCredential (FedCM) function function function function function function function function function function function function function function navigator.userAgentData object object object object object object object object object object object object object object navigator.cookieDeprecationLabel undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined cookieStore object object object object object object object object object object object object object object PP:browsing-topics true true true true true true true true true true true true true true PP:join-ad-interest-group true true true true false false false false false false false false false false PP:run-ad-auction true true true true false false false false false false false false false false PP:attribution-reporting true true false false false false false false false false false false false false PP:shared-storage true true true true false false false false false false false false false false PP:private-aggregation true true true true false false false false false false false false false false HTMLImageElement.attributionSrc yes yes yes yes yes yes yes yes yes yes yes yes yes yes HTMLAnchorElement.attributionSrc yes yes yes yes yes yes yes yes yes yes yes yes yes yes PP known:browsing-topics true true true true true true true true true true true true true true PP known:join-ad-interest-group true true true true false false false false false false false false false false PP known:run-ad-auction true true true true false false false false false false false false false false PP known:attribution-reporting true true false false false false false false false false false false false false PP known:shared-storage true true true true false false false false false false false false false false PP known:private-aggregation true true true true false false false false false false false false false false == Calls on https://localhost (ps_call_probe.mjs CALLS, plus fetch(browsingTopics)) == expression [1] 151 [2] 151 [3] 152 [4] 152 [5] 153 [6] 153 [7] 153 [8] 154 [9] 154 [10] 154 [11] 155 [12] 155 [13] 155 [14] 156 --------------------------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- ---------------- document.browsingTopics() RETURNED [] RETURNED [] NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro NotSupportedErro navigator.joinAdInterestGroup() RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin navigator.runAdAuction() RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null RETURNED null sharedStorage.set() RETURNED undefin OperationError: OperationError: OperationError: TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno sharedStorage.worklet.addModule() OperationError: OperationError: OperationError: OperationError: TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno TypeError: Canno document.hasPrivateToken() RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false RETURNED false document.requestStorageAccess() RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin RETURNED undefin fetch(attributionReporting) RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 fetch(browsingTopics) RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 RETURNED 200 == Full call results (untruncated) == -- [1] 151.0.7922.34 headless shell, Playwright 1.62.1 default document.browsingTopics() RETURNED [] navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() RETURNED undefined sharedStorage.worklet.addModule() OperationError: Rejecting load of https://localhost:8445/w.js due to unexpected MIME type. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [2] 151.0.7922.34 Chromium, Playwright 1.62.1 document.browsingTopics() RETURNED [] navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() OperationError: sharedStorage is disabled sharedStorage.worklet.addModule() OperationError: sharedStorage.worklet.addModule is disabled because either sharedStorage is disabled or both sharedStorage.selectURL and privateAggregation are disabl document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [3] 152.0.7977.54 headless shell, Playwright 1.63.0-alpha-2026-08-23 document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() OperationError: Failed to execute 'set' on 'SharedStorage': Shared Storage is disabled. sharedStorage.worklet.addModule() OperationError: Failed to execute 'addModule' on 'SharedStorageWorklet': Shared Storage is disabled. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [4] 152.0.7977.82 Chromium, Debian bookworm-security document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() OperationError: Failed to execute 'set' on 'SharedStorage': Shared Storage is disabled. sharedStorage.worklet.addModule() OperationError: Failed to execute 'addModule' on 'SharedStorageWorklet': Shared Storage is disabled. document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [5] 153.0.8010.12 headless shell, Playwright 1.63.0-alpha-2026-08-29 document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [6] 153.0.8010.52 Chromium, Debian bookworm-security document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [7] 153.0.8010.52 Google Chrome for Testing document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [8] 154.0.8037.0 headless shell, Playwright 1.64.0-alpha-1789764292000 document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [9] 154.0.8037.57 Chromium, Debian bookworm-security document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [10] 154.0.8037.57 Google Chrome for Testing document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [11] 155.0.8059.12 Chromium, Playwright 1.64.0-alpha-2026-09-27 document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [12] 155.0.8059.12 headless shell, Playwright 1.64.0-alpha-2026-09-27 document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [13] 155.0.8059.12 Google Chrome for Testing document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 -- [14] 156.0.8075.0 Google Chrome for Testing document.browsingTopics() NotSupportedError: Failed to execute 'browsingTopics' on 'Document': document.browsingTopics() is deprecated and has been removed. navigator.joinAdInterestGroup() RETURNED undefined navigator.runAdAuction() RETURNED null sharedStorage.set() TypeError: Cannot read properties of undefined (reading 'set') sharedStorage.worklet.addModule() TypeError: Cannot read properties of undefined (reading 'worklet') document.hasPrivateToken() RETURNED false document.requestStorageAccess() RETURNED undefined fetch(attributionReporting) RETURNED 200 fetch(browsingTopics) RETURNED 200 == Every request the page caused (path + Privacy Sandbox request headers) == -- [1] 151.0.7922.34 headless shell, Playwright 1.62.1 default / sec-fetch-dest=document /w.js sec-fetch-dest=sharedstorageworklet /x attribution-reporting-eligible=event-source;navigation-source sec-fetch-dest=empty /t sec-browsing-topics=();p=P0000000000000000000000000000000 sec-fetch-dest=empty -- [2] 151.0.7922.34 Chromium, Playwright 1.62.1 / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x attribution-reporting-eligible=event-source=navigation-source sec-fetch-dest=empty /t sec-browsing-topics=();p=P0000000000000000000000000000000 sec-fetch-dest=empty -- [3] 152.0.7977.54 headless shell, Playwright 1.63.0-alpha-2026-08-23 / sec-fetch-dest=document /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [4] 152.0.7977.82 Chromium, Debian bookworm-security / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [5] 153.0.8010.12 headless shell, Playwright 1.63.0-alpha-2026-08-29 / sec-fetch-dest=document /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [6] 153.0.8010.52 Chromium, Debian bookworm-security / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [7] 153.0.8010.52 Google Chrome for Testing / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [8] 154.0.8037.0 headless shell, Playwright 1.64.0-alpha-1789764292000 / sec-fetch-dest=document /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [9] 154.0.8037.57 Chromium, Debian bookworm-security / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [10] 154.0.8037.57 Google Chrome for Testing / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [11] 155.0.8059.12 Chromium, Playwright 1.64.0-alpha-2026-09-27 / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [12] 155.0.8059.12 headless shell, Playwright 1.64.0-alpha-2026-09-27 / sec-fetch-dest=document /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [13] 155.0.8059.12 Google Chrome for Testing / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty -- [14] 156.0.8075.0 Google Chrome for Testing / sec-fetch-dest=document /favicon.ico sec-fetch-dest=image /x sec-fetch-dest=empty /t sec-fetch-dest=empty == Protected Audience auction with a served bidding and decision script (probe.example, enrollment override) == -- [1] 151.0.7922.34 headless shell, Playwright 1.62.1 default joinAdInterestGroup ok undefined runAdAuction ok "urn:uuid:3012660e-f15c-4b71-a739-523ece3b26a4" server saw /bid.js /decide.js /bid.js -- [2] 151.0.7922.34 Chromium, Playwright 1.62.1 joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [3] 152.0.7977.54 headless shell, Playwright 1.63.0-alpha-2026-08-23 joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [4] 152.0.7977.82 Chromium, Debian bookworm-security joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [5] 153.0.8010.12 headless shell, Playwright 1.63.0-alpha-2026-08-29 joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [6] 153.0.8010.52 Chromium, Debian bookworm-security joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [7] 153.0.8010.52 Google Chrome for Testing joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [8] 154.0.8037.0 headless shell, Playwright 1.64.0-alpha-1789764292000 joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [9] 154.0.8037.57 Chromium, Debian bookworm-security joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [10] 154.0.8037.57 Google Chrome for Testing joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [11] 155.0.8059.12 Chromium, Playwright 1.64.0-alpha-2026-09-27 joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [12] 155.0.8059.12 headless shell, Playwright 1.64.0-alpha-2026-09-27 joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [13] 155.0.8059.12 Google Chrome for Testing joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) -- [14] 156.0.8075.0 Google Chrome for Testing joinAdInterestGroup ok undefined runAdAuction ok null server saw (nothing but the page) == CDP Storage domain, Privacy Sandbox names (from each binary's /json/protocol) == -- [1] 151.0.7922.34 headless shell, Playwright 1.62.1 default CDP Storage commands (12): getInterestGroupDetails, setInterestGroupTracking, setInterestGroupAuctionTracking, getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (5): interestGroupAccessed, interestGroupAuctionEventOccurred, interestGroupAuctionNetworkRequestCreated, sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [2] 151.0.7922.34 Chromium, Playwright 1.62.1 CDP Storage commands (12): getInterestGroupDetails, setInterestGroupTracking, setInterestGroupAuctionTracking, getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (5): interestGroupAccessed, interestGroupAuctionEventOccurred, interestGroupAuctionNetworkRequestCreated, sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [3] 152.0.7977.54 headless shell, Playwright 1.63.0-alpha-2026-08-23 CDP Storage commands (9): getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (2): sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [4] 152.0.7977.82 Chromium, Debian bookworm-security CDP Storage commands (9): getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (2): sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [5] 153.0.8010.12 headless shell, Playwright 1.63.0-alpha-2026-08-29 CDP Storage commands (9): getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (2): sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [6] 153.0.8010.52 Chromium, Debian bookworm-security CDP Storage commands (9): getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (2): sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [7] 153.0.8010.52 Google Chrome for Testing CDP Storage commands (9): getSharedStorageMetadata, getSharedStorageEntries, setSharedStorageEntry, deleteSharedStorageEntry, clearSharedStorageEntries, resetSharedStorageBudget, setSharedStorageTracking, runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (2): sharedStorageAccessed, sharedStorageWorkletOperationExecutionFinished -- [8] 154.0.8037.0 headless shell, Playwright 1.64.0-alpha-1789764292000 CDP Storage commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (0): (none) -- [9] 154.0.8037.57 Chromium, Debian bookworm-security CDP Storage commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (0): (none) -- [10] 154.0.8037.57 Google Chrome for Testing CDP Storage commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (0): (none) -- [11] 155.0.8059.12 Chromium, Playwright 1.64.0-alpha-2026-09-27 CDP Storage commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (0): (none) -- [12] 155.0.8059.12 headless shell, Playwright 1.64.0-alpha-2026-09-27 CDP Storage commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (0): (none) -- [13] 155.0.8059.12 Google Chrome for Testing CDP Storage commands (2): runBounceTrackingMitigations, getRelatedWebsiteSets CDP Storage events (0): (none) -- [14] 156.0.8075.0 Google Chrome for Testing CDP Storage commands (1): runBounceTrackingMitigations CDP Storage events (0): (none)
External checks, 2026-09-27
scripts/external_checks_privacy_sandbox.mjs is new. The 2026-09-01 run verified its sources by hand and left no re-runnable script, which is why this refresh had to rediscover the 22 feature ids from the search API. It exits non-zero on any FAILED line: a counter and an exit gate, not a grep. Mutation test: MUTATE=1 corrupts one check in each of sections 2–6. It also shifts one stated expectation, the Shared Storage thread's newest date. It printed 10 FAILED lines and exited 1: 1 chromestatus name, 1 release-notes phrase, 2 blink-dev phrases, 1 thread-date expectation, 1 retirement-blog phrase and 4 GitHub archived assertions. The clean run exits 0. Every value the content page states — the four schedule dates it prints, removal-stage milestones, zero Topics removal entries, the release-notes headings for 152–154, each thread's newest date, the linux-arm64 availability per milestone, the tip-of-tree CDP commands — is in the script's EXPECT and fails the run when it changes. The first version only printed several of them next to “OK”, and the generic reviewer caught that. Lines that only report are marked INFO.
- external_checks_privacy_sandbox.mjs
// Re-fetches every non-corpus status fact privacy:privacy_sandbox leans on and // prints what the primary source says today. Written 2026-09-27, when the page's // "removal milestone M153" statement had gone stale (chromestatus still said // Proposed while stable was M154). // // node scripts/external_checks_privacy_sandbox.mjs > scripts/external_checks_privacy_sandbox-output.txt 2>&1 // // Every check prints OK or FAILED. FAILED increments a counter and the process // exits 1 if the counter is non-zero — the gate is at the bottom, not a grep. // Where the page states a value (a removal milestone, "no Topics entry", "no // thread update after July", "M154 lists no removals"), the value is in EXPECT // below and a change FAILS the run: it means the page needs re-reading, not // that the fetch broke. Lines that only report are marked INFO, not OK. // MUTATE=1 corrupts one check in each of sections 2-6 and one EXPECT value, to // prove those sections can fail; its output is kept beside this file. Section 1 // asserts the four schedule dates the page prints and reports the rest as INFO. // // Google's devsite pages localise by vantage, not by header alone: without // accept-language this run got Hebrew release notes and a Spanish status page. // Every Google URL below also carries ?hl=en. // // Chrome Platform Status: always the per-feature endpoint, and every stage is // printed. The summary `browsers.chrome.status` collapses a lifecycle into one // label and was still "Proposed" for removals Chrome's own release notes list. import fs from 'node:fs'; const UA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0 Safari/537.36'; const MUTATE = process.env.MUTATE === '1'; const OUT = 'out/ps/ext'; fs.mkdirSync(OUT, { recursive: true }); let FAILS = 0; const say = (s = '') => console.log(s); const fail = (s) => { say(`FAILED: ${s}`); FAILS += 1; }; const mut = (s) => (MUTATE ? s + ' MUTATED' : s); const collapse = (t) => t.replace(/<[^>]+>/g, ' ') .replace(/ /g, ' ').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>') .replace(/[’‘]/g, "'").replace(/[“”]/g, '"') .replace(/\s+/g, ' ') // tags around link text leave a space before closing punctuation .replace(/\( /g, '(').replace(/ ([),.;:])/g, '$1'); async function get(url, { json = false, xssi = false } = {}) { let last; for (let i = 0; i < 3; i++) { const r = await fetch(url, { headers: { 'user-agent': UA, 'accept-language': 'en-US,en;q=0.9', accept: json ? 'application/json' : 'text/html,*/*' }, redirect: 'follow' }); const body = await r.text(); last = { status: r.status, body }; if (r.status === 200 || r.status === 404) break; await new Promise((res) => setTimeout(res, 2000)); } if (json && last.status === 200) last.data = JSON.parse(xssi ? last.body.slice(last.body.indexOf('\n') + 1) : last.body); return last; } async function phrase(name, url, needles) { const r = await get(url); fs.writeFileSync(`${OUT}/${name.replace(/[^A-Za-z0-9]+/g, '_')}.html`, r.body); const t = collapse(r.body); for (const n of needles) { if (r.status === 200 && t.includes(n)) say(`OK [${r.status}] ${name} — "${n}"`); else fail(`[${r.status}] ${name} — phrase not found: "${n}" (${url})`); } return t; } // --------------------------------------------------------------------------- say('== 1. Chrome release channel and milestone schedule (chromiumdash) =='); const today = new Date().toISOString().slice(0, 10); say(`run date ${today}`); const rel = await get('https://chromiumdash.appspot.com/fetch_releases?channel=Stable&platform=Linux&num=1', { json: true }); if (rel.status !== 200) fail(`chromiumdash fetch_releases HTTP ${rel.status}`); const stable = rel.data[0]; say(`INFO Chrome stable (Linux): milestone ${stable.milestone}, ${stable.version}, this patch released ${new Date(stable.time).toISOString().slice(0, 10)}`); // The schedule dates the page prints. Kept here rather than in EXPECT because // section 1 runs before EXPECT is defined. const SCHEDULE_EXPECT = { 152: { stable_date: '2026-08-25' }, 153: { stable_date: '2026-09-08' }, 154: { stable_date: '2026-09-22' }, 156: { branch_point: '2026-09-28' } }; const sched = {}; for (let m = 150; m <= 156; m++) { const r = await get(`https://chromiumdash.appspot.com/fetch_milestone_schedule?mstone=${m}`, { json: true }); if (r.status !== 200) { fail(`chromiumdash schedule M${m} HTTP ${r.status}`); continue; } const s = r.data.mstones[0]; if (s.mstone !== m) fail(`chromiumdash schedule asked for M${m}, got ${s.mstone}`); sched[m] = s; const line = `M${m}: branch_point ${s.branch_point.slice(0, 10)}, earliest_beta ${s.earliest_beta.slice(0, 10)}, stable_date ${s.stable_date.slice(0, 10)}`; const want = SCHEDULE_EXPECT[m]; if (!want) { say(`INFO ${line}`); continue; } const bad = Object.entries(want).filter(([k, v]) => s[k].slice(0, 10) !== v); if (bad.length) fail(`${line} — page says ${bad.map(([k, v]) => `${k} ${v}`).join(', ')}`); else say(`OK ${line}`); } // --------------------------------------------------------------------------- say(''); say('== 2. Chrome Platform Status, per-feature endpoint, every stage =='); const STAGE = { 110: 'incubate', 120: 'prototype', 130: 'dev trial', 140: 'eval readiness', 150: 'origin trial', 151: 'extend OT', 160: 'ship', 220: 'prototype', 230: 'dev trial', 250: 'origin trial', 251: 'extend OT', 260: 'ship', 320: 'implement', 330: 'dev trial', 360: 'ship', 410: 'dep: plan', 430: 'dep: dev trial', 450: 'dep: deprecation trial', 451: 'dep: extend DT', 460: 'dep: ship (removal)', 470: 'dep: remove code', 1061: 'enterprise rollout', 1070: 'enterprise shipped', }; // [row on the page, id, name must match] const FEATURES = [ ['Topics (ship)', '5680923054964736', /^Topics API$/], ['Protected Audience (ship)', '5733583115255808', /Protected audience API/i], ['Protected Audience (removal)', '6552486106234880', /Deprecate and Remove Protected Audience$/], ['Attribution Reporting (ship)', '6412002824028160', /^Attribution Reporting API$/], ['Attribution Reporting (removal)', '6320639375966208', /Deprecate and remove: Attribution Reporting API/], ['Shared Storage (ship)', '6256348582903808', /^Shared Storage API$/], ['Shared Storage (removal)', '5076349064708096', /Deprecate and Remove: Shared Storage API/], ['Private Aggregation (ship)', '5743412790689792', /^Private Aggregation API$/], ['Private Aggregation (removal)', '4683382919397376', /Deprecate and remove: Private Aggregation API/], ['Fenced Frames (ship)', '5699388062040064', /^Fenced frames$/], ['Fenced Frames (removal)', '6366274495053824', /Remove FencedFrame element/], ['Related Website Sets (ship)', '5640066519007232', /^Related Website Sets$/], ['Related Website Sets (removal)', '5194473869017088', /Deprecate and Remove: Related Website Sets/], ['requestStorageAccessFor (ship)', '5122534152863744', /^requestStorageAccessFor$/], ['requestStorageAccessFor (removal)', '5162221567082496', /Deprecate and Remove: document.requestStorageAccessFor/], ['CHIPS (ship)', '5179189105786880', /CHIPS/], ['CHIPS (WebView entry)', '5279664766713856', /Deprecate CHIPS and Relaunch in WebView/], ['Storage Access API (ship)', '5612590694662144', /^Storage Access API$/], ['Private State Tokens (ship)', '5078049450098688', /^Private State Token API$/], ['FedCM (ship)', '6438627087220736', /Federated Credential/], ['Bounce tracking mitigations (ship)', '5705149616488448', /^Bounce Tracking Mitigations$/], ['UA Client Hints (ship)', '5995832180473856', /Sec-CH-UA Client Hints/], ]; // Values the content page states as of 2026-09-27. const EXPECT = { removalDesktop: { '6552486106234880': 153, '6320639375966208': 153, '5076349064708096': 153, '4683382919397376': 152, '6366274495053824': 156, '5194473869017088': 153, '5162221567082496': 153 }, topicsRemovalEntries: 0, releaseNoteHeads: { 152: ['Remove Private Aggregation API'], 153: ['Deprecate and remove Protected Audience', 'Deprecate and remove Related Website Sets (RWS)', 'Deprecate and remove Shared Storage API', 'Deprecate and remove document.requestStorageAccessFor', 'Deprecate and remove Attribution Reporting API'], 154: null }, threadNewest: { Attribution_Reporting: '2026-07-01', Shared_Storage: '2026-07-07', Private_Aggregation: '2026-07-08', Protected_Audience: '2026-09-24', Topics: '2026-09-24' }, cftArm64: { 151: false, 152: false, 153: true, 154: true, 155: true, 156: true }, totStorage: ['runBounceTrackingMitigations'], }; if (MUTATE) EXPECT.threadNewest.Shared_Storage = '2026-07-06'; const cs = {}; for (const [row, id, re] of FEATURES) { const r = await get(`https://chromestatus.com/api/v0/features/${id}`, { json: true, xssi: true }); if (r.status !== 200) { fail(`chromestatus ${id} (${row}) HTTP ${r.status}`); continue; } const d = r.data; cs[id] = d; const name = MUTATE && id === '6552486106234880' ? d.name + ' MUTATED' : d.name; if (!re.test(name)) { fail(`chromestatus ${id} (${row}) name is "${name}", expected ${re}`); continue; } const c = d.browsers.chrome; say(`OK ${id} ${row} — "${d.name}" [${d.feature_type}] summary: ${c.status.text} (${c.status.milestone_str}), desktop ${c.desktop}, android ${c.android}, webview ${c.webview}; updated ${d.updated.when.slice(0, 10)}`); if (id in EXPECT.removalDesktop) { const got = d.stages.filter((st) => st.stage_type === 460).map((st) => st.desktop_first); if (c.status.text !== 'Proposed' || got.length !== 1 || got[0] !== EXPECT.removalDesktop[id]) fail(`${id} ${row}: page says Proposed, removal stage desktop ${EXPECT.removalDesktop[id]}; now ${c.status.text}, stage 460 desktop ${got.join('/')}`); } for (const s of d.stages) { const ms = [s.desktop_first && `desktop ${s.desktop_first}${s.desktop_last ? '–' + s.desktop_last : ''}`, s.android_first && `android ${s.android_first}`].filter(Boolean).join(', '); say(` stage ${s.stage_type} ${(STAGE[s.stage_type] || '?').padEnd(24)} ${ms || '(no milestone)'}${s.intent_thread_url ? ' intent: ' + s.intent_thread_url : ''}`); } } // The page says Topics has no dedicated removal entry. That is a negative // claim, so print the candidate set it rests on: every Feature-removal entry // the search returns for these queries. for (const q of ['Topics', 'browsingTopics', 'Deprecate and remove']) { const r = await get(`https://chromestatus.com/api/v0/features?q=${encodeURIComponent(q)}&num=300`, { json: true, xssi: true }); if (r.status !== 200) { fail(`chromestatus search "${q}" HTTP ${r.status}`); continue; } const rm = r.data.features.filter((f) => f.feature_type === 'Feature removal'); const topics = rm.filter((f) => /topic/i.test(f.name)); const line = `search "${q}": ${r.data.total_count} results, ${rm.length} Feature removal, of which name mentions Topics: ${topics.length}${topics.length ? ' — ' + topics.map((f) => f.id + ' ' + f.name).join('; ') : ''}`; if (topics.length !== EXPECT.topicsRemovalEntries) fail(`${line} (page says there is no Topics removal entry)`); else say(`OK ${line}`); } // --------------------------------------------------------------------------- say(''); say('== 3. Chrome release notes, "Deprecations and removals" (developer.chrome.com) =='); for (let m = 150; m <= 156; m++) { const url = `https://developer.chrome.com/release-notes/${m}?hl=en`; const r = await get(url); const released = sched[m] && sched[m].stable_date.slice(0, 10) <= today; if (r.status === 404) { if (released) fail(`[404] ${url} although M${m} stable_date has passed`); else say(`OK [404] ${url} — M${m} not yet stable (stable_date ${sched[m] ? sched[m].stable_date.slice(0, 10) : '?'})`); continue; } if (r.status !== 200) { fail(`[${r.status}] ${url}`); continue; } const i = r.body.indexOf('id="deprecations_and_removals"'); const upd0 = (collapse(r.body).match(/Last updated (\d{4}-\d\d-\d\d) UTC/) || [])[1]; // A release with nothing removed may have no section at all (M154 did not); // that is a finding about the notes, not a failed fetch. if (i < 0) { if (m in EXPECT.releaseNoteHeads && EXPECT.releaseNoteHeads[m] !== null) fail(`M${m}: no "Deprecations and removals" section, page expects ${EXPECT.releaseNoteHeads[m].join(' | ')}`); else say(`OK [200] M${m} (page last updated ${upd0}): NO "Deprecations and removals" section on the page`); continue; } if (EXPECT.releaseNoteHeads[m] === null) { fail(`M${m}: page says the notes list no removals, but a "Deprecations and removals" section now exists`); continue; } const end = r.body.indexOf('Contribute', i); const seg = r.body.slice(i, end > 0 ? end : undefined); const heads = [...seg.matchAll(/<h3[^>]*>([\s\S]*?)<\/h3>/g)].map((h) => collapse(h[1]).trim()); const upd = (collapse(r.body).match(/Last updated (\d{4}-\d\d-\d\d) UTC/) || [])[1]; const missing = (EXPECT.releaseNoteHeads[m] || []).filter((h) => !heads.includes(h)); if (missing.length) fail(`M${m}: expected heading(s) missing: ${missing.join(' | ')}`); say(`${missing.length ? 'INFO' : 'OK '} [200] M${m} (page last updated ${upd}): ${heads.length ? heads.join(' | ') : collapse(seg.replace(/^[^>]*>/, '')).slice(0, 160).trim()}`); } // The wording of the M152 and M153 entries is the finding: M152 says Private // Aggregation "is deprecated and removed"; M153 says the others are "planned". await phrase('release notes 152', 'https://developer.chrome.com/release-notes/152?hl=en', [ 'the Private Aggregation API is deprecated and removed (along with related Privacy Sandbox APIs)', ]); await phrase('release notes 153', 'https://developer.chrome.com/release-notes/153?hl=en', [ mut('the Protected Audience API is planned for deprecation and removal (along with related Privacy Sandbox APIs)'), 'the Shared Storage API is planned for deprecation and removal', 'the Attribution Reporting API is planned for deprecation and removal', 'Related Website Sets is planned for deprecation and removal', 'it is planned for deprecation and removal along with Related Website Sets', ]); await phrase('release notes 150', 'https://developer.chrome.com/release-notes/150?hl=en', ['There are no deprecations or removals in this release.']); // --------------------------------------------------------------------------- say(''); say('== 4. blink-dev intent threads: the phased plan and its September status =='); await phrase('blink-dev Protected Audience k_nubsMb97g', 'https://groups.google.com/a/chromium.org/g/blink-dev/c/k_nubsMb97g', [ 'Planning to deprecate in M144 and remove in M150', 'M152: Permanently remove the underlying implementation code for the API, and replace it with a lightweight stub API. The stub will remain removable via field trial.', 'M152 Canary/Beta: Begin a controlled rollout of the stub API removal via a field trial.', 'When I test on Chrome 152 stable, both navigator.joinAdInterestGroup and navigator.runAdAuction exist.', mut('We have seen no regressions on 1% of stable (M152+)'), 'We are actually still seeing ~ 1% usage.', '10% Stable: Hold for 2 weeks (through Oct 12)', '100% Stable: Roll out starting Oct 26', 'It wasn\'t actually an increase in API usage, it was just a change in measurement.', ]); await phrase('blink-dev Topics _R85yctz4Rs', 'https://groups.google.com/a/chromium.org/g/blink-dev/c/_R85yctz4Rs', [ 'Deprecate in M144 and then remove in M150', 'Currently the usage is 4.9% of page loads.', 'replace it with a lightweight stub API', mut('We have reached 1% on Stable (M153+) with no regressions or known breakage.'), 'The current usage is at 2%', '10% Stable: Hold for 2 weeks (through Oct 15)', '100% Stable: Target launch after Oct 15', ]); await phrase('blink-dev Attribution Reporting 4K2RRt6VYCQ', 'https://groups.google.com/a/chromium.org/g/blink-dev/c/4K2RRt6VYCQ', [ 'Currently the usage is 19.7% of page loads.', 'M152: Permanently remove the underlying implementation code for the API, and replace it with a lightweight stub API.', ]); await phrase('blink-dev Shared Storage uh5Ke6qyegc', 'https://groups.google.com/a/chromium.org/g/blink-dev/c/uh5Ke6qyegc', [ 'Currently, ~11% of page loads use Shared Storage API.', 'M152: Permanently remove the underlying implementation code for the API, and replace it with a lightweight stub API.', ]); await phrase('blink-dev Private Aggregation Ld7avyD0U0Q', 'https://groups.google.com/a/chromium.org/g/blink-dev/c/Ld7avyD0U0Q', [ 'Currently the usage is still 2.4% of page loads.', 'M152: Permanently remove the underlying implementation code for the API. There is no need to replace it with a stub API', ]); // Which threads have a rollout update after the July LGTMs? Print each // thread's newest dated message so "no September update" is visible, not assumed. const MON = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 }; for (const f of fs.readdirSync(OUT).filter((n) => n.startsWith('blink_dev_'))) { const t = collapse(fs.readFileSync(`${OUT}/${f}`, 'utf8')); const ds = [...t.matchAll(/\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{1,2}), (20\d\d), \d{1,2}:\d\d:\d\d\s?[AP]M/g)] .map((m) => new Date(Date.UTC(+m[3], MON[m[1]], +m[2])).toISOString().slice(0, 10)).sort(); if (!ds.length) { fail(`${f}: no dated messages parsed`); continue; } const key = Object.keys(EXPECT.threadNewest).find((k) => f.includes(k)); const newest = ds[ds.length - 1]; // "Newest" is the newest date stamp rendered in the server-side HTML of the // thread page; it is not checked against Google Groups' own message count. const line = `${f.replace(/\.html$/, '')}: ${ds.length} date stamps rendered, ${new Set(ds).size} distinct dates, first ${ds[0]}, newest ${newest}`; if (!key) fail(`${line} — no expectation for this thread`); else if (newest !== EXPECT.threadNewest[key]) fail(`${line} — page says newest ${EXPECT.threadNewest[key]}`); else say(`OK ${line}`); } // --------------------------------------------------------------------------- say(''); say('== 5. Google\'s own Privacy Sandbox status and retirement pages =='); const st = await get('https://privacysandbox.google.com/overview/status?hl=en'); if (st.status !== 200) fail(`status page HTTP ${st.status}`); else { const upd = (collapse(st.body).match(/Last updated:? ?(\w+ \d{1,2}, \d{4})/) || [])[1]; say(`OK [200] privacysandbox.google.com/overview/status — "Last updated: ${upd}"`); const rows = [...st.body.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/g)].map((m) => [...m[1].matchAll(/<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/g)].map((c) => collapse(c[1]).trim())); if (rows.length < 5) fail(`status page: ${rows.length} table rows, expected the per-platform table`); for (const r of rows.slice(1)) say(` ${r.join(' | ')}`); } await phrase('retirement blog 2025-10-17', 'https://privacysandbox.google.com/blog/update-on-plans-for-privacy-sandbox-technologies?hl=en', [ mut('Related Website Sets (including requestStorageAccessFor and Related Website Partition), SelectURL, SDK Runtime and Topics (Chrome and Android)'), ]); await phrase('enrollment page', 'https://privacysandbox.google.com/private-advertising/enrollment?hl=en', [ 'We will no longer accept new account creation and enrollment', ]); await phrase('CMA case page', 'https://www.gov.uk/cma-cases/investigation-into-googles-privacy-sandbox-browser-changes', [ 'Case state: Closed', 'Closed: 17 October 2025', ]); // --------------------------------------------------------------------------- say(''); say('== 6. Artefacts the page names (GitHub archived / pushed_at; tip-of-tree CDP) =='); const auth = process.env.GH_TOKEN ? { authorization: `Bearer ${process.env.GH_TOKEN}` } : {}; for (const repo of ['GoogleChrome/related-website-sets', 'GoogleChrome/ip-protection', 'privacysandbox/attestation', 'patcg-individual-drafts/topics']) { const r = await fetch(`https://api.github.com/repos/${repo}`, { headers: { 'user-agent': UA, ...auth } }); if (r.status !== 200) { fail(`github ${repo} HTTP ${r.status}`); continue; } const d = await r.json(); const ok = d.archived === !MUTATE; (ok ? say : fail)(`${ok ? 'OK ' : ''}github.com/${repo} archived=${d.archived} pushed_at=${d.pushed_at.slice(0, 10)}`); } const tot = await get('https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/json/browser_protocol.json', { json: true }); if (tot.status !== 200) fail(`devtools-protocol master HTTP ${tot.status}`); else { const sd = tot.data.domains.find((d) => d.domain === 'Storage'); const RE = /interest|shared|attribution|bounce|related|topic/i; const cmds = sd.commands.map((c) => c.name).filter((n) => RE.test(n)); const evs = sd.events.map((e) => e.name).filter((n) => RE.test(n)); const line = `devtools-protocol master Storage domain: commands ${cmds.join(', ') || '(none)'}; events ${evs.join(', ') || '(none)'}`; if (cmds.join() !== EXPECT.totStorage.join() || evs.length) fail(`${line} — page says only ${EXPECT.totStorage.join(', ')}`); else say(`OK ${line}`); } // Chrome for Testing: which milestones have a linux-arm64 build. This host is // aarch64; the first pass of this refresh looked only for 'linux64', concluded // no Google-built binary could run here, and a reviewer caught it. const cft = await get('https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone-with-downloads.json', { json: true }); if (cft.status !== 200) fail(`chrome-for-testing JSON HTTP ${cft.status}`); else { for (let m = 151; m <= 156; m++) { const e = cft.data.milestones[String(m)]; const arm = !!e && e.downloads.chrome.some((d) => d.platform === 'linux-arm64'); const line = `Chrome for Testing M${m}: ${e ? e.version : 'no entry'}, linux-arm64 ${arm ? 'yes' : 'no'}`; if (arm !== EXPECT.cftArm64[m]) fail(`${line} — page expects linux-arm64 ${EXPECT.cftArm64[m] ? 'yes' : 'no'}`); else say(`OK ${line}`); } } const npmPw = await get('https://registry.npmjs.org/playwright', { json: true }); if (npmPw.status !== 200) fail(`npm playwright HTTP ${npmPw.status}`); else say(`INFO npm playwright dist-tags: latest ${npmPw.data['dist-tags'].latest} (${npmPw.data.time[npmPw.data['dist-tags'].latest].slice(0, 10)}), next ${npmPw.data['dist-tags'].next}`); say(''); say('== summary =='); say(`FAILED checks: ${FAILS}`); process.exit(FAILS > 0 ? 1 : 0);
Its real, unedited output:
- external_checks_privacy_sandbox-output.txt
== 1. Chrome release channel and milestone schedule (chromiumdash) == run date 2026-09-27 INFO Chrome stable (Linux): milestone 154, 154.0.8037.57, this patch released 2026-09-22 INFO M150: branch_point 2026-06-01, earliest_beta 2026-06-03, stable_date 2026-06-30 INFO M151: branch_point 2026-06-29, earliest_beta 2026-07-01, stable_date 2026-07-28 OK M152: branch_point 2026-07-27, earliest_beta 2026-07-29, stable_date 2026-08-25 OK M153: branch_point 2026-08-17, earliest_beta 2026-08-19, stable_date 2026-09-08 OK M154: branch_point 2026-08-31, earliest_beta 2026-09-02, stable_date 2026-09-22 INFO M155: branch_point 2026-09-14, earliest_beta 2026-09-16, stable_date 2026-10-06 OK M156: branch_point 2026-09-28, earliest_beta 2026-09-30, stable_date 2026-10-20 == 2. Chrome Platform Status, per-feature endpoint, every stage == OK 5680923054964736 Topics (ship) — "Topics API" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2023-08-17 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/b845db21-f158-4bf3-9109-7f4665126ec1n%40chromium.org stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 101–115, android 101 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAANMuaNXE3KYymq%3Dd0jcOmw%2Br2si_DOJK4cc%2BqMCU8mCoQBWTA%40mail.gmail.com stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAANMuaNc94moaqPPWepai4THP1rOvXoxvixS%3Dostu2QGMLPtvA%40mail.gmail.com OK 5733583115255808 Protected Audience (ship) — "Protected audience API (formerly FLEDGE)" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2023-06-23 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/w9hm8eQCmNI stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 101–115, android 105 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABQTWr=-pfYiMtALbieskeC7K7E-fPDA8angjX3SM8gqQS6jpw@mail.gmail.com stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABQTWrn8eM3wOtUY3RzmDrt7SVxR_y_6Fo02bJ%2BF1bzbwpFfkQ%40mail.gmail.com OK 6552486106234880 Protected Audience (removal) — "Deprecate and Remove Protected Audience" [Feature removal] summary: Proposed (Proposed), desktop 153, android 153, webview 153; updated 2026-08-21 stage 410 dep: plan (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABQTWrnevtY14OMos%2BuDgzXtFwQJ5vzY3hEMmyRFQBKDHBz-9A%40mail.gmail.com stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 153, android 153 stage 470 dep: remove code (no milestone) OK 6412002824028160 Attribution Reporting (ship) — "Attribution Reporting API" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/7B0ldtZR_68/m/GjLBu0n4DgAJ stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 101–115, android 101 intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/jEnNpideO1Y/m/nlEDdjmnCgAJ stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/9402d8f1-1700-4eb3-8709-eaba907784aen%40chromium.org OK 6320639375966208 Attribution Reporting (removal) — "Deprecate and remove: Attribution Reporting API" [Feature removal] summary: Proposed (Proposed), desktop 153, android 153, webview null; updated 2026-08-21 stage 410 dep: plan (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/beb44d6a-aae5-4664-994a-38fb93bb4580n%40chromium.org stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 153, android 153 stage 470 dep: remove code (no milestone) OK 6256348582903808 Shared Storage (ship) — "Shared Storage API" [New or changed feature] summary: Origin trial (115), desktop 115, android 115, webview null; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/_quChIvPzT8/m/0W7IxD_1AAAJ stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 104–115, android 104 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAANMuaNn%3DOwqymhbTRPfcY6zW-S4Gs9JFummJhU%3Dx%2BcoydV%2BYw%40mail.gmail.com stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAANMuaOmcaZAPAgOg97yDtW%2BuEPMXnKb3nnth8GHS28KBqSAWQ%40mail.gmail.com OK 5076349064708096 Shared Storage (removal) — "Deprecate and Remove: Shared Storage API" [Feature removal] summary: Proposed (Proposed), desktop 153, android 153, webview null; updated 2026-08-21 stage 410 dep: plan (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAJ8xcq6ty%3DiW_L9%3DpbzJbZKAT7rVdu_%2B9-QJ2P2ojCRtSRk4xw%40mail.gmail.com stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 153, android 153 stage 470 dep: remove code (no milestone) OK 5743412790689792 Private Aggregation (ship) — "Private Aggregation API" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview 115; updated 2024-08-15 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAA%2BBiFkKSt4YBNUn2h42G3z%2BqjwxjFAo%3DsPnrbvvOoNaDa_aAQ%40mail.gmail.com stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 107–115, android 107 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAA%2BBiF%3DKQYXEVn%3DB4rMabH14UdYyA%2BF8qQkWyUVPB0rypS1N0Q%40mail.gmail.com stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAA%2BBiFk4cb%2Bi69Symy-KCjHbtquGSQCn5scXy_YMSSWGut2vJw%40mail.gmail.com OK 4683382919397376 Private Aggregation (removal) — "Deprecate and remove: Private Aggregation API" [Feature removal] summary: Proposed (Proposed), desktop 152, android 152, webview null; updated 2026-08-21 stage 410 dep: plan (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/8d2b9c6a-d1f1-4111-bdb4-31933998cd36n%40chromium.org stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 152, android 152 stage 470 dep: remove code (no milestone) OK 5699388062040064 Fenced Frames (ship) — "Fenced frames" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/Ko9UXQYPgUE/m/URRsB-qvAAAJ stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 102–115, android 102 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CADAcp0-Htb44ezkojydCMKEWM9YnXL0Zc+GVE9xLO68csf8tJw@mail.gmail.com stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CADAcp093_yvrvYDigFOZVdzmBN_cW0y6xLbthQHjNQY5K%2BjVgw%40mail.gmail.com OK 6366274495053824 Fenced Frames (removal) — "Remove FencedFrame element and window.fence APIs" [Feature removal] summary: Proposed (Proposed), desktop 156, android 156, webview 156; updated 2026-09-24 stage 410 dep: plan (no milestone) stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 156, android 156 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/6a7b8346.f5684636.88ead.041c.GAE%40google.com stage 470 dep: remove code (no milestone) OK 5640066519007232 Related Website Sets (ship) — "Related Website Sets" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2023-10-03 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/u/1/a/chromium.org/g/blink-dev/c/0EMGi-xbI-8/m/FgSjq6TtBwAJ stage 130 dev trial desktop 115, android 115 stage 140 eval readiness (no milestone) stage 150 origin trial desktop 89–93, android 89 intent: https://groups.google.com/u/1/a/chromium.org/g/blink-dev/c/XkWbQKrBzMg stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAD_OO4jfJ3tEbyWMX6RgJMFhhNe5t5aScd9kNerYMC8THe1-Sg%40mail.gmail.com OK 5194473869017088 Related Website Sets (removal) — "Deprecate and Remove: Related Website Sets (RWS)" [Feature removal] summary: Proposed (Proposed), desktop 153, android 153, webview 153; updated 2026-08-21 stage 410 dep: plan (no milestone) stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 153, android 153 stage 470 dep: remove code (no milestone) stage 1061 enterprise rollout desktop 144 stage 1061 enterprise rollout desktop 153 OK 5122534152863744 requestStorageAccessFor (ship) — "requestStorageAccessFor" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2025-03-25 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAGg35awh2_aqmFtWgOdo40vYVnWf%2BkEv3o7jxZ8DLbWq0eC3eQ%40mail.gmail.com stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial (no milestone) stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAD_OO4g87zWDtnn00w1%2BVD%2BYwr9oP0TjOfemX97VrBe96kby3A%40mail.gmail.com OK 5162221567082496 requestStorageAccessFor (removal) — "Deprecate and Remove: document.requestStorageAccessFor" [Feature removal] summary: Proposed (Proposed), desktop 153, android 153, webview 153; updated 2026-08-21 stage 410 dep: plan (no milestone) stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) desktop 153, android 153 stage 470 dep: remove code (no milestone) stage 1061 enterprise rollout desktop 144 stage 1061 enterprise rollout desktop 153 OK 5179189105786880 CHIPS (ship) — "Cookies Having Independent Partitioned State (CHIPS)" [New or changed feature] summary: Enabled by default (114), desktop 114, android 114, webview 114; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/hvMJ33kqHRo/m/3diUOI0uAQAJ stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 106–106, android 106 intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/_dJFNJpf91U stage 160 ship desktop 114, android 114 intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/JNOQvsTxecI OK 5279664766713856 CHIPS (WebView entry) — "Deprecate CHIPS and Relaunch in WebView" [Feature removal] summary: Proposed (Proposed), desktop null, android null, webview 128; updated 2026-06-24 stage 410 dep: plan (no milestone) stage 430 dep: dev trial (no milestone) stage 450 dep: deprecation trial (no milestone) stage 460 dep: ship (removal) (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/_hHg5MASz4s/m/xWu_9uZNAgAJ stage 470 dep: remove code (no milestone) OK 5612590694662144 Storage Access API (ship) — "Storage Access API" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial (no milestone) stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAD_OO4hnt1Rd5WSC%3DFU9dQriOP%3DKF0Cz9McxoT2_7UgP0u%3DKPw%40mail.gmail.com OK 5078049450098688 Private State Tokens (ship) — "Private State Token API" [New or changed feature] summary: Enabled by default (115), desktop 115, android 115, webview null; updated 2024-07-18 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/forum stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial desktop 84–101, android 84 intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/UIvia1WwIhk stage 160 ship desktop 115, android 115 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CANduzxCC8T5D9WSrvo0yq7Tu7hdAj-YXLwuOyu2DqqkTRoHQRg%40mail.gmail.com OK 6438627087220736 FedCM (ship) — "Federated Credentials Management (was WebID)" [New or changed feature] summary: Origin trial (108), desktop 108, android 108, webview null; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/2B4TJ7j2U4M/m/1X5T3OszCAAJ stage 130 dev trial desktop 103, android 98 stage 140 eval readiness (no milestone) stage 150 origin trial desktop 103–107, android 101 intent: https://groups.google.com/a/chromium.org/g/blink-dev/c/kws-gltC5us stage 160 ship desktop 108, android 108 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CALdEk-yf1BMQ4ZtMYH9cCQsecroEE3ZPOKgY8LU%3DNX3zAfbwYA%40mail.gmail.com OK 5705149616488448 Bounce tracking mitigations (ship) — "Bounce Tracking Mitigations" [New or changed feature] summary: Enabled by default (116), desktop 116, android 116, webview null; updated 2023-10-10 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAK7rkMi_7z0-yeTbBiE43V5SD1ri4jSVxrkR8Gs%3DD0NRoRKivA%40mail.gmail.com stage 130 dev trial desktop 115, android 115 stage 140 eval readiness (no milestone) stage 150 origin trial (no milestone) stage 160 ship desktop 116, android 116 intent: https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAK7rkMhBxsxD-V15C0idt4OULm72Oac2yT_hVyPf3noUxex0LA%40mail.gmail.com OK 5995832180473856 UA Client Hints (ship) — "Sec-CH-UA Client Hints" [New or changed feature] summary: Enabled by default (89), desktop 89, android 89, webview null; updated 2025-08-22 stage 110 incubate (no milestone) stage 120 prototype (no milestone) intent: https://groups.google.com/a/chromium.org/d/msg/blink-dev/WQ0eC_Gf8bw/dhWMhCYYDwAJ stage 130 dev trial (no milestone) stage 140 eval readiness (no milestone) stage 150 origin trial (no milestone) stage 160 ship desktop 89, android 89 OK search "Topics": 8 results, 0 Feature removal, of which name mentions Topics: 0 OK search "browsingTopics": 1 results, 0 Feature removal, of which name mentions Topics: 0 OK search "Deprecate and remove": 106 results, 53 Feature removal, of which name mentions Topics: 0 == 3. Chrome release notes, "Deprecations and removals" (developer.chrome.com) == OK [200] M150 (page last updated 2026-06-30): Deprecations and removals There are no deprecations or removals in this release. Except as otherwise noted, the content of this page is licensed under the Creat OK [200] M151 (page last updated 2026-07-28): Chrome 151 removes support for macOS 12 | Remove [LegacyNoInterfaceObject] from FontFaceSet IDL OK [200] M152 (page last updated 2026-08-25): Remove Private Aggregation API OK [200] M153 (page last updated 2026-09-08): Deprecate and remove Protected Audience | Deprecate and remove Related Website Sets (RWS) | Deprecate and remove Shared Storage API | Deprecate and remove document.requestStorageAccessFor | Deprecate and remove Attribution Reporting API | Remove non-standard navigations targeted at _current OK [200] M154 (page last updated 2026-09-22): NO "Deprecations and removals" section on the page OK [404] https://developer.chrome.com/release-notes/155?hl=en — M155 not yet stable (stable_date 2026-10-06) OK [404] https://developer.chrome.com/release-notes/156?hl=en — M156 not yet stable (stable_date 2026-10-20) OK [200] release notes 152 — "the Private Aggregation API is deprecated and removed (along with related Privacy Sandbox APIs)" OK [200] release notes 153 — "the Protected Audience API is planned for deprecation and removal (along with related Privacy Sandbox APIs)" OK [200] release notes 153 — "the Shared Storage API is planned for deprecation and removal" OK [200] release notes 153 — "the Attribution Reporting API is planned for deprecation and removal" OK [200] release notes 153 — "Related Website Sets is planned for deprecation and removal" OK [200] release notes 153 — "it is planned for deprecation and removal along with Related Website Sets" OK [200] release notes 150 — "There are no deprecations or removals in this release." == 4. blink-dev intent threads: the phased plan and its September status == OK [200] blink-dev Protected Audience k_nubsMb97g — "Planning to deprecate in M144 and remove in M150" OK [200] blink-dev Protected Audience k_nubsMb97g — "M152: Permanently remove the underlying implementation code for the API, and replace it with a lightweight stub API. The stub will remain removable via field trial." OK [200] blink-dev Protected Audience k_nubsMb97g — "M152 Canary/Beta: Begin a controlled rollout of the stub API removal via a field trial." OK [200] blink-dev Protected Audience k_nubsMb97g — "When I test on Chrome 152 stable, both navigator.joinAdInterestGroup and navigator.runAdAuction exist." OK [200] blink-dev Protected Audience k_nubsMb97g — "We have seen no regressions on 1% of stable (M152+)" OK [200] blink-dev Protected Audience k_nubsMb97g — "We are actually still seeing ~ 1% usage." OK [200] blink-dev Protected Audience k_nubsMb97g — "10% Stable: Hold for 2 weeks (through Oct 12)" OK [200] blink-dev Protected Audience k_nubsMb97g — "100% Stable: Roll out starting Oct 26" OK [200] blink-dev Protected Audience k_nubsMb97g — "It wasn't actually an increase in API usage, it was just a change in measurement." OK [200] blink-dev Topics _R85yctz4Rs — "Deprecate in M144 and then remove in M150" OK [200] blink-dev Topics _R85yctz4Rs — "Currently the usage is 4.9% of page loads." OK [200] blink-dev Topics _R85yctz4Rs — "replace it with a lightweight stub API" OK [200] blink-dev Topics _R85yctz4Rs — "We have reached 1% on Stable (M153+) with no regressions or known breakage." OK [200] blink-dev Topics _R85yctz4Rs — "The current usage is at 2%" OK [200] blink-dev Topics _R85yctz4Rs — "10% Stable: Hold for 2 weeks (through Oct 15)" OK [200] blink-dev Topics _R85yctz4Rs — "100% Stable: Target launch after Oct 15" OK [200] blink-dev Attribution Reporting 4K2RRt6VYCQ — "Currently the usage is 19.7% of page loads." OK [200] blink-dev Attribution Reporting 4K2RRt6VYCQ — "M152: Permanently remove the underlying implementation code for the API, and replace it with a lightweight stub API." OK [200] blink-dev Shared Storage uh5Ke6qyegc — "Currently, ~11% of page loads use Shared Storage API." OK [200] blink-dev Shared Storage uh5Ke6qyegc — "M152: Permanently remove the underlying implementation code for the API, and replace it with a lightweight stub API." OK [200] blink-dev Private Aggregation Ld7avyD0U0Q — "Currently the usage is still 2.4% of page loads." OK [200] blink-dev Private Aggregation Ld7avyD0U0Q — "M152: Permanently remove the underlying implementation code for the API. There is no need to replace it with a stub API" OK blink_dev_Attribution_Reporting_4K2RRt6VYCQ: 11 date stamps rendered, 9 distinct dates, first 2025-11-07, newest 2026-07-01 OK blink_dev_Private_Aggregation_Ld7avyD0U0Q: 9 date stamps rendered, 5 distinct dates, first 2025-11-07, newest 2026-07-08 OK blink_dev_Protected_Audience_k_nubsMb97g: 14 date stamps rendered, 11 distinct dates, first 2025-11-07, newest 2026-09-24 OK blink_dev_Shared_Storage_uh5Ke6qyegc: 12 date stamps rendered, 9 distinct dates, first 2025-11-07, newest 2026-07-07 OK blink_dev_Topics_R85yctz4Rs: 9 date stamps rendered, 6 distinct dates, first 2025-11-07, newest 2026-09-24 == 5. Google's own Privacy Sandbox status and retirement pages == OK [200] privacysandbox.google.com/overview/status — "Last updated: August 14, 2026" Chrome | Bounce tracking mitigations CHIPS FedCM frame-ancestors directive Private State Tokens Storage and Network State Partitioning Storage Access (including Storage Access Header) User-Agent Client Hints User-Agent reduction | Continue to support Chrome | Aggregation Service Attribution Reporting Fenced Frames Private Aggregation Protected Audience Related Web Sites (including requestStorageAccessFor()) Shared Storage (including Select URL) Topics | Deprecate and remove Chrome | IP Protection Partitioned Popins Related Website Partition | Discontinue Chrome | Fenced Storage Read Private Proofs Probabilistic Reveal Tokens Script Blocking | Do not launch Android | Attribution Reporting On-Device Personalization Protected App Signals Protected Audience SDK Runtime Topics | Deprecate and remove OK [200] retirement blog 2025-10-17 — "Related Website Sets (including requestStorageAccessFor and Related Website Partition), SelectURL, SDK Runtime and Topics (Chrome and Android)" OK [200] enrollment page — "We will no longer accept new account creation and enrollment" OK [200] CMA case page — "Case state: Closed" OK [200] CMA case page — "Closed: 17 October 2025" == 6. Artefacts the page names (GitHub archived / pushed_at; tip-of-tree CDP) == OK github.com/GoogleChrome/related-website-sets archived=true pushed_at=2025-11-21 OK github.com/GoogleChrome/ip-protection archived=true pushed_at=2025-11-03 OK github.com/privacysandbox/attestation archived=true pushed_at=2026-01-22 OK github.com/patcg-individual-drafts/topics archived=true pushed_at=2025-11-07 OK devtools-protocol master Storage domain: commands runBounceTrackingMitigations; events (none) OK Chrome for Testing M151: 151.0.7922.138, linux-arm64 no OK Chrome for Testing M152: 152.0.7977.82, linux-arm64 no OK Chrome for Testing M153: 153.0.8010.52, linux-arm64 yes OK Chrome for Testing M154: 154.0.8037.57, linux-arm64 yes OK Chrome for Testing M155: 155.0.8059.12, linux-arm64 yes OK Chrome for Testing M156: 156.0.8075.0, linux-arm64 yes INFO npm playwright dist-tags: latest 1.63.0 (2026-09-04), next 1.64.0-alpha-2026-09-27 == summary == FAILED checks: 0
What this refresh could not establish
- The behaviour of the Chrome that users run. Chrome for Testing 153–156 is Google-built and agrees with Chromium on every row. But it is not the auto-updating user binary, and no automation launch can see the field-trial state that removes the stubs for 1–10% of users. There is no Google-built arm64 152.
- Private Aggregation's behaviour. Only its Permissions-Policy feature was probed (unknown from 153). A worklet-side
privateAggregationcall was not attempted. - Related Website Sets' behaviour. Not probed. The page carries its status labels and its CDP command only.
- Whether the October field-trial dates held. The page gives them as requests made on 2026-09-24, not as events.
- Whether the M150 field-trial rejection ever reached stable users. The plan says it would; no fetched message reports it.
- Whether servers still send
Attribution-Reporting-Register-*orObserve-Browsing-Topics. Only our own servers were in the probe. The page says “whatever … servers may still send”. - Whether Puppeteer's default launch picks up a variations seed. Puppeteer 25.5.0 passes
--disable-background-networkingbut not--disable-field-trial-config. The probe ran only through Playwright. - Why full Chromium 151 refuses what headless shell 151 allows. The page gives our reading, a settings layer the shell lacks, and flags it as untested.
- The Protected Audience and Topics stubs' own removal from the automation build. No milestone is published for deleting the stub code, and the stubs are still in 156.0.8075.0, so the page tells the reader to re-run the probe.
Judgement calls, 2026-09-27
- Measure instead of restating. The item asked for chromestatus to be re-fetched and the staircase restated. Re-fetching showed that chromestatus, the release notes and the status page disagree with each other and with the binaries. A fourth restatement would have repeated the M153 error in a new form. The page's status column now rests on the probe, and the three status sources are shown beside it as labels.
- A new subsection rather than a longer table. The API table keeps one short status cell per API. The per-source evidence goes in “The removal staircase, measured”, so a reader designing a crawl can skip it.
- Kept the 2026-09-01 measurement section, labelled as pre-removal, rather than re-running it on 156. It documents the instruments as they were when they worked, and those instruments are what a retrospective crawl on a pinned binary needs.
- The two new blink-dev usage figures went into the existing telemetry table with the same unaudited-vendor caveat. So did the “Fledge” use-counter redefinition, because it is a measurement pitfall in the only post-2025 series there is.
- Did not re-run the Puppeteer branch of
ps_probe.mjsacross the builds. Puppeteer is what the corpus's Protected Audience crawl used, and its launch is the full Chromium that already disagreed with Playwright's in 151, so a reasonable reviewer would have added it. It was left out because the full-Chromium column is already covered by the Debian and Chrome for Testing builds. The flag-set difference is recorded as an unknown above. - Two origins in one probe. The calls table runs on
https://localhost, which is exempt from enrollment, with no extra flags. The auction runs on a mappedprobe.examplewith the ads-API override and an enrollment override, the flag set ofps_cdp_probe.mjs. The goal was to change one thing at a time against the 2026-09-01 probes, not to make the two tables comparable with each other. - Did not edit cookie_syncing. It carries a dated sentence (“as of 2026-08-26 … a removal milestone of Chrome 153 with status 'Proposed'”) and quotes the status page's label as “Intent to deprecate and remove filed”, which now reads “Deprecate and remove”. That page is not this item's; it is listed in the run's outcome note instead.
Review log, 2026-09-27 refresh
Three focused sonnet passes ran in parallel on a frozen local draft (the page was not saved until they had reported). Each was told that the author's context may not be exhaustive, and each got the before and after page, the diff, this section, both scripts and their outputs. Each wrote its findings to a file as it went.
| Pass | Finding | Disposition |
|---|---|---|
| figures vs script | MAJOR. The top box and the staircase table dated the Protected Audience auction failure and the Shared Storage rejection “from M152”. But build [2], full Chromium 151, already shows both, and the page's own next paragraph said so | Accepted, fixed. Both places now say the 152 date is a headless-shell-to-headless-shell comparison, and that full Chromium already refused both in 151. The table's intro paragraph states the rule once |
| figures vs script | NIT: @@REVIEW_LOG@@ placeholder unfilled in the draft | Expected: this table is what fills it |
| figures vs script | Code: no bug found. lift() pulls the same strings; request logs are reset per build after the browser closes, so nothing leaks between builds; the exit gate fails under MUTATE=1. The reviewer re-ran the four 2026-09-01 probes and reproduced their outputs | Noted |
| citations and quotes | MAJOR. The CHIPS WebView footnote said “Proposed with no milestone”; the entry's browsers.chrome.webview is 128 | Accepted, fixed. The footnote now says WebView milestone 128, no desktop or Android milestone. The external-checks script printed only desktop/android, so it could not have caught this, and it now prints webview too |
| citations and quotes | All other quotes verbatim: the four-step plan and its “word for word” claim across four threads, the PrivAgg “no need … stub”, the PA 09-23/09-24 figures, the Topics 09-24 message, the “Fledge” exchange (Garrett Johnson asked, Paul Jensen answered), the release-notes wording, and the status-page date. Links resolve to existing headings. Footnotes are balanced. No new citekeys | Noted |
| external currency | HIGH. “Chrome for Testing is x86-64 only for Linux” was wrong: linux-arm64 exists for 153.0.8010.52, 154.0.8037.57, 155.0.8059.12 (and 156.0.8075.0). The first pass had filtered the manifest on linux64 | Accepted, and it changed the result. The four CfT arm64 builds were downloaded and added to the probe: 10 builds became 14. All four agree with Chromium on every row, and CfT 156.0.8075.0 also shows getRelatedWebsiteSets gone. The page no longer says “no Google Chrome binary was run”. It says Google-built but not the user's Chrome. The external-checks script printed the arm64 availability; the generic pass pointed out that it did not assert it, and it now does |
| external currency | LOW. OpenWPM is at v0.37.0 (2026-09-06), not v0.36.0. Its README still says “OpenWPM is built on top of Firefox” | Accepted, fixed (an unedited sentence outside this refresh's window; verified against the v0.37.0 README). The page's “README opens” became “README says”, because the sentence is not the README's first |
| external currency | Rejected secondary sources: a search-engine summary that dropped “planned” and claimed rSAFor was “removed in Chrome 153”, and mdn/browser-compat-data PR #30659 (“Chrome 152 removed Shared Storage API”). That PR is contested in its own thread and spans 152/153/154 across its diff | Agreed; neither cited. They are recorded here so the next run does not re-add them |
What this round says about the slots. The one finding that changed the measurement came from the currency pass, and it concerned the author's tooling, not the page's sources. The figures pass caught the page contradicting itself within one subsection. Both MAJOR fixes are new text, so they went to the generic pass below.
Generic fable pass, on the draft with every fix above applied, frozen again. Same bundle plus this table. It reported 1 MAJOR, 21 MINOR and 7 NIT. It found more than the three focused passes combined, as it did on 2026-09-01.
| Finding | Disposition |
|---|---|
MAJOR A1. “Puppeteer was pointed at the same Chromium binary … not of two browser builds”, on the content page and in this page's run record. The refresh had just established that chromium.executablePath() is the full Chromium and Playwright's default the headless shell, and left the sentence standing | Accepted, fixed in both places, and added to What the 2026-09-01 page said that did not hold. The result stands: builds [1] and [2] agree on presence |
A2. “Permissions-Policy feature unknown” was inferred from allowsFeature() = false, which is also the answer for a known but ungranted feature | Accepted, and measured instead of reworded: the probe now prints featurePolicy.features() membership. It matches allowsFeature() in all 14 builds, so “unknown” is now a measurement, and the staircase intro says how “known” is defined |
| A3. “Stub from M152” for Protected Audience is the plan's word. No build says so, and the headless-shell failure at 152 is over-determined (the implementation could be gone, or the override switches could be) | Accepted. The cell now says “No auction runs from M152 … a stub per the blink-dev plan”. The timeline says what was observed |
| A4. “After field-trial users had started to get rejections” and “(1%, then 10%) have no method at all” state rollout facts no source gives | Accepted. The top box now says the October percentages are requests. “No method at all” is flagged as what the plan implies, which no automation build can show. The M150 rollout is listed as unknown |
| A5. “The plan Chrome then followed” is contradicted by Shared Storage (IDL gone at 153 with no field trial); “posted to all five” is contradicted by the PrivAgg footnote | Accepted, fixed. Now “four of the five”, and Shared Storage is named as not having followed its own plan |
A6. “The Register-* responses servers still send” was never observed | Accepted, reworded to “may still send; we did not measure whether any do” |
| A7. The Topics headers were never provoked | Accepted, and measured: fetch(…, {browsingTopics: true}) was added. Sec-Browsing-Topics is sent only by the two 151 builds, and the header section says so |
A8. “Chrome for Testing gets no field trials” is a property of the Playwright launch (--disable-field-trial-config, fresh profile), not of the binary | Accepted. The page now attributes it to the launch. We then checked Puppeteer 25.5.0's own launcher: it passes --disable-background-networking but not --disable-field-trial-config or --disable-component-update. So the reviewer's own suggestion that Puppeteer “passes the same” flags was wrong, and the page says only what was verified |
| B1–B5. “Ten Chromium binaries” left over; “this run had only Chromium 151”; “two status sources” against a list of three; “you need M151” without “as headless shell”; “156” not flagged as pre-branch | All accepted, fixed |
| B7. The Fenced Frames entry is titled “Remove …”, not “Deprecate and remove” | Accepted; the shortened top box no longer quotes the label |
C1–C5. Untouched sentences made stale: RWS “reachable via CDP”; “What to Read First” sending readers to the status pages as the primary sources; “the most actionable thing on the page”; present-tense CDP and override sentences; the .dat “moves with the component updater” (Playwright passes --disable-component-update) | All accepted, fixed. C5 says Playwright only, for the reason in A8 |
| D1. The answer to “can I measure this today” is scattered | Accepted: a five-sentence “If you start a crawl today” box at the top of the detection section |
| D2. What to Report's example version needs the stub warning | Accepted |
| E1. The top box grew from 477 to 746 words and held evidence, mechanism and the page's own history | Accepted. It is now 4 short bullets after the two unchanged ones. The four-step plan, the thread links and the October dates moved to the staircase section, and the “until 2026-09-27 this page said M153” history moved to this page |
| F1. The external-checks script printed “OK” beside values the page states, e.g. “linux-arm64 NO” | Accepted. An EXPECT block now makes every stated value fail the run when it changes, and report-only lines print INFO. The mutation run shifts one expectation too, and prints 10 FAILED |
| F2. “No message after July” rests on date stamps in one rendered page | Accepted. The stamp count per thread is printed, the caveat is in the source table and in the page's footnote, and a new newest date fails the run |
| F3. “Could not establish” omitted three things the page states | Accepted. Two of them (policy-feature membership, the Topics header) were measured instead. The M150 rollout, third-party Register-* senders and Puppeteer's seed behaviour are listed |
| F4. This page's run record repeated the A1 sentence | Accepted, fixed with a dated inline correction |
| F5. “The summary status field is not evidence of anything” overstated, since stage milestones from the same endpoint are quoted | Accepted, reworded |
| F6. Unrecorded decisions: no Puppeteer branch across builds, no persistent-profile launch, two origins in one probe | Accepted, recorded under Judgement calls, 2026-09-27; not acted on |
Rejected: nothing, except one detail inside A8 (Puppeteer's flags), which was checked and found wrong. As on 2026-09-01, the generic slot found the class of defect the checklists cannot see: advice that is well-sourced in each sentence and wrong in combination. Here that was the untouched Puppeteer sentence, which contradicted the refresh's own finding eighteen lines further down.
Re-check of the fixes (sonnet), on a third frozen snapshot, told that fixes are unreviewed text. It found 0 MAJOR, 2 MINOR and 1 NIT, and confirmed the rest, including the Playwright and Puppeteer flag claims read from both packages' launchers.
| Finding | Disposition |
|---|---|
| MINOR. Section 1 of the checks script still printed “OK” for schedule dates the page prints (M152–M154 stable, M156 branch) without asserting them | Accepted, fixed. Those four dates are asserted; the other milestones print INFO |
| MINOR. The main status table kept “Permissions-Policy feature unknown” after the staircase table had moved to the measured “no longer a known policy feature” | Accepted, fixed |
| NIT. The “If you start a crawl today” box said “a pinned pre-152 binary”, which is broader than the evidence (only the headless shell ran an auction in 151) | Accepted, fixed: it now says chrome-headless-shell, not full Chromium |
These three fixes are one-clause edits and were not sent for a further pass.
References
Markup sweep, 2026-09-17
Mechanical rendering repair only: a fresh live raw/XHTML export of 188 pages was checked with check_wrap.mjs and check_typography.mjs. Affected plugin tags, CLI flags and heading markup were repaired; no figures or substantive prose were changed. The resulting source and rendered DOM were re-checked after saving.
