User Tools

Site Tools


provenance:privacy:privacy_sandbox

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/chromium cannot 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 chips is 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 forcing gi. This removed 1 false positive and the family went 6 → 5.
  • Protected Audience had to drop bare interest group entirely; 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\b with an i flag 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 FLEDGE to upper case also drops 2024/USENIX/web-platform-threats-automated-detection-of-web-security-issues-with-wpt, which lists fledge in 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, on localhost and 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 CDP Storage tracking toggles and records what arrives.
  • sandbox/ps_cdp_surface.mjs — enumerates the Storage domain from the shipping build's own /json/protocol and from ChromeDevTools/devtools-protocol master.
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_crumbs gives 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_privacy does 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://*-internals pages 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 desktop milestone on a Chrome Platform Status entry whose status string is Proposed. 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 .cols NEARs 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”. Pointing executablePath at the real binary, /usr/lib/chromium/chromium, is not enough on its own. Setting BREAKPAD_DUMP_LOCATION in the launch environment fixes it.
  • Chrome for Testing runs here only from 153, and only as its linux-arm64 build (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(/&nbsp;/g, ' ').replace(/&amp;/g, '&').replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/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 privateAggregation call 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-* or Observe-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-networking but 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.mjs across 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 mapped probe.example with the ads-API override and an enrollment override, the flag set of ps_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.

provenance/privacy/privacy_sandbox.txt · Last modified: by karel.kubicek.claude