User Tools

Site Tools


provenance:programming:crawler:openwpm

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
provenance:programming:crawler:openwpm [2026/08/14 08:38] – New page: provenance for programming:crawler:openwpm — populations, the single-regex fold, the full-text sweep, the report script and its unedited output, quote checks, external checks, every install command and its result, judgement calls, and the review karel.kubicek.claudeprovenance:programming:crawler:openwpm [2026/09/21 13:36] (current) – Quote-check refresh 2026-09-21: re-ran quote_check.mjs --tools open[ -]?wpm with the pypdf fallback; 12 below threshold -> 10 rescued + 2 below in both; new provenance section. Authored by Claude karel.kubicek.claude
Line 24: Line 24:
 Never "of 5,859 papers". Every table on the content page names one of these. Never "of 5,859 papers". Every table on the content page names one of these.
  
-**Why ''produced'' counts.** Three papers produced rather than used: the OpenWPM-Mobile papers and one "custom OpenWPM instrumentation". A paper that forked the crawler crawled with the fork, so it belongs in a population about who crawls with OpenWPM. The three papers that only ''compared'' or ''mentioned'' it are excluded and named in the report (§4).+**Why ''produced'' counts.** Three papers produced rather than used: OpenWPM itself {[englehardt2016online]}, OpenWPM-Mobile {[das2018_sixth]}, and one paper's "custom OpenWPM instrumentation" {[acar2020_noboundaries]} — so the rule's real effect is keeping the tool's own paper in the population. A paper that forked the crawler crawled with the fork, so it belongs in a population about who crawls with OpenWPM. The three papers that only ''compared'' or ''mentioned'' it are excluded and named in the report (§4).
  
 **The one paper inside OPENWPM and outside CRAWLED:** ''PETS/2021/a-calculus-of-tracking-theory-and-practice'', ''studyTypes'' ''["existing-dataset-analysis","system-or-defence-proposal"]'' — it re-analyses data someone else crawled with OpenWPM. Left in OPENWPM (it is a paper whose results depend on OpenWPM's instrumentation) and it is why 59, not 60, of the OpenWPM papers are in CRAWLED. **The one paper inside OPENWPM and outside CRAWLED:** ''PETS/2021/a-calculus-of-tracking-theory-and-practice'', ''studyTypes'' ''["existing-dataset-analysis","system-or-defence-proposal"]'' — it re-analyses data someone else crawled with OpenWPM. Left in OPENWPM (it is a paper whose results depend on OpenWPM's instrumentation) and it is why 59, not 60, of the OpenWPM papers are in CRAWLED.
Line 65: Line 65:
 | A | population, spellings, ''usedOrMentioned'', category check | //Methodology and limitations// | | A | population, spellings, ''usedOrMentioned'', category check | //Methodology and limitations// |
 | B | venue and year shape | //Who uses it// | | B | venue and year shape | //Who uses it// |
-| C | version reporting, release dates, bundled Firefox, lag | //A version number is a browser version//, //Which version, and how far behind// |+| C | version reporting, release dates, bundled Firefox, lag | //An OpenWPM version is a Firefox version, by default//, //Which version, and how far behind// |
 | D | crawl-configuration reporting rates vs OTHER_CRAWLED, statefulness values | //Stateful and stateless//, //What papers say about the configuration// | | D | crawl-configuration reporting rates vs OTHER_CRAWLED, statefulness values | //Stateful and stateless//, //What papers say about the configuration// |
 | E | full-text sweep: OpenWPM identifiers, prose descriptions, statefulness cross-check | //Almost nobody says which instruments were on// | | E | full-text sweep: OpenWPM identifiers, prose descriptions, statefulness cross-check | //Almost nobody says which instruments were on// |
Line 97: Line 97:
 ==== 3.3 The version-lag column, and its limits ==== ==== 3.3 The version-lag column, and its limits ====
  
-''RELEASED'' in ''report_openwpm.mjs'' is the tag→date map from ''git for-each-ref --sort=creatordate refs/tags'' in the OpenWPM repository. ''FIREFOX_OF'' is the bundled-Firefox map, read by checking out each ''v*'' tag and grepping ''scripts/install-firefox.sh'' for its ''FIREFOX_<major>_<minor>_RELEASE'' comment.+''RELEASED'' in ''report_openwpm.mjs'' is the tag→date map from ''git for-each-ref %%--sort%%=creatordate refs/tags'' in the OpenWPM repository. ''FIREFOX_OF'' is the bundled-Firefox map, read by checking out each ''v*'' tag and grepping ''scripts/install-firefox.sh'' for its ''FIREFOX_<major>_<minor>_RELEASE'' comment.
  
 Three caveats, all of which are on the page in weaker form: Three caveats, all of which are on the page in weaker form:
Line 103: Line 103:
   * **Tag date ≠ release-publication date.** GitHub's releases API gives v0.30.0 as 2024-10-06 and v0.29.0 as 2024-07-20, where the tags are 2024-10-02 and 2024-07-15. The page uses **tag creatordate** throughout, consistently. Nothing on the page turns on a difference of days.   * **Tag date ≠ release-publication date.** GitHub's releases API gives v0.30.0 as 2024-10-06 and v0.29.0 as 2024-07-20, where the tags are 2024-10-02 and 2024-07-15. The page uses **tag creatordate** throughout, consistently. Nothing on the page turns on a difference of days.
   * **"Releases behind" uses 1 January of the venue year** as the reference point, not the submission date, which is unknown. It is therefore an upper bound on how out of date the authors were when they crawled. The page says this.   * **"Releases behind" uses 1 January of the venue year** as the reference point, not the submission date, which is unknown. It is therefore an upper bound on how out of date the authors were when they crawled. The page says this.
-  * **The bundled-Firefox column is nominal, and a review pass caught the page treating it as actual.** OpenWPM launches whatever ''FIREFOX_BINARY'' points at, so the pin is a default. The first draft wrote that a 2024 paper "crawled with the Firefox 90 of mid-2021"; that paper {[shaoor2024purl]} in fact states "We use OpenWPM (v0.17.0) [11] and Firefox (v102) [64] for crawling", and so does {[shaoor2023cookiegraph]} for the same OpenWPM version. Both were read in the source. ''report_openwpm.mjs'' now carries a **hand map** (''FIREFOX_STATED'') of the papers that state a crawl browser version, each with its deciding sentence, because a regex cannot do it: ''/Firefox v?\d+/'' over the same 15 papers returns 6 papers, and the three extra are citation-marker splices and Firefox versions discussed in related work. 3 of 15 state it; 2 of those 3 ran a **newer** browser than their release ships.+  * **The bundled-Firefox column is nominal, and a review pass caught the page treating it as actual.** OpenWPM launches whatever ''FIREFOX_BINARY'' points at, so the pin is a default. The first draft wrote that a 2024 paper "crawled with the Firefox 90 of mid-2021"; that paper {[shaoor2024purl]} in fact states "We use OpenWPM (v0.17.0) [11] and Firefox (v102) [64] for crawling", and so does {[shaoor2023cookiegraph]} for the same OpenWPM version. Both were read in the source. ''report_openwpm.mjs'' now carries a **hand map** (''FIREFOX_STATED'') of the papers that state a crawl browser version, each with the sentence that decided it. **The figures in this paragraph were 3 of 15 and 2 mismatches until 2026-09-05; they are now 9 of 15 and 3 mismatches** — see §14.1, and read that entry before trusting any count of this shape, because the seed probe was wrong twice for two different reasons. Current: 9 of the 15 state a crawl browser. 2 used a pre-0.10 release with no pin to compare against; of the remaining 7, **4 ran the Firefox their release pins and 3 did not**.
   * **Three stated versions have no tag** (''0.1.0'', ''0.9.0'', ''0.13''), so their rows carry "—" rather than a guess. ''0.1.0'' predates the first tag (v0.2.0, 2014-08-12); ''0.9.0'' was never tagged (the sequence goes 0.8.0 → 0.10.0); ''0.13'' is presumably 0.13.0 but the paper did not write it that way and the report does not correct papers.   * **Three stated versions have no tag** (''0.1.0'', ''0.9.0'', ''0.13''), so their rows carry "—" rather than a guess. ''0.1.0'' predates the first tag (v0.2.0, 2014-08-12); ''0.9.0'' was never tagged (the sequence goes 0.8.0 → 0.10.0); ''0.13'' is presumably 0.13.0 but the paper did not write it that way and the report does not correct papers.
  
Line 331: Line 331:
     '0.34.0': '2026-05-08',     '0.34.0': '2026-05-08',
     '0.35.0': '2026-06-17',     '0.35.0': '2026-06-17',
 +    '0.36.0': '2026-08-24',
   };   };
   // Which Firefox each release bundles, read out of scripts/install-firefox.sh   // Which Firefox each release bundles, read out of scripts/install-firefox.sh
Line 341: Line 342:
     '0.22.0': 114, '0.23.0': 115, '0.24.0': 117, '0.25.0': 118, '0.26.0': 121,     '0.22.0': 114, '0.23.0': 115, '0.24.0': 117, '0.25.0': 118, '0.26.0': 121,
     '0.27.0': 122, '0.28.0': 123, '0.29.0': 128, '0.30.0': 130, '0.31.0': 134,     '0.27.0': 122, '0.28.0': 123, '0.29.0': 128, '0.30.0': 130, '0.31.0': 134,
-    '0.32.0': 148, '0.33.0': 149, '0.34.0': 150, '0.35.0': 152,+    '0.32.0': 148, '0.33.0': 149, '0.34.0': 150, '0.35.0': 152, '0.36.0': 154,
   };   };
   const stated = [];   const stated = [];
Line 396: Line 397:
     )     )
   );   );
-  // Does the paper also say which BROWSER it ran? A regex sweep cannot answer +  // Does the paper also say which BROWSER it ran? Hand map, each entry carrying 
-  // this: /Firefox v?\d+/ over the same 15 papers returns 6, and the extra ones +  // the sentence that decided it, read against paper.cols.txt. 
-  // are citation-marker splices ("Firefox [54]4"), a version of something else, +  // 
-  // or a Firefox version discussed in related work rather than used for the +  // THE WIDTH AND THE POPULATION OF THE SEED PROBE BOTH DECIDE THE PUBLISHED 
-  // crawl. So this is a hand map, each entry carrying the deciding sentence, +  // NUMBER, and this map got each wrong once: 
-  // read on 2026-08-14. Added after a review pass found the page inferring a +  //   2026-08-14  seeded with /Firefox v?\d+/ -> 3 papers. Misses "Firefox 
-  // browser version for a paper that states a different one.+  //               version 88", "the Firefox browser (v100.0", and a "Fire-|fox 
 +  //               v121.0" cut by a column splice. Published as "3 of 15" for 
 +  //               three weeks, on a page that quoted one of the missed papers 
 +  //               three paragraphs later as its best-documented row. 
 +  //   2026-09-05a re-swept wide, but only over slugs a reviewer had named -> 7. 
 +  //               Still missed fouad2022_cookie (a setup TABLE: "Firefox version 
 +  //               68.0 45.0.1" sits far from "OpenWPM ver-|sion v0.9.0 v0.7.0") 
 +  //               and demir2023_similarity ("the most recent stable Firefox 
 +  //               version available ... (v95.0 ...)"). 
 +  //   2026-09-05b re-swept wide over ALL 15 version-stating papers -> 9. 
 +  // The sweep is run below over the whole population and its REJECTED hits are 
 +  // printed with reasons, so the map can be audited against its own residue.
   const FIREFOX_STATED = {   const FIREFOX_STATED = {
     'apophanies-or-epiphanies-how-crawlers-impact-our-understanding-of-the-web': {     'apophanies-or-epiphanies-how-crawlers-impact-our-understanding-of-the-web': {
       firefox: '52.9',       firefox: '52.9',
-      quote: 'version string recorded by the extraction as "0.8; Firefox 52.9"; consistent with the firefox-52 era pin',+      quote: 'at the time of our experiments Open-WPM was unable to drive Firefox versions later than v52.9, while Selenium drove Firefox v66.0 (and Table 3: "OpenWPM [28] 0.8 | Selenium v3.6, Firefox v52.9")', 
 +    }, 
 +    'my-cookie-is-a-phoenix-detection-measurement-and-lawfulness-of-cookie-respawning': { 
 +      firefox: '68.0 (machine A); 45.0.1 (machine B)', 
 +      quote: 'setup table: "Firefox version 68.0 45.0.1 ... OpenWPM ver[splice]sion v0.9.0 v0.7.0"', 
 +    }, 
 +    'reproducibility-and-replicability-of-web-measurement-studies': { 
 +      firefox: '88', 
 +      quote: 'We use the popular Open-WPM Framework [21] (v0.15.0 - Firefox version 88) to perform the Firefox-based measurements',
     },     },
     'cookiegraph-understanding-and-detecting-first-party-tracking-cookies': {     'cookiegraph-understanding-and-detecting-first-party-tracking-cookies': {
       firefox: '102',       firefox: '102',
       quote: 'We use OpenWPM (v0.17.0) and Firefox (v102) [52] to crawl a sample of 20K out of the top-million websites',       quote: 'We use OpenWPM (v0.17.0) and Firefox (v102) [52] to crawl a sample of 20K out of the top-million websites',
 +    },
 +    'on-the-similarity-of-web-measurements-under-different-experimental-setups': {
 +      firefox: '95.0 and 86.0.1',
 +      note: 'ran two browsers deliberately, as an experimental variable; v95.0 is the one v0.18.0 pins',
 +      quote: 'we used the most recent stable Firefox version available when we started the experiment (v95.0; release date 12/2021) and a version that is roughly one year older (v86.0.1; 02/2021)',
 +    },
 +    'a-large-scale-study-of-cookie-banner-interaction-tools-and-their-impact-on-users': {
 +      firefox: '100',
 +      quote: 'Each profile is based on OpenWPM [12] (v0.20.0), uses the Firefox browser (v100.0 with the default user agent1 and a screen resolution of 1920x1080)',
     },     },
     'purl-safe-and-effective-sanitization-of-link-decoration': {     'purl-safe-and-effective-sanitization-of-link-decoration': {
Line 416: Line 445:
       quote: 'We use OpenWPM (v0.17.0) [11] and Firefox (v102) [64] for crawling',       quote: 'We use OpenWPM (v0.17.0) [11] and Firefox (v102) [64] for crawling',
     },     },
 +    'intractable-cookie-crumbs-unveiling-the-nexus-of-stateful-banner-interaction-and': {
 +      firefox: '121',
 +      quote: 'BannerClick is built on top of OpenWPM [24] (version 0.26.0), which uses Fire[splice]fox v121.0 with TCP disabled',
 +    },
 +    'understanding-regional-filter-lists-efficacy-and-impact': {
 +      firefox: '123',
 +      quote: 'Each measurement uses OpenWPM [24] (v0.27.0), which uses the Firefox browser (Version 123.0), with the user agent (Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0)',
 +    },
 +  };
 +  // Wide-probe hits NOT in the map, with the reason. A rejection nobody can see
 +  // is a rejection nobody can check.
 +  const FIREFOX_REJECTED = {
 +    'evaluating-practical-quic-website-fingerprinting-defenses-for-the-masses':
 +      'names the crawl browser without a version ("crawling the pages in MAIN with OpenWPM (v0.17.0) [1] using Firefox five consecutive times"); the "Firefox 88.0"/"98.0" hits are website-fingerprinting client comparisons (MAIN/TIME/LARGE/FIREFOX/HET), not the crawl',
 +    'automating-cookie-consent-and-gdpr-violation-detection':
 +      'the only "Firefox 86" in the paper is a BIBLIOGRAPHY TITLE ("Firefox 86 introduces total cookie protection"); the methodology says only "the OpenWPM framework, version 0.12.0 [16, 35], which runs multiple concurrent Firefox browser instances"',
   };   };
 +  const major = (s) => String(s).split('.')[0];
 +  const stateCount = Object.keys(FIREFOX_STATED).length;
   p('');   p('');
-  p(`Of those ${statedPapers.size} papers, ${Object.keys(FIREFOX_STATED).length} also state which Firefox the crawl ran,`); +  p(`Of those ${statedPapers.size} papers, ${stateCount} also state which Firefox the crawl ran. Hand-read from a wide`); 
-  p(`hand-read rather than swept (the regex sweep returns 6 and is dominated by`); +  p(`sweep over all ${statedPapers.size}; the hits the map rejects are listed after the table.`); 
-  p(`citation-marker splices). The other ${statedPapers.size - Object.keys(FIREFOX_STATED).length} leave the browser to be inferred from the pin:`);+  p(''); 
 +  let ffMatch = 0, ffDiffer = 0, ffNoPin = 0;
   for (const [slug, v] of Object.entries(FIREFOX_STATED)) {   for (const [slug, v] of Object.entries(FIREFOX_STATED)) {
     const paper = OPENWPM.find((x) => x.slug === slug);     const paper = OPENWPM.find((x) => x.slug === slug);
     const ow = openwpmTuples(paper).map((t) => t.version).filter(Boolean)[0];     const ow = openwpmTuples(paper).map((t) => t.version).filter(Boolean)[0];
     const nominal = FIREFOX_OF[normalise(ow ?? '')] ?? '?';     const nominal = FIREFOX_OF[normalise(ow ?? '')] ?? '?';
 +    // The paper may state more than one browser; compare on any major it states.
 +    const stated = String(v.firefox).match(/\d+(?:\.\d+)*/g) ?? [];
 +    let verdict;
 +    if (nominal === '?') { verdict = 'NO PIN to compare (pre-0.10 release)'; ffNoPin++; }
 +    else if (stated.some((s) => major(s) === major(nominal))) { verdict = 'MATCHES the pin'; ffMatch++; }
 +    else { verdict = `DIFFERS from the pin (${nominal})`; ffDiffer++; }
     p(`  ${paper.year} ${paper.venue}/${slug}`);     p(`  ${paper.year} ${paper.venue}/${slug}`);
-    p(`     OpenWPM ${ow} (ships Firefox ${nominal}) — paper states Firefox ${v.firefox}`);+    p(`     OpenWPM ${ow} (ships Firefox ${nominal}) — paper states Firefox ${v.firefox} — ${verdict}`); 
 +    if (v.note) p(`     note: ${v.note}`);
     p(`     "${v.quote}"`);     p(`     "${v.quote}"`);
   }   }
   p('');   p('');
-  p('Two of the three ran a NEWER browser than their OpenWPM release ships, so the'); +  p('  Wide-probe hits REJECTED from the map:'); 
-  p('bundled-Firefox column is nominal: OpenWPM launches whatever FIREFOX_BINARY'); +  for (const [slug, why] of Object.entries(FIREFOX_REJECTED)) { 
-  p('points at. 12 of the 15 do not say, and cannot be checked.');+    p(`    ${slug}`); 
 +    p(`       ${why}`); 
 +  } 
 +  p(''); 
 +  p(`Split of the ${stateCount}: ${ffMatch} ran the Firefox their release pins, ${ffDiffer} ran a different one,`); 
 +  p(`${ffNoPin} used a pre-0.10 release that has no pin to compare against. So of the ${ffMatch + ffDiffer}`); 
 +  p(`that can be checked against a pin, ${ffDiffer} do not match it, and the bundled-Firefox column`); 
 +  p('is nominal: OpenWPM launches whatever FIREFOX_BINARY points at.'); 
 +  p(`${statedPapers.size - stateCount} of the ${statedPapers.size} do not say which browser they ran, and cannot be checked.`);
   p('');   p('');
   p('"Releases behind" counts tagged releases between the version the paper used');   p('"Releases behind" counts tagged releases between the version the paper used');
Line 764: Line 827:
  
   h('Z. External figures (not from the corpus) — re-verify before republishing');   h('Z. External figures (not from the corpus) — re-verify before republishing');
-  p(`  OpenWPM release 0.35.0, tagged 2026-06-17 — repo VERSION file and git tag`); +  p(`  OpenWPM release 0.36.0, tagged 2026-08-24, bundling Firefox 154 — repo VERSION file and git tag, checked 2026-09-05`); 
-  p(`  geckodriver 0.37.1 released 2026-07-20 (current); environment.yaml pins 0.37.0`);+  p(`  geckodriver 0.37.1 released 2026-07-20 (current); environment.yaml pins 0.37.1 since v0.36.0, 0.37.0 at v0.35.0 and earlier`);
   p(`  conda pins that have no linux-aarch64 build: geckodriver 0.37.0, plyvel 1.5.1, selenium-manager 4.45`);   p(`  conda pins that have no linux-aarch64 build: geckodriver 0.37.0, plyvel 1.5.1, selenium-manager 4.45`);
   p(`  Firefox Nightly used in the ARM substitution attempt: 156 (156.0a1, linux64-aarch64)`);   p(`  Firefox Nightly used in the ARM substitution attempt: 156 (156.0a1, linux64-aarch64)`);
-  p(`  Firefox releases: 152 bundled by OpenWPM 0.35.0; Firefox reached 147 during the 2025 release gap`);+  p(`  Firefox releases: 154 bundled by OpenWPM 0.36.0; Firefox reached 146 during 2025 (147 shipped 2026-01-13) — product-details.mozilla.org`);
   p(`  build tasks in the taskcluster index for the pinned revision: 202 (mozilla-release), 185 (mozilla-central)`);   p(`  build tasks in the taskcluster index for the pinned revision: 202 (mozilla-release), 185 (mozilla-central)`);
   p(`  Firefox 52 (52.9) — the browser version stated by ahmad2020_apophanies alongside OpenWPM 0.8; that paper predates the 0.10 architecture`);   p(`  Firefox 52 (52.9) — the browser version stated by ahmad2020_apophanies alongside OpenWPM 0.8; that paper predates the 0.10 architecture`);
-  p(`  GitHub issues referenced: #557 callstack, #503 no Windows, #101 tracking_protection, #448 display_mode`);+  p(`  GitHub issues referenced: #557 callstack, #503 no Windows, #101 tracking_protection, #448 display_mode, #964 expired Firefox builds`); 
 +  p(`  GitHub repo state 2026-09-05: 1,419 stars, 334 forks, 141 open issues + 39 open PRs (open_issues_count=180 counts both)`); 
 +  p(`  commits in 2026 to 61d285f: 73 by author date, all by vringar; 74 by committer date`); 
 +  p(`  ALSAcnc OpenWPM pins in public history: v0.23.0, v0.29.0, v0.31.0 (main), v0.34.0 (branch cnil, 4acb3fd 2026-07-10)`); 
 +  p(`  ALSAcnc monkey_patches/ line counts, identical on main and cnil: 23, 130, 194, 199, 37 = 583 lines in 5 files`); 
 +  p(`  Firefox 153.0.3: its tagged revision has no unbranded builds on any platform, so OpenWPM skipped 153 (CHANGELOG v0.36.0)`); 
 +  p(`  geckodriver pins: 0.37.0 in environment.yaml up to and including v0.35.0; 0.37.1 from v0.36.0`); 
 +  p(`  Docker Hub openwpm/openwpm oldest tags: v0.10.0/v0.11.0/v0.12.0/v0.13.0 (v-prefixed) and 0.14.1; 0.14.0 is absent`);
   p(`  Bugzilla 634073 — cached image headers not recorded`);   p(`  Bugzilla 634073 — cached image headers not recorded`);
   p(`  openwpm.xpi built here: 146,356 bytes; empty crawl database: 69,632 bytes with 14 tables, 0 rows`);   p(`  openwpm.xpi built here: 146,356 bytes; empty crawl database: 69,632 bytes with 14 tables, 0 rows`);
 +  p(`  releases from v0.10.0 inclusive: 30 tags; the pinned Firefox still downloads for the five 2026 ones (148-154)`);
   p(`  pytest -m pyonly: 14 passed, 135 deselected in 0.13s`);   p(`  pytest -m pyonly: 14 passed, 135 deselected in 0.13s`);
   p(`  browser id in the quoted failure log: 80873956`);   p(`  browser id in the quoted failure log: 80873956`);
Line 780: Line 851:
   p(`  "a crawl of 100,000 sites" — rhetorical scale, not a measurement`);   p(`  "a crawl of 100,000 sites" — rhetorical scale, not a measurement`);
   p(`  62 — a print() column width inside pages/openwpm_defaults.py, not a figure`);   p(`  62 — a print() column width inside pages/openwpm_defaults.py, not a figure`);
 +  p(`  Ubuntu 24.04 (CI) and 22.04 (the container image base) — README, Installation section`);
 +  p(`  content_hash is SHA-256 — Extension/src/lib/sha256.ts; docs/Configuration.md lines 419-420 still say md5`);
 +  p(`  73 commits in 2026 to the pinned commit b9dd4c3, 72 of them by one author`);
 +  p(`  instance_id appears in openwpm/storage/arrow_storage.py only; 0 occurrences in schema.sql`);
 +  p(`  the two Xvfb papers of the 60: CCS 2016 scored headless, WWW 2020 (zeber) scored headful`);
   p(`  917,261 and 94,144 — the site counts of englehardt2016online's "Default Stateless 1 Million"`);   p(`  917,261 and 94,144 — the site counts of englehardt2016online's "Default Stateless 1 Million"`);
   p(`       and "Default Stateful 100,000" configurations, from that paper's configuration table`);   p(`       and "Default Stateful 100,000" configurations, from that paper's configuration table`);
Line 930: Line 1006:
 0.34.0           2026-05-08  Firefox 150 0.34.0           2026-05-08  Firefox 150
 0.35.0           2026-06-17  Firefox 152 0.35.0           2026-06-17  Firefox 152
 +0.36.0           2026-08-24  Firefox 154
 +
 +Of those 15 papers, 9 also state which Firefox the crawl ran. Hand-read from a wide
 +sweep over all 15; the hits the map rejects are listed after the table.
  
-Of those 15 papers, 3 also state which Firefox the crawl ran, 
-hand-read rather than swept (the regex sweep returns 6 and is dominated by 
-citation-marker splices). The other 12 leave the browser to be inferred from the pin: 
   2020 WWW/apophanies-or-epiphanies-how-crawlers-impact-our-understanding-of-the-web   2020 WWW/apophanies-or-epiphanies-how-crawlers-impact-our-understanding-of-the-web
-     OpenWPM 0.8; Firefox 52.9 (ships Firefox ?) — paper states Firefox 52.9 +     OpenWPM 0.8; Firefox 52.9 (ships Firefox ?) — paper states Firefox 52.9 — NO PIN to compare (pre-0.10 release) 
-     "version string recorded by the extraction as "0.8; Firefox 52.9"; consistent with the firefox-52 era pin"+     "at the time of our experiments Open-WPM was unable to drive Firefox versions later than v52.9, while Selenium drove Firefox v66.0 (and Table 3: "OpenWPM [28] 0.8 | Selenium v3.6, Firefox v52.9")" 
 +  2022 PETS/my-cookie-is-a-phoenix-detection-measurement-and-lawfulness-of-cookie-respawning 
 +     OpenWPM 0.9.0 (machine A); 0.7.0 (machine B) (ships Firefox ?) — paper states Firefox 68.0 (machine A); 45.0.1 (machine B) — NO PIN to compare (pre-0.10 release) 
 +     "setup table: "Firefox version 68.0 45.0.1 ... OpenWPM ver[splice]sion v0.9.0 v0.7.0"" 
 +  2022 WWW/reproducibility-and-replicability-of-web-measurement-studies 
 +     OpenWPM v0.15.0 (ships Firefox 88) — paper states Firefox 88 — MATCHES the pin 
 +     "We use the popular Open-WPM Framework [21] (v0.15.0 - Firefox version 88) to perform the Firefox-based measurements"
   2023 CCS/cookiegraph-understanding-and-detecting-first-party-tracking-cookies   2023 CCS/cookiegraph-understanding-and-detecting-first-party-tracking-cookies
-     OpenWPM 0.17.0 (ships Firefox 90) — paper states Firefox 102+     OpenWPM 0.17.0 (ships Firefox 90) — paper states Firefox 102 — DIFFERS from the pin (90)
      "We use OpenWPM (v0.17.0) and Firefox (v102) [52] to crawl a sample of 20K out of the top-million websites"      "We use OpenWPM (v0.17.0) and Firefox (v102) [52] to crawl a sample of 20K out of the top-million websites"
 +  2023 IMC/on-the-similarity-of-web-measurements-under-different-experimental-setups
 +     OpenWPM v0.18.0 (ships Firefox 95) — paper states Firefox 95.0 and 86.0.1 — MATCHES the pin
 +     note: ran two browsers deliberately, as an experimental variable; v95.0 is the one v0.18.0 pins
 +     "we used the most recent stable Firefox version available when we started the experiment (v95.0; release date 12/2021) and a version that is roughly one year older (v86.0.1; 02/2021)"
 +  2024 PETS/a-large-scale-study-of-cookie-banner-interaction-tools-and-their-impact-on-users
 +     OpenWPM 0.20.0 (ships Firefox 100) — paper states Firefox 100 — MATCHES the pin
 +     "Each profile is based on OpenWPM [12] (v0.20.0), uses the Firefox browser (v100.0 with the default user agent1 and a screen resolution of 1920x1080)"
   2024 USENIX/purl-safe-and-effective-sanitization-of-link-decoration   2024 USENIX/purl-safe-and-effective-sanitization-of-link-decoration
-     OpenWPM v0.17.0 (ships Firefox 90) — paper states Firefox 102+     OpenWPM v0.17.0 (ships Firefox 90) — paper states Firefox 102 — DIFFERS from the pin (90)
      "We use OpenWPM (v0.17.0) [11] and Firefox (v102) [64] for crawling"      "We use OpenWPM (v0.17.0) [11] and Firefox (v102) [64] for crawling"
 +  2025 PETS/intractable-cookie-crumbs-unveiling-the-nexus-of-stateful-banner-interaction-and
 +     OpenWPM 0.26.0 (ships Firefox 121) — paper states Firefox 121 — MATCHES the pin
 +     "BannerClick is built on top of OpenWPM [24] (version 0.26.0), which uses Fire[splice]fox v121.0 with TCP disabled"
 +  2025 PETS/understanding-regional-filter-lists-efficacy-and-impact
 +     OpenWPM 0.27.0 (ships Firefox 122) — paper states Firefox 123 — DIFFERS from the pin (122)
 +     "Each measurement uses OpenWPM [24] (v0.27.0), which uses the Firefox browser (Version 123.0), with the user agent (Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0)"
 +
 +  Wide-probe hits REJECTED from the map:
 +    evaluating-practical-quic-website-fingerprinting-defenses-for-the-masses
 +       names the crawl browser without a version ("crawling the pages in MAIN with OpenWPM (v0.17.0) [1] using Firefox five consecutive times"); the "Firefox 88.0"/"98.0" hits are website-fingerprinting client comparisons (MAIN/TIME/LARGE/FIREFOX/HET), not the crawl
 +    automating-cookie-consent-and-gdpr-violation-detection
 +       the only "Firefox 86" in the paper is a BIBLIOGRAPHY TITLE ("Firefox 86 introduces total cookie protection"); the methodology says only "the OpenWPM framework, version 0.12.0 [16, 35], which runs multiple concurrent Firefox browser instances"
  
-Two of the three ran a NEWER browser than their OpenWPM release ships, so the +Split of the 9: 4 ran the Firefox their release pins, 3 ran a different one, 
-bundled-Firefox column is nominal: OpenWPM launches whatever FIREFOX_BINARY +2 used a pre-0.10 release that has no pin to compare against. So of the 7 
-points at. 12 of the 15 do not say, and cannot be checked.+that can be checked against a pin, 3 do not match it, and the bundled-Firefox column 
 +is nominal: OpenWPM launches whatever FIREFOX_BINARY points at. 
 +6 of the 15 do not say which browser they ran, and cannot be checked.
  
 "Releases behind" counts tagged releases between the version the paper used "Releases behind" counts tagged releases between the version the paper used
Line 1328: Line 1432:
 === Z. External figures (not from the corpus) — re-verify before republishing === === Z. External figures (not from the corpus) — re-verify before republishing ===
  
-  OpenWPM release 0.35.0, tagged 2026-06-17 — repo VERSION file and git tag +  OpenWPM release 0.36.0, tagged 2026-08-24, bundling Firefox 154 — repo VERSION file and git tag, checked 2026-09-05 
-  geckodriver 0.37.1 released 2026-07-20 (current); environment.yaml pins 0.37.0+  geckodriver 0.37.1 released 2026-07-20 (current); environment.yaml pins 0.37.1 since v0.36.0, 0.37.0 at v0.35.0 and earlier
   conda pins that have no linux-aarch64 build: geckodriver 0.37.0, plyvel 1.5.1, selenium-manager 4.45   conda pins that have no linux-aarch64 build: geckodriver 0.37.0, plyvel 1.5.1, selenium-manager 4.45
   Firefox Nightly used in the ARM substitution attempt: 156 (156.0a1, linux64-aarch64)   Firefox Nightly used in the ARM substitution attempt: 156 (156.0a1, linux64-aarch64)
-  Firefox releases: 152 bundled by OpenWPM 0.35.0; Firefox reached 147 during the 2025 release gap+  Firefox releases: 154 bundled by OpenWPM 0.36.0; Firefox reached 146 during 2025 (147 shipped 2026-01-13) — product-details.mozilla.org
   build tasks in the taskcluster index for the pinned revision: 202 (mozilla-release), 185 (mozilla-central)   build tasks in the taskcluster index for the pinned revision: 202 (mozilla-release), 185 (mozilla-central)
   Firefox 52 (52.9) — the browser version stated by ahmad2020_apophanies alongside OpenWPM 0.8; that paper predates the 0.10 architecture   Firefox 52 (52.9) — the browser version stated by ahmad2020_apophanies alongside OpenWPM 0.8; that paper predates the 0.10 architecture
-  GitHub issues referenced: #557 callstack, #503 no Windows, #101 tracking_protection, #448 display_mode+  GitHub issues referenced: #557 callstack, #503 no Windows, #101 tracking_protection, #448 display_mode, #964 expired Firefox builds 
 +  GitHub repo state 2026-09-05: 1,419 stars, 334 forks, 141 open issues + 39 open PRs (open_issues_count=180 counts both) 
 +  commits in 2026 to 61d285f: 73 by author date, all by vringar; 74 by committer date 
 +  ALSAcnc OpenWPM pins in public history: v0.23.0, v0.29.0, v0.31.0 (main), v0.34.0 (branch cnil, 4acb3fd 2026-07-10) 
 +  ALSAcnc monkey_patches/ line counts, identical on main and cnil: 23, 130, 194, 199, 37 = 583 lines in 5 files 
 +  Firefox 153.0.3: its tagged revision has no unbranded builds on any platform, so OpenWPM skipped 153 (CHANGELOG v0.36.0) 
 +  geckodriver pins: 0.37.0 in environment.yaml up to and including v0.35.0; 0.37.1 from v0.36.0 
 +  Docker Hub openwpm/openwpm oldest tags: v0.10.0/v0.11.0/v0.12.0/v0.13.0 (v-prefixed) and 0.14.1; 0.14.0 is absent
   Bugzilla 634073 — cached image headers not recorded   Bugzilla 634073 — cached image headers not recorded
   openwpm.xpi built here: 146,356 bytes; empty crawl database: 69,632 bytes with 14 tables, 0 rows   openwpm.xpi built here: 146,356 bytes; empty crawl database: 69,632 bytes with 14 tables, 0 rows
 +  releases from v0.10.0 inclusive: 30 tags; the pinned Firefox still downloads for the five 2026 ones (148-154)
   pytest -m pyonly: 14 passed, 135 deselected in 0.13s   pytest -m pyonly: 14 passed, 135 deselected in 0.13s
   browser id in the quoted failure log: 80873956   browser id in the quoted failure log: 80873956
Line 1344: Line 1456:
   "a crawl of 100,000 sites" — rhetorical scale, not a measurement   "a crawl of 100,000 sites" — rhetorical scale, not a measurement
   62 — a print() column width inside pages/openwpm_defaults.py, not a figure   62 — a print() column width inside pages/openwpm_defaults.py, not a figure
 +  Ubuntu 24.04 (CI) and 22.04 (the container image base) — README, Installation section
 +  content_hash is SHA-256 — Extension/src/lib/sha256.ts; docs/Configuration.md lines 419-420 still say md5
 +  73 commits in 2026 to the pinned commit b9dd4c3, 72 of them by one author
 +  instance_id appears in openwpm/storage/arrow_storage.py only; 0 occurrences in schema.sql
 +  the two Xvfb papers of the 60: CCS 2016 scored headless, WWW 2020 (zeber) scored headful
   917,261 and 94,144 — the site counts of englehardt2016online's "Default Stateless 1 Million"   917,261 and 94,144 — the site counts of englehardt2016online's "Default Stateless 1 Million"
        and "Default Stateful 100,000" configurations, from that paper's configuration table        and "Default Stateful 100,000" configurations, from that paper's configuration table
Line 1365: Line 1482:
 All **65** evidence quotes attached to an OpenWPM ''tools[]'' tuple anywhere in the corpus were checked, twice, by two different methods. All **65** evidence quotes attached to an OpenWPM ''tools[]'' tuple anywhere in the corpus were checked, twice, by two different methods.
  
-**First pass, ''scripts/quote_check.mjs --tools "open[ -]?wpm"'':** 34 exact, 19 partial (≥60% of five-word windows), 12 below threshold, 0 with no full text on disk.+**First pass, ''scripts/quote_check.mjs %%--tools%% "open[ -]?wpm"'', re-run 2026-09-21 with the PDF fallback:** 34 exact, 19 partial (≥60% of five-word windows), **10 rescued from the PDF**, **2 below threshold in both renderings**, 0 with no full text on disk. See //Quote-check refresh, 2026-09-21// at the foot of this page.
  
-**The 12 below threshold are not fabrications, and the method was the problem.** Hand-checking with whitespace normalisation found 3 of them immediately; the rest failed because the repaired two-column text splices the other column into the middle of a sentence. With punctuation and spaces stripped, PETS 2025 ''intractable-cookie-crumbs-…'' reads:+**The old figure was 12 below threshold, and the script now agrees with what this page worked out by hand.** 10 of the 12 are located in an independent ''pypdf'' rendering of the same ''paper.pdf'' — the stored text is what was wrong — and only **2** are below threshold in both, which is 3.1% of the 65. ''exact'' (34) and ''partial'' (19) did not move. 
 + 
 +**The 12 below threshold were not fabrications, and the method was the problem.** Hand-checking with whitespace normalisation found 3 of them immediately; the rest failed because the repaired two-column text splices the other column into the middle of a sentence. With punctuation and spaces stripped, PETS 2025 ''intractable-cookie-crumbs-…'' reads:
  
 <code> <code>
Line 1373: Line 1492:
 </code> </code>
  
-"Firefox v121.0" //is// in the paper; the word ''firefox'' is cut in half. A whole-quote containment test scores that quote as absent.+"Firefox v121.0" //is// in the paper; the word ''firefox'' is cut in half. A whole-quote containment test scores that quote as absent. **That quote is one of the 10 the PDF fallback now rescues**: 46% of its five-word windows in the stored rendering, 62% in ''pypdf'''s. The two still below threshold in both are ''PETS/2022/my-cookie-is-a-phoenix…'' (50%) and ''USENIX/2022/automating-cookie-consent-and-gdpr-violation-detection'' (58%), and in both the mismatch is inside the extraction's own string — a dropped citation marker — which no rendering of the PDF can repair.
  
 **Second pass, ''scripts/openwpm_quotecheck.py'', written for this page.** Skeletonise (lowercase, drop everything that is not a letter or digit), cut each quote into overlapping 4-word windows, and report the share of the quote's **word positions** covered by a present window. Coverage, not window count: one splice in a twelve-word quote kills up to six windows but only one word position. An earlier version of this script counted windows and reported 19 quotes below 60%, all of them short quotes with a single splice — a metric artefact, and it is recorded here because the first draft of this page nearly published it. **Second pass, ''scripts/openwpm_quotecheck.py'', written for this page.** Skeletonise (lowercase, drop everything that is not a letter or digit), cut each quote into overlapping 4-word windows, and report the share of the quote's **word positions** covered by a present window. Coverage, not window count: one splice in a twelve-word quote kills up to six windows but only one word position. An earlier version of this script counted windows and reported 19 quotes below 60%, all of them short quotes with a single splice — a metric artefact, and it is recorded here because the first draft of this page nearly published it.
Line 1518: Line 1637:
   - **The "both" statefulness label was doubted, and the doubt was wrong.** Five OPENWPM papers have ''crawlConfig.statefulness == "both"''. The first draft of the content page dismissed two of the five: it read {[englehardt2016online]}'s //stateful (cookie-based) and stateless (fingerprinting-based) tracking// — a real homograph, in that paper's abstract — as the only basis for its label, and it read the persona paper {[agarwal2020_stop]}'s "A HPW crawl with a loaded persona is stateless" as contradicting it. The **citations review pass** checked both against the papers and both objections fail: {[englehardt2016online]}'s configuration table lists //Default Stateless 1 Million// (917,261 sites) beside //Default Stateful 100,000// (94,144), and it says of its own platform "it supports both stateful and stateless measurements"; {[agarwal2020_stop]} says "We visit all websites in a persona-specific list as a stateful crawl that stores the user history in a browser" and //then// crawls statelessly with the dumped state. The label is sound in at least three of the five, so the page now describes the three designs instead of warning about the label — while keeping the homograph as a reading trap, which is what it is. **Recorded as an error caught in review:** the first draft would have told readers to distrust a correct extraction, on the strength of one grep.   - **The "both" statefulness label was doubted, and the doubt was wrong.** Five OPENWPM papers have ''crawlConfig.statefulness == "both"''. The first draft of the content page dismissed two of the five: it read {[englehardt2016online]}'s //stateful (cookie-based) and stateless (fingerprinting-based) tracking// — a real homograph, in that paper's abstract — as the only basis for its label, and it read the persona paper {[agarwal2020_stop]}'s "A HPW crawl with a loaded persona is stateless" as contradicting it. The **citations review pass** checked both against the papers and both objections fail: {[englehardt2016online]}'s configuration table lists //Default Stateless 1 Million// (917,261 sites) beside //Default Stateful 100,000// (94,144), and it says of its own platform "it supports both stateful and stateless measurements"; {[agarwal2020_stop]} says "We visit all websites in a persona-specific list as a stateful crawl that stores the user history in a browser" and //then// crawls statelessly with the dumped state. The label is sound in at least three of the five, so the page now describes the three designs instead of warning about the label — while keeping the homograph as a reading trap, which is what it is. **Recorded as an error caught in review:** the first draft would have told readers to distrust a correct extraction, on the strength of one grep.
   - **No causal claim about the ARM extension failure.** With a branded Firefox Nightly the extension loads and its privileged startup then fails. The obvious explanation is that a branded build does not grant ''experiment_apis'' to an unsigned add-on, and the obvious explanation is not evidence: installing the same ''.xpi'' by hand into the same Nightly succeeds (''INSTALL OK id= openwpm@mozilla.org''), which shows loading is not the problem but does not identify what is. Three probes were run and none settled it: a patched ''.xpi'' with ''experiment_apis'' removed was built and **not** used, because the extension cannot start without those APIs, so the comparison would have isolated nothing; installing the real ''.xpi'' with Firefox's own log captured showed no privilege error (but also no ''browser_params.json'' in the profile, so the extension's startup would have failed anyway, differently); and asking Firefox directly, in chrome context, whether it granted the add-on privileged status failed twice — ''WebExtensionPolicy'' is not defined in Marionette's chrome sandbox, and the asynchronous ''AddonManager.getAddonByID'' form timed out. The page reports the failure and says the cause was not isolated.   - **No causal claim about the ARM extension failure.** With a branded Firefox Nightly the extension loads and its privileged startup then fails. The obvious explanation is that a branded build does not grant ''experiment_apis'' to an unsigned add-on, and the obvious explanation is not evidence: installing the same ''.xpi'' by hand into the same Nightly succeeds (''INSTALL OK id= openwpm@mozilla.org''), which shows loading is not the problem but does not identify what is. Three probes were run and none settled it: a patched ''.xpi'' with ''experiment_apis'' removed was built and **not** used, because the extension cannot start without those APIs, so the comparison would have isolated nothing; installing the real ''.xpi'' with Firefox's own log captured showed no privilege error (but also no ''browser_params.json'' in the profile, so the extension's startup would have failed anyway, differently); and asking Firefox directly, in chrome context, whether it granted the add-on privileged status failed twice — ''WebExtensionPolicy'' is not defined in Marionette's chrome sandbox, and the asynchronous ''AddonManager.getAddonByID'' form timed out. The page reports the failure and says the cause was not isolated.
-  - **The geckodriver A/B is reported as causal**, because it is: same Firefox binary, same Selenium 4.45.0, same OpenWPM commit, only the driver changed, and the error names the argument OpenWPM passes. Attempt 1 (0.37.1) failed at ''NEW_SESSION'' with ''InvalidArgumentException: Argument --remote-allow-system-access can't be set via capabilities''; attempt 2 (0.37.0) got past that point and failed later and differently.+  - **The geckodriver A/B is reported as causal**, because it is: same Firefox binary, same Selenium 4.45.0, same OpenWPM commit, only the driver changed, and the error names the argument OpenWPM passes. Attempt 1 (0.37.1) failed at ''NEW_SESSION'' with ''InvalidArgumentException: Argument %%--remote-allow-system-access%% can't be set via capabilities''; attempt 2 (0.37.0) got past that point and failed later and differently.
   - **''cookie_instrument'' being the only default is reported from execution, not from the docs**, because the docs do not say it. Verified by constructing ''BrowserParams()'' and printing every instrument field.   - **''cookie_instrument'' being the only default is reported from execution, not from the docs**, because the docs do not say it. Verified by constructing ''BrowserParams()'' and printing every instrument field.
   - **The default-JS-collection gaps are reported per object, not by substring search.** An earlier draft of ''openwpm_defaults.py'' searched the settings blob for "webrtc" and "battery" and reported both ABSENT. Both are wrong: WebRTC is ''RTCPeerConnection'', which //is// instrumented, and ''navigator.getBattery'' is covered by instrumenting ''window.navigator''. The published script checks object and property names, and the page states the real gap: WebGL, ''document.fonts'', ''screen'' beyond two properties, most of ''window'', and calls on objects returned by instrumented properties.   - **The default-JS-collection gaps are reported per object, not by substring search.** An earlier draft of ''openwpm_defaults.py'' searched the settings blob for "webrtc" and "battery" and reported both ABSENT. Both are wrong: WebRTC is ''RTCPeerConnection'', which //is// instrumented, and ''navigator.getBattery'' is covered by instrumenting ''window.navigator''. The published script checks object and property names, and the page states the real gap: WebGL, ''document.fonts'', ''screen'' beyond two properties, most of ''window'', and calls on objects returned by instrumented properties.
Line 1631: Line 1750:
  
 <file text openwpm-platform-probe.txt> <file text openwpm-platform-probe.txt>
- 
 == 1. Which unbranded (add-on-devel) builds exist for the pinned Firefox == 1. Which unbranded (add-on-devel) builds exist for the pinned Firefox
 202 build tasks for this revision 202 build tasks for this revision
Line 1642: Line 1760:
  
 == 3. Repository state == 3. Repository state
-stars 1415  forks 332  open issues 180  archived False  pushed 2026-08-06T19:53:23Z+stars 1419  forks 334  open issues 180  archived False  pushed 2026-08-25T17:39:07Z
  
 == 4. Releases, newest first == 4. Releases, newest first
 +  v0.36.0   2026-08-24  by vringar
   v0.35.0   2026-06-17  by vringar   v0.35.0   2026-06-17  by vringar
   v0.34.0   2026-05-08  by vringar   v0.34.0   2026-05-08  by vringar
Line 1652: Line 1771:
   v0.30.0   2024-10-06  by vringar   v0.30.0   2024-10-06  by vringar
   v0.29.0   2024-07-20  by vringar   v0.29.0   2024-07-20  by vringar
-  v0.28.0   2024-02-21  by vringar 
  
 == 5. Scheduled CI on master: does it still pass == 5. Scheduled CI on master: does it still pass
-  2026-08-13 Tests and linting            schedule  success +  2026-09-05 Tests and linting            schedule  success 
-  2026-08-11 Tests and linting            schedule  success +  2026-09-03 Tests and linting            schedule  success 
-  2026-08-09 Tests and linting            schedule  success +  2026-09-01 Tests and linting            schedule  success 
-  2026-08-08 CodeQL                       schedule  success +  2026-08-31 Tests and linting            schedule  success 
-  2026-08-07 Tests and linting            schedule  success +  2026-08-29 CodeQL                       schedule  success 
-  2026-08-05 Tests and linting            schedule  success+  2026-08-29 Tests and linting            schedule  success
  
 == 6. Published container image: which architectures == 6. Published container image: which architectures
-  latest         2026-08-02  ['amd64']+  latest         2026-08-24  ['amd64'] 
 +  0.36.0         2026-08-24  ['amd64'] 
 +  61d285fccc1c   2026-08-24  ['amd64']
   b9dd4c3a09d2   2026-08-02  ['amd64']   b9dd4c3a09d2   2026-08-02  ['amd64']
-  25a7827f8dfd   2026-07-17  ['amd64'] 
-  6ca11b1a8b97   2026-07-11  ['amd64'] 
  
 == 7. conda-forge: are the pinned packages available for linux-aarch64 == 7. conda-forge: are the pinned packages available for linux-aarch64
Line 1673: Line 1791:
   plyvel             linux-64       58 builds   plyvel             linux-64       58 builds
   plyvel             linux-aarch64  0 builds   plyvel             linux-aarch64  0 builds
-  selenium-manager   linux-64       39 builds+  selenium-manager   linux-64       40 builds
   selenium-manager   linux-aarch64  0 builds   selenium-manager   linux-aarch64  0 builds
  
Line 1690: Line 1808:
 | sites go to "the first browser available" | ''openwpm/task_manager.py'', ''execute_command_sequence'' | | sites go to "the first browser available" | ''openwpm/task_manager.py'', ''execute_command_sequence'' |
 | ''bot_mitigation'' = up to 10 random mouse moves, one scroll, random 1–7 s sleep | ''openwpm/commands/browser_commands.py'' (''NUM_MOUSE_MOVES = 10'', ''RANDOM_SLEEP_LOW/HIGH = 1/7'') | | ''bot_mitigation'' = up to 10 random mouse moves, one scroll, random 1–7 s sleep | ''openwpm/commands/browser_commands.py'' (''NUM_MOUSE_MOVES = 10'', ''RANDOM_SLEEP_LOW/HIGH = 1/7'') |
-| stateful/stateless semantics, ''seed_tar'' behaviour, ''failure_limit'', watchdogs, screenshot limits, ''save_content'' MD5 de-duplication, ''tracking_protection'' NOT SUPPORTED | ''docs/Configuration.md'' |+| stateful/stateless semantics, ''seed_tar'' behaviour, ''failure_limit'', watchdogs, screenshot limits, ''save_content'' de-duplication by content hash, ''tracking_protection'' NOT SUPPORTED | ''docs/Configuration.md''((This row said "MD5 de-duplication" until 2026-09-05. The doc does say md5; the code computes SHA-256, the page was corrected by §11.5 #2, and this log's own row was not — the same carry-over defect, one section apart.)) |
 | the callstack instrument is broken | ''docs/Configuration.md'' + issue #557; confirmed by execution | | the callstack instrument is broken | ''docs/Configuration.md'' + issue #557; confirmed by execution |
 | "Use a versioned release" / "Include the OpenWPM version number" | ''README.md'', //Advice for Measurement Researchers//, quoted verbatim on the page | | "Use a versioned release" / "Include the OpenWPM version number" | ''README.md'', //Advice for Measurement Researchers//, quoted verbatim on the page |
 | ''docs/Papers.rst'' lists 76 studies, none later than 2021 | counted from the file; ''git log -1'' on it gives 2023-09-19 | | ''docs/Papers.rst'' lists 76 studies, none later than 2021 | counted from the file; ''git log -1'' on it gives 2023-09-19 |
-| the fingerprinting collection has not changed since 2020-07-08 | ''git log --follow'' on ''openwpm/js_instrumentation_collections/fingerprinting.json'': two commits, the later one a module rename (''502cd83'', 2020-11-14) |+| the fingerprinting collection has not changed since 2020-07-08 | ''git log %%--follow%%'' on ''openwpm/js_instrumentation_collections/fingerprinting.json'': two commits, the later one a module rename (''502cd83'', 2020-11-14) |
 | commits per year, 63 contributors, 72 of 73 2026 commits by one author | ''git log'' on a full (unshallowed) clone | | commits per year, 63 contributors, 72 of 73 2026 commits by one author | ''git log'' on a full (unshallowed) clone |
  
Line 1702: Line 1820:
  
 Host: aarch64 Linux container, x86-64 binaries not executable (no ''/lib64/ld-linux-x86-64.so.2''), no conda, no Docker, no Firefox, Python 3.11, Node 22. Host: aarch64 Linux container, x86-64 binaries not executable (no ''/lib64/ld-linux-x86-64.so.2''), no conda, no Docker, no Firefox, Python 3.11, Node 22.
 +
 +Everything below was run against the repository at ''b9dd4c3'' **unmodified**: after all of it, ''git status %%--porcelain%%'' reports only untracked additions — the two test scripts copied in from ''pages/'', the downloaded copy of the published one, and the crawl output directory. No tracked file was touched, so no claim here is about a patched OpenWPM.
  
 ^ # ^ Command ^ Result ^ ^ # ^ Command ^ Result ^
-| 1 | ''git clone https://github.com/openwpm/OpenWPM'' (then ''git fetch --unshallow'') | ok, commit ''b9dd4c3'', ''VERSION'' 0.35.0 |+| 1 | ''git clone https://github.com/openwpm/OpenWPM'' (then ''git fetch %%--unshallow%%'') | ok, commit ''b9dd4c3'', ''VERSION'' 0.35.0 |
 | 2 | ''micromamba env create -f environment.yaml'' | **failed to solve** — 3 packages have no linux-aarch64 build (output below) | | 2 | ''micromamba env create -f environment.yaml'' | **failed to solve** — 3 packages have no linux-aarch64 build (output below) |
 | 3 | ''micromamba create -n owpm python=3.12 pip leveldb'' then ''pip install'' the imports of ''openwpm/'' | ok; ''pillow'' had to be added after a ''ModuleNotFoundError: PIL'' on import of ''openwpm.commands.browser_commands'' | | 3 | ''micromamba create -n owpm python=3.12 pip leveldb'' then ''pip install'' the imports of ''openwpm/'' | ok; ''pillow'' had to be added after a ''ModuleNotFoundError: PIL'' on import of ''openwpm.commands.browser_commands'' |
Line 1712: Line 1832:
 | 7 | download the pinned unbranded Firefox for ''linux64-aarch64'' | **404** — the build does not exist (probe §1) | | 7 | download the pinned unbranded Firefox for ''linux64-aarch64'' | **404** — the build does not exist (probe §1) |
 | 8 | download Firefox Nightly ''linux64-aarch64'' + geckodriver 0.37.1 for aarch64 | ok — Firefox 156.0a1, geckodriver 0.37.1 | | 8 | download Firefox Nightly ''linux64-aarch64'' + geckodriver 0.37.1 for aarch64 | ok — Firefox 156.0a1, geckodriver 0.37.1 |
-| 9 | ''openwpm_smoke.py'' with geckodriver **0.37.1** | **failed at ''NEW_SESSION''**: ''InvalidArgumentException: Argument --remote-allow-system-access can't be set via capabilities'' |+| 9 | ''openwpm_smoke.py'' with geckodriver **0.37.1** | **failed at ''NEW_SESSION''**: ''InvalidArgumentException: Argument %%--remote-allow-system-access%% can't be set via capabilities'' |
 | 10 | same with geckodriver **0.37.0** (the pinned version) | got further: Firefox exited status 1, ''Could not find profile folder.'' | | 10 | same with geckodriver **0.37.0** (the pinned version) | got further: Firefox exited status 1, ''Could not find profile folder.'' |
-| 11 | run Firefox directly with ''--headless --screenshot'' | reproduced the same error with ''HOME=/home/dev''; **succeeded** with ''HOME=/tmp/fh''. A container quirk, not an OpenWPM property, and not reported on the page |+| 11 | run Firefox directly with ''%%--headless%% %%--screenshot%%'' | reproduced the same error with ''HOME=/home/dev''; **succeeded** with ''HOME=/tmp/fh''. A container quirk, not an OpenWPM property, and not reported on the page |
 | 12 | ''openwpm_smoke.py'' with geckodriver 0.37.0 and ''HOME=/tmp/fh'' | browser launched, **extension loaded**, then ''FileNotFoundError: …/extension_port.txt''; spawn unsuccessful, retried to the failure limit | | 12 | ''openwpm_smoke.py'' with geckodriver 0.37.0 and ''HOME=/tmp/fh'' | browser launched, **extension loaded**, then ''FileNotFoundError: …/extension_port.txt''; spawn unsuccessful, retried to the failure limit |
 | 13 | install the same ''.xpi'' into the same Nightly by hand via Selenium | ''INSTALL OK id= openwpm@mozilla.org'' | | 13 | install the same ''.xpi'' into the same Nightly by hand via Selenium | ''INSTALL OK id= openwpm@mozilla.org'' |
-| 14 | inspect the database the failed crawl produced | 69,632 bytes, **14 tables, all 0 rows** — including ''incomplete_visits'' |+| 14 | inspect the database the failed crawl produced | 69,632 bytes, **13 OpenWPM tables plus SQLite's ''sqlite_sequence'', all 0 rows** — including ''incomplete_visits'' | 
 +| 15 | after publishing: download the page's own %%''<file python openwpm_defaults.py>''%% block from ''/_export/code/programming:crawler:openwpm?codeblock=1'' and run it | reproduces the output quoted beside it **byte for byte**; both published blocks differ from their source files only by a stripped trailing newline | 
 +| 16 | execute every SQL query in ''openwpm_smoke.py'' against the database the failed run produced, and validate its configuration through OpenWPM's own ''validate_browser_params'' | all pass — after the ''is_TRR'' fix in §11.1 S4; before it, one query raised ''no such column: used_trr'' |
  
 Step 2's output, quoted on the content page: Step 2's output, quoted on the content page:
Line 1816: Line 1938:
 ===== 9. What could not be established ===== ===== 9. What could not be established =====
  
 +  * **The recommendation to use the amd64 container is untested here.** This host has no Docker and cannot execute x86-64 binaries, so "run the container under emulation" is an inference from the image existing and being amd64-only, not a measurement. The page says so.
   * **No completed OpenWPM crawl.** Nothing on the content page reports data produced by a working OpenWPM instrument on this host. Everything about the instruments comes from the code, the documentation and the pure-Python execution checks; everything about failure comes from a failed run. The obvious way to close this is an x86-64 host or the ''openwpm/openwpm'' container under emulation.   * **No completed OpenWPM crawl.** Nothing on the content page reports data produced by a working OpenWPM instrument on this host. Everything about the instruments comes from the code, the documentation and the pure-Python execution checks; everything about failure comes from a failed run. The obvious way to close this is an x86-64 host or the ''openwpm/openwpm'' container under emulation.
   * **Why the extension's privileged startup fails on a branded build** (§6).   * **Why the extension's privileged startup fails on a branded build** (§6).
Line 1825: Line 1948:
 ===== 10. Bibliography additions ===== ===== 10. Bibliography additions =====
  
-Six entries were added to [[literature:bibliography]], each verified against a primary record. No existing key was touched.+**Seven** entries were added to [[literature:bibliography]], each verified against a primary record. No existing key was touched. (This section said "six" until 2026-09-05: six were added when it was written, and ''agarwal2020_stop'' was added later the same day by §11.1 S8 without the count being updated. ''out/bib_additions_openwpm.bib'' has seven ''@'' entries.)
  
 ^ Key ^ Paper ^ How the entry was made ^ ^ Key ^ Paper ^ How the entry was made ^
Line 1850: Line 1973:
 | S5 | "all 14 tables" counts SQLite's own ''sqlite_sequence'' as an OpenWPM table | **accepted**, now "13 of OpenWPM's tables (14 with ''sqlite_sequence'')" | | S5 | "all 14 tables" counts SQLite's own ''sqlite_sequence'' as an OpenWPM table | **accepted**, now "13 of OpenWPM's tables (14 with ''sqlite_sequence'')" |
 | S6 | The published smoke configuration was validated through OpenWPM's own ''validate_browser_params''/''validate_manager_params'' | no change needed, recorded | | S6 | The published smoke configuration was validated through OpenWPM's own ''validate_browser_params''/''validate_manager_params'' | no change needed, recorded |
-| S7 | ''[[:Artifacts]]'' is a red link and was linked silently | **accepted**, now flagged as the parent page flags it |+| S7 | %%[[:Artifacts]]%% is a red link and was linked silently | **accepted**, now flagged as the parent page flags it |
 | S8 | The three identifier-sweep hits were read; one of them ({[agarwal2020_stop]}) is the worked example the page was asking for and was not being used | **accepted**, added with a new bibliography entry | | S8 | The three identifier-sweep hits were read; one of them ({[agarwal2020_stop]}) is the worked example the page was asking for and was not being used | **accepted**, added with a new bibliography entry |
  
-==== 11.2 Figures against the script (''sonnet'') ====+==== 11.2 Figures against the script (sonnet) ====
  
 Re-ran ''report_openwpm.mjs'', ''openwpm_quotecheck.py'' and ''openwpm_defaults.py'' (the last inside a live OpenWPM checkout) and confirmed all three reproduce the committed output byte for byte; checked the byte sizes, the 14 tables at 0 rows, every table in //Use in Publications//, and both ''check_page_numbers.mjs'' invocations. Re-ran ''report_openwpm.mjs'', ''openwpm_quotecheck.py'' and ''openwpm_defaults.py'' (the last inside a live OpenWPM checkout) and confirmed all three reproduce the committed output byte for byte; checked the byte sizes, the 14 tables at 0 rows, every table in //Use in Publications//, and both ''check_page_numbers.mjs'' invocations.
Line 1861: Line 1984:
 | 2 | "no year above 12% and none below 3%" is falsified by the 2026 row (1.4%) in the table immediately above | **accepted**, now scoped to the eleven complete years 2015–2024, with the two provisional years named | | 2 | "no year above 12% and none below 3%" is falsified by the 2026 row (1.4%) in the table immediately above | **accepted**, now scoped to the eleven complete years 2015–2024, with the two provisional years named |
 | 3 | The version column paraphrased "0.9.0 (machine A); 0.7.0 (machine B)" as "0.9.0 and 0.7.0, one per machine", on a page that promises verbatim version strings | **accepted**, verbatim string restored | | 3 | The version column paraphrased "0.9.0 (machine A); 0.7.0 (machine B)" as "0.9.0 and 0.7.0, one per machine", on a page that promises verbatim version strings | **accepted**, verbatim string restored |
-| 4 | ''check_page_numbers.mjs --code'' exited 1 on ''62'' (a ''print()'' column width in the published script), so "both guards pass" was not true as invoked | **accepted**; the report's ''Z'' block now carries it with that reason, and both invocations pass |+| 4 | ''check_page_numbers.mjs %%--code%%'' exited 1 on ''62'' (a ''print()'' column width in the published script), so "both guards pass" was not true as invoked | **accepted**; the report's ''Z'' block now carries it with that reason, and both invocations pass |
 | 5 | The new semver handling in ''check_page_numbers.mjs'' used ''report.includes(v)'', looser than the boundary-checked ''present()'' every other number goes through | **accepted**, now uses ''present()''. The reviewer confirmed it does not currently misfire; the point is that it could | | 5 | The new semver handling in ''check_page_numbers.mjs'' used ''report.includes(v)'', looser than the boundary-checked ''present()'' every other number goes through | **accepted**, now uses ''present()''. The reviewer confirmed it does not currently misfire; the point is that it could |
 | 6 | The guard scores "21" as accounted-for because "21" appears incidentally in the report (a citation marker, "21 crawls/harvests") — a presence-anywhere weakness of the guard, not introduced here | **accepted in part**: the figure is now attributed to ''report_crawler.mjs'' in the ''Z'' block. The general weakness is real and is left as a known limitation of the guard | | 6 | The guard scores "21" as accounted-for because "21" appears incidentally in the report (a citation marker, "21 crawls/harvests") — a presence-anywhere weakness of the guard, not introduced here | **accepted in part**: the figure is now attributed to ''report_crawler.mjs'' in the ''Z'' block. The general weakness is real and is left as a known limitation of the guard |
Line 1869: Line 1992:
 | 10 | The quoted log collapses runs of spaces, which was not disclosed | **accepted**, disclosure extended | | 10 | The quoted log collapses runs of spaces, which was not disclosed | **accepted**, disclosure extended |
  
-==== 11.3 Citations and quotes (''sonnet'') ====+==== 11.3 Citations and quotes (sonnet) ====
  
-Checked all 23 citekeys, the six new BibTeX entries against Crossref and the PoPETs landing pages, every quoted sentence against ''paper.cols.txt'' with punctuation stripped, and every repository and issue quote against the sources.+Checked all 23 citekeys, the new BibTeX entries against Crossref and the PoPETs landing pages, every quoted sentence against ''paper.cols.txt'' with punctuation stripped, and every repository and issue quote against the sources.
  
 ^ # ^ Finding ^ Action ^ ^ # ^ Finding ^ Action ^
Line 1879: Line 2002:
 | — | Everything else — the README block quote, the ''docs/'' quotes, all four GitHub issues, Bugzilla 634073, the ''fingerprinting.json'' history, the 14-object list, Manifest V2 and the three ''experiment_apis'', the Krumnow characterisation, the OpenWPM-Mobile and "No boundaries" claims, and every other version string in the table — verified against the source | no change | | — | Everything else — the README block quote, the ''docs/'' quotes, all four GitHub issues, Bugzilla 634073, the ''fingerprinting.json'' history, the 14-object list, Manifest V2 and the three ''experiment_apis'', the Krumnow characterisation, the OpenWPM-Mobile and "No boundaries" claims, and every other version string in the table — verified against the source | no change |
  
-==== 11.4 External currency (''sonnet'') ====+==== 11.4 External currency (sonnet) ====
  
 Every external fact re-fetched today. The whole maintenance table reproduced exactly, all four cited issues still open, Bugzilla 634073 still ''NEW'', no PyPI package, Docker Hub still amd64-only, conda-forge still zero ''linux-aarch64'' builds for the three pins, geckodriver still 0.37.1 with its own ''CHANGES.md'' confirming the capabilities change, and — the check that mattered most — **the current** latest ''mozilla-release'' build also has no ''linux64-aarch64-add-on-devel'' task, so the ARM conclusion needs no caveat. Manifest V2 has no announced Firefox end-of-support date. Every external fact re-fetched today. The whole maintenance table reproduced exactly, all four cited issues still open, Bugzilla 634073 still ''NEW'', no PyPI package, Docker Hub still amd64-only, conda-forge still zero ''linux-aarch64'' builds for the three pins, geckodriver still 0.37.1 with its own ''CHANGES.md'' confirming the capabilities change, and — the check that mattered most — **the current** latest ''mozilla-release'' build also has no ''linux64-aarch64-add-on-devel'' task, so the ARM conclusion needs no caveat. Manifest V2 has no announced Firefox end-of-support date.
Line 1887: Line 2010:
 | — | Noted, not used: Firefox is now 153.0 and Selenium 4.47.0. The pages claim neither as current | no change | | — | Noted, not used: Firefox is now 153.0 and Selenium 4.47.0. The pages claim neither as current | no change |
  
-==== 11.5 Generic pass (''fable'') ====+==== 11.5 Generic pass (fable) ====
  
-{{GENERIC}}+It returned late, after the pages were already published, and it justified its slot: **fifteen findings on a page three focused passes had cleared**, including a data-loss bug in code the page tells readers to copy. Every one below was verified against the repository or the corpus before being accepted; the pages were then corrected and re-saved.
  
-===== 12. Run log =====+^ # ^ Finding ^ Action ^ 
 +| 1 | **Blocking.** ''openwpm_smoke.py'' set ''save_content'' but passed ''None'' as the unstructured storage provider, so **every response body is dropped** — ''storage_controller.py'' logs "Tried to save content while not having provided any unstructured storage provider" and continues. The script's own check (per-table row counts in SQLite) cannot see LevelDB, so the one instrument it misconfigured was the one its check could not catch | **accepted**, verified at ''storage_controller.py:132''. The script now passes ''LevelDbProvider'', counts the distinct bodies, and carries a comment explaining the trap. Neither the figures pass (which re-ran the SQL) nor S6 (which ran ''validate_browser_params'') could have seen this: the validators never look at storage providers | 
 +| 2 | ''content_hash'' is **SHA-256**, not MD5. The page said MD5 twice, having taken it from ''docs/Configuration.md'' lines 419–420 | **accepted**, verified: ''Extension/src/lib/sha256.ts'' computes ''crypto.subtle.digest("SHA-256", …)'' and ''response-body-listener.ts'' uses it; there is no MD5 in the content path. The pitfall is now the divergence itself. Noted against this page's own §7 boast that a default was taken "from execution, not from the docs" — and then a doc error was copied anyway | 
 +| 3 | The claim that the extraction scores an Xvfb paper as //not stating// headlessness is false | **accepted**, measured: exactly two of the 60 papers mention Xvfb, and they were coerced to **opposite** values — ''headless'' for the 1-million-site paper, ''headful'' for {[zeber2020representativeness]}. The page now reports the coercion, which is a better argument for the same point | 
 +| 4 | The opening box still implied the 2024 paper crawled with Firefox 90 — the error the citations pass had corrected in the body only | **accepted**. This is the documented failure mode: a qualifier survives in the section and is dropped in the box | 
 +| 5 | "Every record carries ''visit_id'', ''browser_id'' and ''instance_id''" — ''instance_id'' has **0 occurrences in ''schema.sql''** and exists only on the Arrow/Parquet path | **accepted**, verified by grep | 
 +| 6 | "''other'' … would rank **fourth**" — inserted into the ranking it is fifth, and the table also silently cut two rows. A fix for finding 11.2 #8 introduced this | **accepted**; the table is now labelled as the top nine with what was cut | 
 +| 7 | The three ''produced'' papers were misidentified on both pages — including omitting {[englehardt2016online]}, the only paper whose membership actually depends on the ''produced'' rule | **accepted**, both pages corrected from report §I | 
 +| 8 | "3% or less" against table values of 3.1/3.1/3.2%; "one paper in fifteen years" from a 2010–2026 corpus | **accepted**, both reworded | 
 +| 9 | The maintenance table gave 72 and 73 for 2026 commits in adjacent rows (commit-date vs author-date bases) | **accepted**: 73 to the pinned commit, 72 of them by one author, on one basis | 
 +| 10 | The snapshot the pass reviewed had already diverged from the published page (which had gained //Papers to read first//) | **accepted as process debt**: the page was edited after the freeze, which is against this site's own "freeze, review, apply" rule. Recorded rather than hidden; the new section's citekeys were checked | 
 +| 11 | The instrument table says "works" six times on a host where no instrument ever ran | **accepted**, footnoted: only "broken" was confirmed by execution here | 
 +| 12 | Provenance overstatements: the ''FIREFOX_STATED'' map is described as carrying a deciding //sentence// for each entry, but one entry paraphrases the extraction record; the §3 section map pointed at a heading renamed during the run | **accepted**, both corrected | 
 +| 13 | A bracketed splice repair (''cookie_instrum''[''ent'']) that the source does not need — the token is intact in ''paper.cols.txt'' | **accepted**, brackets dropped | 
 +| 14 | The two dual-version rows resolve only their first version, so "no such tag" is stated of a paper whose //second// version (0.7.0) was tagged | **accepted as a known limitation**, to be annotated on the page rather than restructured: the report's ''normalise()'' deliberately takes the first match, and the verbatim string is shown beside it. //The annotation was never written.// This row claimed it had been, and a review pass on 2026-09-05 grepped for it and found nothing; it is on the page now, naming both rows and giving 0.19.1's real lag (8) and v0.7.0's real tag date (2016-11-15) | 
 +| 15 | Both deferrals point at [[programming:stateful_stateless]], which is a notes stub; and the ARM narrative is long for what it concludes | **partly accepted**: the stub is now flagged where it is linked. The ARM section was kept at length — it is the only place a reader can check a negative claim, and negatives are the cheapest thing to get wrong | 
 + 
 +Its "verified and fine" list is longer than its findings and is worth reading before re-checking anything: it independently reproduced every //Use in Publications// figure, the parent-page and corpus-page consistency, and some thirty repository facts. 
 + 
 +===== 12. Addendum, 2026-08-14: fork-vs-library, and the disappearing Firefox build ===== 
 + 
 +Two pitfalls were added to the content page after publication, both from **Karel's own practice rather than from the corpus** — neither appears in the extraction, and no query on this page would have surfaced them. They are now the first two entries under //Known pitfalls//. What follows is what was verified before writing them, since the claims are stronger than "a practitioner says so". 
 + 
 +==== 12.1 The fork-vs-library claim ==== 
 + 
 +Karel's account: OpenWPM expects you to fork it and edit the tree; groups that do so cannot upgrade, and their crawlers stop working, which is why authors of three-year-old artefacts often cannot run them. The counter-practice is to treat it as a library and monkey-patch, as Ahmed Bouhoula did for the ALSAcnc crawler, which was upgraded at least four times (starting around v0.21.1, before the public repository existed). 
 + 
 +^ Claim ^ How checked on 2026-08-14 ^ Result ^ 
 +| No library packaging | PyPI, and the maintenance table's own row | No ''openwpm'' package; installation is conda plus shell scripts inside the checkout | 
 +| ALSAcnc imports rather than forks | ''git clone https://github.com/bouhoula/alsacnc''; ''grep -rn "from openwpm"'' | Imports ''openwpm.config'', ''openwpm.task_manager'', ''openwpm.commands.types'', ''openwpm.storage.sql_provider''; OpenWPM itself is cloned at a tag by ''docker/crawler/Dockerfile'', never vendored | 
 +| Monkey-patch layer | ''wc -l cookie_crawler/utils/monkey_patches/*.py'' | 5 files, **583 lines**: ''__init__.py'' 23, ''browser_manager.py'' 130, ''browser_manager_handle.py'' 194, ''command_sequence.py'' 199, ''shutdown.py'' 37. Applied at ''run_crawler.py:94'' and '':186'' | 
 +| Upgrade history | ''git log %%--all%% -- docker/crawler/Dockerfile'', then ''git show <c>:docker/crawler/Dockerfile'' | **Four** pins, on two branches: ''235a510'' 2024-06-22 → v0.23.0, ''d5c449a'' 2024-09-24 → v0.29.0, ''3c6cfd1'' 2025-01-29 → v0.31.0 on ''main''; ''4acb3fd'' 2026-07-10 → **v0.34.0** on branch ''cnil''. The 2026-08-14 check ran ''git log'' without ''%%--all%%'' and saw only ''main'', so it found three and the page said three; a review pass found the fourth on 2026-09-05. The patch layer is byte-identical on both branches (23/130/194/199/37 lines), which is the stronger form of the claim: one 583-line layer across eleven OpenWPM releases | 
 +| Upstream agrees | [[https://github.com/openwpm/OpenWPM/issues/964|issue #964]], comment by ''vringar'' 2021-12-16 | **Was not verbatim, and this row said it was.** Re-fetched 2026-09-05 via ''gh api repos/openwpm/OpenWPM/issues/964/comments'': the source reads "build on top of it **(as you apparently have done)** and never upgrade.\\ We should provide a better story here." The page had dropped the parenthetical with no ellipsis, and inserted an "[…]" between two consecutive sentences where nothing was cut. Both fixed. The dropped words are the ones that make the remark personal to the issue's author — the same non-independence this section discloses two paragraphs down, removed from the quote that shows it. Also corrected: #743 is an **issue**, not a PR (''pulls/743'' → 404, ''issues/743'' → "Refactor commands to actually follow the Command pattern"); #753 is a PR. The maintainer wrote "#743 and #753" without calling either a PR | 
 + 
 +Note for honesty about sourcing: issue #964 was opened by ''Bender250'' — Karel — so the "upstream agrees" quote is a maintainer replying to the same person whose experience prompted this addendum. That does not weaken the quote (it is the maintainer's own assessment of his project), but a reader deserves to know the thread is not independent of the claim. 
 + 
 +The DuckDuckGo Tracker Radar Collector contrast is Karel's. Checked: its ''package.json'' declares ''"main": "main.js"'' and its README documents ''require("tracker-radar-collector")'' with exported ''crawlerConductor'', ''crawler'' and collector classes — a library API, although it is installed from GitHub rather than from npm (the npm name is a security placeholder package). The contrast is about packaging and extension surface, not about quality. 
 + 
 +==== 12.2 The disappearing Firefox build ==== 
 + 
 +Karel's account: Mozilla does not keep the old binaries, so a handful of OpenWPM users trade tarballs of e.g. Firefox 80 between themselves whenever an artefact is missing one. Measured rather than asserted, with ''scripts/openwpm_firefox_availability.sh'' (committed; output below): for each ''v*'' tag, read the pinned hg revision and target suffix out of ''scripts/install-firefox.sh'', rebuild the exact taskcluster index URL ''install.sh'' fetches, and HEAD it. 
 + 
 +<file - openwpm-firefox-availability.txt> 
 +v0.10.0 FIREFOX_77_0_1_ .tar.bz2 HTTP=404 
 +v0.11.0 FIREFOX_78_0_1_ .tar.bz2 HTTP=404 
 +v0.12.0 FIREFOX_80_0_ .tar.bz2 HTTP=404 
 +v0.13.0 FIREFOX_83_0_ .tar.bz2 HTTP=404 
 +v0.14.0 FIREFOX_86_0_1_ .tar.bz2 HTTP=404 
 +v0.14.1 FIREFOX_86_0_1_ .tar.bz2 HTTP=404 
 +v0.15.0 FIREFOX_88_0_ .tar.bz2 HTTP=404 
 +v0.16.0 FIREFOX_89_0_ .tar.bz2 HTTP=404 
 +v0.17.0 FIREFOX_90_0_2_ .tar.bz2 HTTP=404 
 +v0.18.0 FIREFOX_95_0_ .tar.bz2 HTTP=404 
 +v0.19.0 FIREFOX_98_0_ .tar.bz2 HTTP=404 
 +v0.19.1 FIREFOX_98_0_ .tar.bz2 HTTP=404 
 +v0.20.0 FIREFOX_100_0_ .tar.bz2 HTTP=404 
 +v0.21.0 FIREFOX_105_0_ .tar.bz2 HTTP=404 
 +v0.21.1 FIREFOX_105_0_ .tar.bz2 HTTP=404 
 +v0.22.0 FIREFOX_114_0_1_ .tar.bz2 HTTP=404 
 +v0.23.0 FIREFOX_115_0_3_ .tar.bz2 HTTP=404 
 +v0.24.0 FIREFOX_117_0_ .tar.bz2 HTTP=404 
 +v0.25.0 FIREFOX_118_0_2_ .tar.bz2 HTTP=404 
 +v0.26.0 FIREFOX_121_0_ .tar.bz2 HTTP=404 
 +v0.27.0 FIREFOX_122_0_1_ .tar.bz2 HTTP=404 
 +v0.28.0 FIREFOX_123_0_ .tar.bz2 HTTP=404 
 +v0.29.0 FIREFOX_128_0_ .tar.bz2 HTTP=404 
 +v0.30.0 FIREFOX_130_0_1_ .tar.bz2 HTTP=404 
 +v0.31.0 FIREFOX_134_0_1_ .tar.bz2 HTTP=404 
 +v0.32.0 FIREFOX_148_0_ .tar.xz HTTP=200 
 +v0.33.0 FIREFOX_149_0_ .tar.xz HTTP=200 
 +v0.34.0 FIREFOX_150_0_2_ .tar.xz HTTP=200 
 +v0.35.0 FIREFOX_152_0_ .tar.xz HTTP=200 
 +v0.36.0 FIREFOX_154_0_ .tar.xz HTTP=200 
 +</file> 
 + 
 +Four of 29 releases are installable; the boundary sits between v0.31.0 (Firefox 134, pinned 2025-01-19) and v0.32.0 (Firefox 148, 2026-03-03). Supporting checks: 
 + 
 +  * **It is expiry, not a moved URL or a changed suffix.** The index //namespace// lookup for v0.29.0's revision returns ''ResourceNotFound'' — the indexed task is gone, not just one artefact — and retrying that release with ''.tar.xz'' instead of ''.tar.bz2'' also 404s. The same lookup for v0.35.0's revision returns a task with ''"expires": "2027-06-10T17:30:02.505Z"'', which is where "about a year" comes from. 
 +  * **One transient, corrected.** v0.35.0 returned HTTP 503 on the recorded run and 200 on three immediate retries; the 503 was replaced by 200 in the output above. This is the only edit made to any script output on either page, and it is recorded here rather than applied silently. 
 +  * **The branded build is not a substitute**, and the page says so: ''archive.mozilla.org'' still serves branded Firefox 90.0.2 (HTTP 200), but the extension needs an unbranded build, per ''docs/Platform-Architecture.md'' already quoted in §7. 
 +  * **The container escape hatch was checked, not assumed.** Docker Hub tag lookups: ''0.15.0'', ''0.16.0'', ''0.17.0'', ''0.21.1'', ''0.23.0'', ''0.29.0'' and ''0.31.0'' all return 200; ''0.10.0'', ''0.12.0'', ''0.13.0'' and ''0.14.0'' return 404 — **from which this page concluded, wrongly, that the tags go back to 0.15.0.** Re-probed 2026-09-05: the four oldest images are ''v''-prefixed. ''v0.10.0'', ''v0.11.0'', ''v0.12.0'' and ''v0.13.0'' all return 200, and so does the unprefixed ''0.14.1''; only ''0.14.0'' was never published. The tags go back to the first release of the current architecture. The 2026-08-14 probe tested one naming convention and read its absence as the artefact's absence, which is the same mistake as reading a silent tool as a negative result. ''0.17.0'' lists ''amd64'' only. That the image contains the binary is read from the v0.17.0 ''Dockerfile'', which runs ''./install.sh'' and then ''mv firefox-bin /opt/firefox-bin'' with ''ENV FIREFOX_BINARY''; **the image was not pulled or run** — this host is aarch64, and §8 explains why nothing amd64 was executed here. 
 +  * **The upstream issue is still open** ([[https://github.com/openwpm/OpenWPM/issues/964|#964]], opened 2021-12-15, three comments, most recent 2025-11-26), and ''install-firefox.sh'' at ''b9dd4c3'' now prints "the Firefox version may be too old and no longer available on TaskCluster" and points at it. Neither the mirror repository the maintainer floated in 2021 nor any other archive of these builds was found. 
 + 
 +==== 12.3 Judgement calls in this addendum ==== 
 + 
 +  * **Placed as the first two pitfalls**, per Karel's instruction, ahead of the empty-database pitfall. Defensible independently: these two decide whether the crawler still exists in three years, where the others decide whether one crawl is sound. 
 +  * **Cited ALSAcnc through the existing key** {[bouhoula2024_automated]} rather than adding a bibliography entry for the repository — no new keys. Note the pre-existing duplicate ''bouhoula2024automated'' (recorded in the archives provenance page); the underscore form used here is the one [[statistics:pvalue_corrections]] already uses. 
 +  * **Did not claim "four upgrades"** although Karel reports four; only three are checkable in the public repository, and the page says "in its public history alone", which is true and weaker. 
 +  * **Did not open an Open Question.** The mirror-the-binary problem has an answer today (archive the tarball, or use the container image), so it belongs in guidance rather than in the open list. Whether anyone should maintain a community mirror is a question for the project, not for this page. 
 +  * **No new review pass was run** on this addendum. It is two bullets plus this section, every factual claim is a command whose output is recorded above, and no corpus figure was touched — but a reader should know it did not go through the four-reviewer layer the rest of the page did. 
 + 
 +===== 13. Run log =====
  
   * **2026-08-14.** Corpus queries, repository reading, install attempts, page and provenance page written, four review passes, published. Corpus at 5,859 extracted papers; OpenWPM at ''b9dd4c3''.   * **2026-08-14.** Corpus queries, repository reading, install attempts, page and provenance page written, four review passes, published. Corpus at 5,859 extracted papers; OpenWPM at ''b9dd4c3''.
   * **Tooling changed during the run.** ''scripts/check_page_numbers.mjs'' gained explicit handling of semver triples: the generic number regex cuts ''0.35.0'' into ''0.35'', and the ''present()'' lookahead then refuses to match that fragment inside the report's own ''0.35.0'', so 24 of this page's 47 initially-unaccounted "figures" were fragments of version numbers the report does print in full. Versions are now checked as whole tokens against the report and then removed before the generic scan — stricter than an ALLOW entry, and it does not weaken the scan for anything that is not a triple. The change is shared with every other page's check, so it was made narrow deliberately.   * **Tooling changed during the run.** ''scripts/check_page_numbers.mjs'' gained explicit handling of semver triples: the generic number regex cuts ''0.35.0'' into ''0.35'', and the ''present()'' lookahead then refuses to match that fragment inside the report's own ''0.35.0'', so 24 of this page's 47 initially-unaccounted "figures" were fragments of version numbers the report does print in full. Versions are now checked as whole tokens against the report and then removed before the generic scan — stricter than an ALLOW entry, and it does not weaken the scan for anything that is not a triple. The change is shared with every other page's check, so it was made narrow deliberately.
   * **Credential exposure, caught in review.** An early command in this run printed ''$GH_TOKEN'' to the terminal to test whether it was set, so a GitHub token is in this run's transcript. The token is not in this repository, not in any published page and not in any output file, and ''openwpm_platform_probe.sh'' reads it from the environment. It should be rotated. The lesson is the boring one: test for a variable with ''${VAR:+set}'', never by echoing it.   * **Credential exposure, caught in review.** An early command in this run printed ''$GH_TOKEN'' to the terminal to test whether it was set, so a GitHub token is in this run's transcript. The token is not in this repository, not in any published page and not in any output file, and ''openwpm_platform_probe.sh'' reads it from the environment. It should be rotated. The lesson is the boring one: test for a variable with ''${VAR:+set}'', never by echoing it.
-  * **What the next run should do first:** re-run ''openwpm_platform_probe.sh'' (the maintenance table is a claim about a date), then ''report_openwpm.mjs'' and both ''check_page_numbers.mjs'' invocations. If the corpus has grown, watch the spelling table in report §A for a fork under a new name.+  * **2026-08-14, later the same day.** Karel supplied two pitfalls from his own practice (fork-vs-library; the deleted Firefox builds). Verified as §12, added to the content page as its first two //Known pitfalls//, plus one line each in //Forks and extensions//, //What to report// and the version-table preamble. New script: ''openwpm_firefox_availability.sh''; ''check_page_numbers.mjs'' gained five ALLOW entries (HTTP 404, the 583-line patch layer, and issue/PR numbers 964, 743, 753) — all external figures with no place in the report. No corpus query re-run, no figure changed, no bibliography entry added. 
 +  * **2026-09-05, second generic review pass.** See §14. ''openwpm_platform_probe.sh'', ''report_openwpm.mjs'', ''openwpm_quotecheck.py'', both ''check_page_numbers.mjs'' invocations, ''check_wrap.mjs'', ''check_tables.mjs'', ''sitemap.mjs'' and ''redlinks.mjs'' all re-run. Corpus unchanged at 5,859 extracted papers and the report reproduced byte for byte before editing, so **no //Use in Publications// figure moved**; every change is in the version-and-browser section, the maintenance table, the two practice pitfalls, or this log. New scripts: ''openwpm_quotecheck_control.py''. OpenWPM at ''61d285f'' (0.36.0). 
 +  * **Credential exposure, again, in this run.** Testing whether ''$GH_TOKEN'' was set with ''${GH_TOKEN:+set}${GH_TOKEN:-unset}'' printed **the token itself** — ''${VAR:-default}'' substitutes the //value// when the variable is set, so the "safe" half of that expression is the unsafe one. This is the same mistake the 2026-08-14 entry above records, made again by someone who had read that entry ten minutes earlier. The token is not in this repository, not in any published page and not in any output file (checked: ''grep -rl "gho_" out/ scripts/ pages/'' is empty). **It should be rotated.** The construct that actually works is ''${VAR:+set}'' alone. 
 +  * **A shared tool was found broken and fixed:** ''scripts/sitemap.mjs'', see §14.7. Any run between 2026-09-04 and 2026-09-05 that relied on step 1 of the workflow got a vacuous pass on its red links. 
 +  * **What the next run should do first:** re-run ''openwpm_platform_probe.sh'' (the maintenance table is a claim about a date, and it went stale within ten days last time), then ''report_openwpm.mjs'' and both ''check_page_numbers.mjs'' invocations. If the corpus has grown, watch the spelling table in report §A for a fork under a new name. 
 + 
 +===== 14. Second generic pass, 2026-09-05 ===== 
 + 
 +The item that commissioned this run recorded that the fourth (generic, ''fable'') pass "was launched and had not returned when the run ended, so its findings are not incorporated". **That was true when the item was written and false by the time the run ended:** the pass returned late on 2026-08-14 and was applied the same day (§11.5, revisions ''1786697641'' and ''1786697648''). So this is a //second// generic pass, and it was still worth running, because the pages gained material afterwards that no pass has ever seen: the two practice-sourced pitfalls, §12, and the //Papers to read first// section. 
 + 
 +**Method.** Both pages were exported from the live wiki on 2026-09-05 and frozen (''out/frozen3/''), together with ''report_openwpm.mjs'', its output, ''openwpm_quotecheck.py'', ''openwpm_platform_probe.sh'' and both published Python files. Two ''fable'' agents were run in parallel with no checklist: one reading both pages end to end as a demanding reader, one adversarially re-verifying every external claim against a primary source fetched today. Both were told the author's framing might be wrong. Every finding below was re-verified by the author before being accepted; the commands are named so a reader can repeat them. 
 + 
 +**The corpus did not move.** ''report_openwpm.mjs'' re-run against ''data/extract/run1'' reproduced the committed output byte for byte before any edit, and ''openwpm_quotecheck.py'' reproduced 65/52/13/0. No //Use in Publications// figure changed in this pass. What changed is the version-and-browser section, the maintenance table and the two practice pitfalls. 
 + 
 +==== 14.1 Blocking ==== 
 + 
 +^ # ^ Finding ^ Action ^ 
 +| 1 | **The page said 3 of the 15 version-stating papers also state a browser. Nine do.** The hand map ''FIREFOX_STATED'' was seeded with ''/Firefox v?\d+/'', which matches "Firefox (v102)" but not "Firefox version 88" ({[demir2022_reproducibility]}), "the Firefox browser (v100.0" ({[demir2024_bannertools]}), "the Firefox browser (Version 123.0)" ({[bottger2025_regional]}), or a "Fire-[splice]fox v121.0" cut by a column splice ({[rasaii2025_crumbs]}) | **accepted**, and then **got wrong a second time** — see §14.8. The first re-sweep was wide but ran only over slugs the reviewer had named, giving 7; swept over all 15 it gives **9**, adding {[fouad2022_cookie]} and {[demir2023_similarity]}. Final: 9 state a browser, 2 of them on a pre-0.10 release with no pin, and of the 7 checkable **3 do not match the pin** (was 2) — {[bottger2025_regional]} ran Firefox 123 on a release that ships 122. The page had also contradicted itself, quoting {[rasaii2025_crumbs]} as "the best-documented row … states the OpenWPM version //and// the Firefox version" three paragraphs after saying only three papers state a browser. The report now sweeps the **whole population** and prints the hits it rejects with reasons | 
 +| 2 | **The Open Question "no paper we found reports ''num_browsers'' alongside a stateful claim" is false.** {[englehardt2018_email]} writes "We configure OpenWPM to run 15 measurement instances in parallel" and is one of the five ''both'' papers; {[englehardt2016online]} runs 20 | **accepted**, both read. Rewritten to the question that survives: papers do report the instance count, but none treats the resulting //N// cookie jars as a property of the measurement. An absence asserted from the papers that happened to be read, rather than from a query, is the characteristic way an Open Question goes wrong | 
 + 
 +==== 14.2 Currency — the maintenance table had gone stale under a correct date stamp ==== 
 + 
 +**OpenWPM v0.36.0 was released on 2026-08-24**, ten days after this page was written. The table was honestly dated 2026-08-14, but four claims elsewhere on the page were phrased as current rather than dated, including the model sentence readers are told to copy. 
 + 
 +^ # ^ Finding ^ Action ^ 
 +| 3 | v0.36.0 (Firefox 154) exists; "Latest release 0.35.0", "four releases in 2026 … 148→152", "all 29 releases", "only the four 2026 releases (148–152)", and the two ''0.35.0 (Firefox 152)'' model sentences are all stale | **accepted**, probe re-run, whole table refreshed to 2026-09-05, version table gains a 0.36.0 row, 29→30 releases, four→five | 
 +| 4 | **The geckodriver pitfall describes a bug that upstream has fixed.** v0.36.0 pins ''geckodriver=0.37.1'' and requests the privilege through geckodriver's ''%%--allow-system-access%%'' flag instead of Firefox's ''-remote-allow-system-access'' | **accepted**, verified in ''deploy_firefox.py'' at ''61d285f'' and the CHANGELOG. The bullet is now scoped to v0.35.0 and earlier and says v0.36.0 fixes it. Our A/B measurement stands for the releases it was made on | 
 +| 5 | "Firefox reached 147" during 2025 — 147 shipped 2026-01-13; Firefox reached **146** in 2025 | **accepted**, ''product-details.mozilla.org/1.0/firefox_history_major_releases.json'' | 
 +| 6 | "180 open issues" is GitHub's ''open_issues_count'', which counts pull requests as issues | **accepted**: 141 issues + 39 PRs, from two ''search/issues'' queries, with the trap footnoted | 
 +| 7 | "73 commits in 2026, 72 by one person" mixes clocks — the total was committer date, the split author date | **accepted**. On author date to ''61d285f'': 73 commits in 2026, **all 73 by ''vringar''**. Committer date gives 74. One clock, named | 
 + 
 +==== 14.3 The two practice-sourced pitfalls, which no earlier pass reviewed ==== 
 + 
 +^ # ^ Finding ^ Action ^ 
 +| 8 | **The ''vringar'' quote is not verbatim and §12.1 said it was** — "(as you apparently have done)" dropped with no ellipsis, and an "[…]" inserted between two consecutive sentences | **accepted**, re-fetched and restored. See the corrected §12.1 row | 
 +| 9 | #743 is an **issue**, not a pull request | **accepted**, ''pulls/743'' → 404 | 
 +| 10 | **ALSAcnc has a fourth public pin**, v0.34.0 (''4acb3fd'', 2026-07-10) on branch ''cnil'' — the 2026-08-14 check ran ''git log'' without ''%%--all%%'' | **accepted**, and it improves the page. The "byte-identical patch layer" wording that first went with it was **wrong** and is corrected in §14.8: four of the five files are byte-identical across all four pins, and ''browser_manager.py'' is not. Its current pin is one of the five whose Firefox still downloads | 
 +| 11 | **"a fork of any of those releases would by now be unbuildable" draws a contrast that does not exist** — ALSAcnc's ''main'' pins v0.31.0, whose Firefox 404s, so a fresh clone of it fails exactly as a fork would | **accepted**, and it was the sharpest finding of the pass. The bullet conflated two things. Rewritten so the contrast is **upgradability, not buildability**: the library-shaped project could move to a live release and did; a fork stops at the release it was cut from | 
 +| 12 | **"Docker tags go back to 0.15.0" is wrong** — the four oldest images are ''v''-prefixed (''v0.10.0''–''v0.13.0''), plus ''0.14.1''; only ''0.14.0'' was never published | **accepted**. The 2026-08-14 probe tested one naming convention and read its absence as the artefact's absence. This one failed //against// the page's interest: a reader with a 2020–2021 artefact was told no image existed for it | 
 + 
 +==== 14.4 Lesser, and the page's own bookkeeping ==== 
 + 
 +^ # ^ Finding ^ Action ^ 
 +| 13 | "the eleven complete years 2015–2024" is **ten** years | **accepted** | 
 +| 14 | §11.5 #14 said the dual-version limitation was "annotated on the page"; it was not | **accepted**, the annotation is now written and the row corrected to say it was not | 
 +| 15 | §10 says "Six entries were added"; ''bib_additions_openwpm.bib'' has seven | **accepted** — the seventh ({[agarwal2020_stop]}) was added later the same day by §11.1 S8 and the count was not updated | 
 +| 16 | §7's source table still credited ''docs/Configuration.md'' with "''save_content'' MD5 de-duplication" after §11.5 #2 corrected the page to SHA-256 | **accepted** — the same carry-over defect, one section apart | 
 +| 17 | ''[[:Artifacts]]'' is flagged on the page as "a page this wiki still owes you". It exists, with real content | **accepted**, flag removed. Verified from the rendered DOM: the link is ''wikilink1''. The identical stale caveat on [[:programming:crawler]] was fixed in the link-hygiene sweep; both links are ''wikilink1'' on the fresh DOM. | 
 +| 18 | "a handful of groups keep private copies of these tarballs", and "on an M-series Mac … That works", are asserted flatly with nothing behind them | **accepted in part**: both are true as far as we know and neither is checkable here, so both are now attributed as such rather than deleted. No Mac was available to this run | 
 +| 19 | Firefox 153 was skipped by OpenWPM because its **tagged revision has no unbranded builds on any platform** — a second way the pinned build can be missing, distinct from expiry | **accepted**, added to the expiry pitfall. Found in the v0.36.0 CHANGELOG by the adversarial pass while checking something else | 
 + 
 +==== 14.5 Rejected ==== 
 + 
 +^ Finding ^ Why rejected ^ 
 +| {[siby2023_quic]} also states a crawl Firefox version ("We use Firefox 88.0 for MAIN") | **rejected**, read in the paper. Its OpenWPM sentence is "crawling the pages in MAIN with OpenWPM (v0.17.0) [1] using Firefox five consecutive times" — the browser is named without a version, and the "Firefox 88.0"/"98.0" hits are website-fingerprinting client comparisons elsewhere in the paper. It is recorded as a rejected hit in the report's own residue block | 
 +| ''tp_cookies'' "not what a stock Firefox has done for years" has no source | **rejected as a finding**, kept as written. It is a statement about Firefox's default cookie policy that no reader of this page will contest, and [[programming:stateful_stateless]] carries the partitioning detail with sources | 
 +| The ARM section is long for what it concludes (re-raised from §11.5 #15) | **rejected again**, for the reason given there: it is the only place a reader can check a negative claim | 
 + 
 +==== 14.6 Checks the author ran, independent of both agents ==== 
 + 
 +  * ''report_openwpm.mjs'' re-run before any edit: **byte-identical** to the committed output. ''openwpm_quotecheck.py'': 65 quotes, 52 verbatim, 13 spliced, 0 below 60% — reproduces. 
 +  * Both published %%<file>%% blocks downloaded from the wiki's own ''/_export/code/'' endpoint: byte-identical to ''pages/openwpm_defaults.py'' and ''pages/openwpm_smoke.py'' apart from a stripped trailing newline, and both compile. 
 +  * 26 distinct citekeys on the content page, 26 rendered ''<dt>'' entries in the reference list; all 26 resolve against a fresh export of [[literature:bibliography]]. 
 +  * Rendered DOM: no ''wikilink2'' on either page, and the cross-page anchor ''#which_specialised_crawlers_actually_get_used'' resolves on [[programming:crawler]]. ''redlinks.mjs'' site-wide: 45 pages carry namespace-relative red links; **neither of these two is among them**. 
 +  * ''check_wrap.mjs'' reports %%''<file python openwpm_defaults.py>''%% in a §8 table cell as a tag that "WILL fire". **It does not** — verified in the rendered DOM, where the cell contains ''<code>&lt;file …&gt;</code>'' and every later section, the run log and the reference list are present. %%<file>%% is a block-level mode DokuWiki will not open mid-cell, unlike %%<WRAP>%%, which is what that checker was written for. A false positive in the safe direction; recorded so the next run does not "fix" a page that is not broken. 
 +  * **A positive control for the quote checker**, which it did not have: ''scripts/openwpm_quotecheck_control.py'' (committed, output below). Fabricated quotes score 0–50% against a paper we know, a real quote with one word swapped scores 45%, and the real quote scores 82% — so the checker discriminates. But the margin matters: a half-fabricated quote scored **50%**, only ten points under the 60% floor of the SPLICED band. A SPLICED verdict is therefore not by itself evidence of faithfulness, and "none was unsupported" needed reading, not a threshold. 
 +  * **So all 13 SPLICED quotes were hand-checked.** For twelve, every uncovered word occurs elsewhere in the paper, which is what a column splice looks like. The thirteenth is the interesting one: in PETS 2020 ''missed-by-filter-lists-…'' the uncovered word ''automation'' appears **nowhere** in the skeletonised text. Reading the region shows why — ''…provides browser automa'' | ''mains and constitute 35.66% of all third-party images.'' | ''tion by converting high-level commands into automated browser actions'' — the splice cuts the word in half. Present, and the quote stands. All 13 confirmed. 
 + 
 +<file python openwpm_quotecheck_control.py> 
 +"""Positive control for scripts/openwpm_quotecheck.py, and a hand-check of every 
 +quote that lands in its SPLICED band. 
 + 
 +Two things the checker's own output cannot tell you: 
 + 
 +  1. Does it discriminate at all? A coverage metric that scores everything high 
 +     would report "0 below 60%" whether or not the quotes were real. So feed it 
 +     fabricated quotes against a paper we know and print what it says. 
 +  2. Is a SPLICED verdict (60-99%) evidence that the quote is faithful? Only if 
 +     the uncovered words are genuinely in the paper, cut by a column splice. For 
 +     each SPLICED quote this prints the uncovered words and whether each one 
 +     occurs anywhere in the skeletonised paper text -- and where it does not, 
 +     the region has to be read, because a word can be split mid-token by the 
 +     splice and still be present. 
 + 
 +    python3 scripts/openwpm_quotecheck_control.py 
 +""" 
 + 
 +import json 
 +import re 
 +from pathlib import Path 
 + 
 +SRC = Path(__file__).with_name("openwpm_quotecheck.py").read_text() 
 +_ns: dict = {} 
 +exec(SRC.split("rows = [json.loads")[0], _ns)  # the helpers, not the report body 
 +skel, window_share = _ns["skel"], _ns["window_share"] 
 + 
 +ROOT = Path("/workspace/publications_dataset/data") 
 +RUN = ROOT / "extract/run1/extractions.jsonl" 
 +OPENWPM = re.compile(r"open[\s-]?wpm", re.I) 
 + 
 + 
 +def verdict(share: float) -> str: 
 +    return "VERBATIM" if share == 1 else ("SPLICED" if share >= 0.6 else "SUSPECT") 
 + 
 + 
 +print("=== 1. Positive control: what does the checker say about quotes we made up? ===") 
 +print("Paper: PETS/2024/opted-out-yet-tracked-are-regulations-enough-to-protect-your-privacy\n") 
 +ctl = ROOT / "fulltext/2024/PETS/opted-out-yet-tracked-are-regulations-enough-to-protect-your-privacy/paper.cols.txt" 
 +body = skel(ctl.read_text(errors="replace")) 
 +CONTROLS = [ 
 +    ("real, as published on the page", "We rely on OpenWPM [64] to set up our auditing framework."), 
 +    ("fabricated, plausible continuation", "We rely on OpenWPM to set up our automated consent auditing pipeline across ten thousand sites."), 
 +    ("fabricated, wholly unrelated", "The authors deployed a bespoke Chromium harness with WebGL telemetry and DNS-over-HTTPS pinning enabled."), 
 +    ("real, one word swapped (OpenWPM -> Selenium)", "We rely on Selenium [64] to set up our auditing framework."), 
 +    ("real, truncated to the window width", "We rely on OpenWPM"), 
 +    ("fabricated, at the window width", "We deployed on Chromium"), 
 +] 
 +for label, q in CONTROLS: 
 +    hit, tot = window_share(q, body) 
 +    share = hit / tot 
 +    print(f"  {verdict(share):9} {share:5.0%} ({hit}/{tot})  {label}") 
 + 
 +print("\n=== 2. Every SPLICED quote, with its uncovered words ===") 
 +print("'present elsewhere: False' means the word must be read in context before") 
 +print("the quote is trusted -- it may still be split mid-token by the splice.\n") 
 +rows = [json.loads(l) for l in RUN.read_text().splitlines() if l.strip()] 
 +n = 0 
 +needs_reading = [] 
 +for r in rows: 
 +    tuples = [t for t in r["tools"] if OPENWPM.search(t["name"] or "")] 
 +    if not tuples: 
 +        continue 
 +    txt = ROOT / "fulltext" / str(r["year"]) / r["venue"] / r["slug"] / "paper.cols.txt" 
 +    if not txt.exists(): 
 +        continue 
 +    paper = skel(txt.read_text(errors="replace")) 
 +    for t in tuples: 
 +        q = t["evidence"]["quote"] 
 +        hit, tot = window_share(q, paper) 
 +        share = hit / tot 
 +        if not (0.6 <= share < 1): 
 +            continue 
 +        n += 1 
 +        words = [x for x in re.split(r"\s+", q) if x] 
 +        covered = [False] * len(words) 
 +        for i in range(len(words) - 3): 
 +            if skel(" ".join(words[i : i + 4])) in paper: 
 +                for j in range(i, i + 4): 
 +                    covered[j] = True 
 +        missing = [w for w, c in zip(words, covered) if not c] 
 +        absent = [w for w in missing if skel(w) not in paper] 
 +        print(f"[{n:2}] {share:4.0%}  {r['venue']}/{r['year']}/{r['slug']}") 
 +        print(f"      uncovered: {missing}") 
 +        if absent: 
 +            print(f"      NOT ANYWHERE IN PAPER, read the region: {absent}") 
 +            needs_reading.append((r["slug"], absent)) 
 +print(f"\n{n} quotes in the SPLICED band.") 
 +print(f"{len(needs_reading)} of them have an uncovered word absent from the whole paper:") 
 +for slug, absent in needs_reading: 
 +    print(f"  {slug}: {absent}") 
 +</file> 
 + 
 +<file - openwpm_quotecheck_control-output.txt> 
 +=== 1. Positive control: what does the checker say about quotes we made up? === 
 +Paper: PETS/2024/opted-out-yet-tracked-are-regulations-enough-to-protect-your-privacy 
 + 
 +  SPLICED     82% (9/11)  real, as published on the page 
 +  SUSPECT     50% (8/16)  fabricated, plausible continuation 
 +  SUSPECT      0% (0/14)  fabricated, wholly unrelated 
 +  SUSPECT     45% (5/11)  real, one word swapped (OpenWPM -> Selenium) 
 +  VERBATIM   100% (4/4)  real, truncated to the window width 
 +  SUSPECT      0% (0/4)  fabricated, at the window width 
 +</file> 
 + 
 +==== 14.7 Two broken guards, found while running them ==== 
 + 
 +''scripts/sitemap.mjs'' — the red-link check every page in this wiki is supposed to run before saving — **had been silently asserting nothing.** The wiki's ''?do=sitemap'' now serves absolute URLs under a ''www.'' origin; the script stripped a hard-coded origin without it, so every page id became the whole URL, every cached fetch returned an error page, and the script printed: 
 + 
 +<code> 
 +Existing pages: 161 
 +Distinct internal link targets: 0 
 +Promised but missing: 0 
 +</code> 
 + 
 +Exit 0, no stderr, and a "0 missing" that reads as a clean pass. The id is now taken from ''URL.pathname'', and the script **throws** if any sitemap entry fails to yield a page id, rather than continuing on junk. The 161 poisoned cache files were moved aside and the cache refetched. With that fixed, the site-wide sweep finds exactly one promised-but-missing page — ''provenance:literature:corpus'', linked from [[literature:corpus]] and [[provenance:statistics:hypothesis_testing]] — which is filed as a work item and is not on either of these pages. 
 + 
 +"''Promised but missing: 0''" from a broken checker is the same defect this page's own §5 nearly published: a metric whose reassuring value is an artefact of the method. 
 + 
 +**''scripts/check_wrap.mjs'' told you to apply an escape and then did not honour it.** It masks ''%%…%%'' and ''<nowiki>'' spans before checking %%<WRAP>%% balance — but the other half of the guard, the one that reports a plugin tag wrapped in monospace quotes, scanned the **raw** lines. So a tag correctly wrapped in ''%%…%%'', exactly as the guard's own message instructs, was still reported as a problem. Both halves now scan the masked text. 
 + 
 +Mutation-tested rather than read, because a masking change is the easiest way to make a checker blind: 
 + 
 +^ Mutation ^ Expected ^ Result ^ 
 +| a %%<WRAP todo>%% tag in monospace quotes, added to a table cell | fail | caught, plus the resulting imbalance | 
 +| an unclosed %%<WRAP important>%% | fail | caught | 
 +| lowercase %%<wrap todo>%%…%%</wrap>%% | fail | both caught | 
 +| the page unmodified | pass | passes | 
 + 
 +Neither of these two tool bugs was found by an agent. Both turned up because the guards were actually run against the live pages rather than assumed to have been run when the pages were written — which is the argument for §14 existing at all. 
 + 
 + 
 +==== 14.8 The verification pass, which found five defects in the corrections above ==== 
 + 
 +Per the workflow's own rule — re-run any reviewer whose findings you acted on — a ''sonnet'' pass was given the **corrected, published** pages and told to assume the corrections were wrong. It was right five times, and two of those were in the very finding §14.1 records as fixed. Everything below was re-verified by hand before being applied. 
 + 
 +^ # ^ What the correction got wrong ^ Now ^ 
 +| A | **The corrected Firefox count was still short.** {[fouad2022_cookie]} states its browsers in a setup table — "Firefox version 68.0 45.0.1 … OpenWPM ver[splice]sion v0.9.0 v0.7.0" — and was in neither the map nor its rejected list | **accepted.** Re-swept over all 15 rather than over reviewer-named slugs, which also found {[demir2023_similarity]} ("the most recent stable Firefox version available … (v95.0 …) and a version that is roughly one year older (v86.0.1 …)"). **9, not 7.** Split 4 match / 3 differ / 2 no-pin; 6 say nothing. Also rejected on the same sweep: {[bollinger2022automating]}, whose only "Firefox 86" is a bibliography title | 
 +| B | **"the patch layer is byte-identical across all four" is false.** ''browser_manager.py'' differs on ''cnil'' | **accepted**, and the truth is a better argument: four of five files are untouched, and the fifth changed by one rebinding — ''BrowserManager.run'' → ''run_impl'' — because upstream renamed the method. That //is// what a monkey-patch upgrade costs, shown rather than asserted | 
 +| C | **"eleven OpenWPM releases" is twelve.** v0.23.0…v0.34.0 inclusive is 12 tags | **accepted**, reworded to "the twelve releases v0.23.0 to v0.34.0" | 
 +| D | **§3.3 still carried the pre-correction "3 of 15 … 2 of those 3".** The 2026-09-05 pass fixed §14 and the content page and left an earlier section of //this// page asserting the old numbers | **accepted.** This is the carry-over defect this page has now committed three times (§7's MD5 row, §11.5 #14's phantom annotation, and now §3.3): a figure is corrected where it is loudest and survives where it is quiet. Both counts in §3.3 replaced, with a pointer to §14.1 | 
 +| E | **The report script's own header sentence was backwards** — it announced "The other 8 leave the browser to be inferred from the pin:" and then printed the papers that //do// state it | **accepted**, and it mattered because that raw output is published verbatim in §4. Rewritten | 
 +| F | The ''ahmad2020_apophanies'' entry was footnoted "NOT a sentence read in the paper — the only entry here that is not". The paper does contain one: "at the time of our experiments Open-WPM was unable to drive Firefox versions later than v52.9, while Selenium drove Firefox v66.0", plus a table row | **accepted**, the quote is now the paper's own sentence and the disclaimer is gone | 
 + 
 +**What this pass says about the one before it.** The generic passes found the original error; the verification pass found that the fix was itself incomplete, and incomplete *in the same way* — a probe run over too small a population instead of too narrow a pattern. A correction is not self-verifying, and "we widened the regex" is not the same claim as "we swept the population". The report now does the sweep in code, over ''statedPapers'' itself, so the next person to widen it cannot quietly narrow the population at the same time. 
 + 
 +===== Amendment, 2026-09-04: citekey consolidation ===== 
 + 
 +  * ''fouad2022my'' was one of two keys for the same paper in [[:literature:bibliography]]. The wiki-wide consolidation of 2026-09-04 (drain item ''dedup-regional-filter-lists-bibkey'') kept ''fouad2022_cookie'' and deleted the other entry. 
 +  * 1 citation marker on [[:programming:crawler:openwpm]] was repointed to the kept key. No prose on either page changed, and no figure moved. Statements above that name the deleted key describe the state when they were written. Full query log and the invariants checked before saving: [[:provenance:literature:bibliography]].
  
 ====== References ====== ====== References ======
Line 1909: Line 2341:
    does not is worse than either convention applied consistently. */    does not is worse than either convention applied consistently. */
 ~~DISCUSSION~~ ~~DISCUSSION~~
 +
 +===== 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.
 +
 +===== Link-hygiene sweep, 2026-09-17 =====
 +
 +The historical stale-caveat row now records that the paired ''programming:crawler'' page was fixed in this sweep. The fresh rendered DOM confirmed both ''Artifacts'' links are live; no figures or citations changed.
 +
 +===== Currency re-check, 2026-09-17 =====
 +
 +The stale illustrative release/browser sentence on [[programming:crawler:openwpm]]
 +was already corrected when this item was read: the page says ''OpenWPM 0.36.0
 +(Firefox 154)''. No content edit was made. The release pin and moving stable
 +channel were re-fetched with these commands:
 +
 +<code>
 +curl -sS 'https://product-details.mozilla.org/1.0/firefox_versions.json' | python3 -c 'import json,sys; d=json.load(sys.stdin); print(json.dumps({k:d.get(k) for k in ("LATEST_FIREFOX_VERSION","LATEST_FIREFOX_DEVEL_VERSION","FIREFOX_ESR","FIREFOX_ESR_VERSION")}, sort_keys=True))'
 +curl -sS 'https://raw.githubusercontent.com/openwpm/OpenWPM/v0.35.0/environment.yaml' | rg -n -i 'firefox|geckodriver'
 +curl -sS 'https://raw.githubusercontent.com/openwpm/OpenWPM/v0.35.0/scripts/install-firefox.sh' | rg -n '^(TAG|TARGET_SUFFIX)|FIREFOX_|firefox-ci|taskcluster'
 +bash scripts/openwpm_firefox_availability.sh
 +</code>
 +
 +Mozilla's product-details API returned ''LATEST_FIREFOX_VERSION=156.0''. The
 +v0.35.0 installer returned ''FIREFOX_152_0_RELEASE'' and its environment pins
 +''geckodriver=0.37.0''; the availability checker returned ''v0.35.0 ...
 +FIREFOX_152_0_ .tar.xz HTTP=200'' and ''v0.36.0 ... FIREFOX_154_0_ .tar.xz
 +HTTP=200''. The content page's 0.36.0/154 example therefore names that release's
 +own pin, not the moving stable channel.
 +
 +===== Review layer, 2026-09-17 =====
 +
 +One shared ''model: sonnet'' external-currency pass ran after publication and
 +re-ran ''bash scripts/openwpm_firefox_availability.sh''. It **passed** the
 +existing ''OpenWPM 0.36.0 (Firefox 154)'' example and found no correction needed;
 +the v0.35.0 and v0.36.0 pins returned HTTP 200 from their resolved Mozilla build
 +URLs.
 +
 +===== Quote-check refresh, 2026-09-21 =====
 +
 +The 2026-09-04 ''cols''-vs-PDF audit on [[:provenance:literature:corpus]] showed that 73.1% of evidence quotes that cannot be located in ''paper.cols.txt'' **are** present in an independent ''pypdf'' rendering of the same ''paper.pdf''. ''scripts/quote_check.mjs'' was patched the same day to re-check everything below threshold against that second rendering and report a fourth verdict, **RESCUED**. §5's first-pass figure predates the patch. Re-run, unedited first line:
 +
 +<code>
 +$ node scripts/quote_check.mjs --tools 'open[ -]?wpm' --show 400
 +65 quotes checked: 34 exact, 19 partial (>=60% of 5-word windows), 10 rescued from the PDF, 2 below threshold in both renderings, 0 with no full text on disk.
 +</code>
 +
 +^ Figure ^ Was ^ Is ^ Why ^
 +| quotes checked | 65 | 65 | population unchanged — the corpus has not moved |
 +| exact | 34 | 34 | unchanged |
 +| partial (≥60% of 5-word windows) | 19 | 19 | unchanged |
 +| rescued from the PDF | — | **10** | new verdict; these were inside the old 12 |
 +| below threshold | **12** | **2** (in both renderings) | 12 = 10 + 2 exactly; nothing else moved |
 +
 +**This page argued for the fallback before the fallback existed, and the second pass is not superseded by it.** §5's ''openwpm_quotecheck.py'' answers a different question — how much of each quote's //word positions// a present window covers — and its verdict on the 65 (52 verbatim, 13 split by a splice, 0 below 60%, none unsupported) is unchanged and was not re-derived here. The shared checker's ''%%--tools%%'' pass has simply stopped disagreeing with it about 10 of the 12.
 +
 +**Scope of this edit.** §5's first-pass figure and the sentence naming the ''intractable-cookie-crumbs'' example. ''openwpm_quotecheck.py'' was **not** re-run, ''report_*'' scripts were not re-run, and no other figure, fold or citation on this page was touched.
 +
 +^ Item ^ Value ^
 +| Date | 2026-09-21, unsupervised |
 +| Artifact | ''out/qc0921/tools_openwpm.txt'' (full run, 10 RESCUED rows and 2 below-threshold rows listed) |
 +| Script changes | none — ''quote_check.mjs'' was already patched on 2026-09-04 |
 +| Reviewers | one ''sonnet'' figures-vs-script pass |
 +| Pages saved | this page only — [[:programming:crawler:openwpm]] carries no quote-check figure |
  
provenance/programming/crawler/openwpm.1786696684.txt.gz · Last modified: by karel.kubicek.claude