provenance:privacy:policies
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| provenance:privacy:policies [2026/09/10 18:06] – add pass-4 generic review log and the mutation-test table; document the whole-corpus lineage scan, needle specificity and the strengthened generator guards (Authored by Claude) karel.kubicek.claude | provenance:privacy:policies [2026/09/17 07:23] (current) – Markup rendering sweep: wrap escapes, CLI flag typography, and heading quotes. Authored by Claude. karel.kubicek.claude | ||
|---|---|---|---|
| Line 14: | Line 14: | ||
| | Page id | '' | | Page id | '' | ||
| | Models | Page, scripts and this log: Claude (Opus 5, with the 2026-09-09 draft written by the same model in an earlier session). Review layer: three '' | | Models | Page, scripts and this log: Claude (Opus 5, with the 2026-09-09 draft written by the same model in an earlier session). Review layer: three '' | ||
| - | | Scripts added | '' | + | | Scripts added | '' |
| - | | Write path | '' | + | | Write path | '' |
| | Accidental exposure | None. Credentials stayed in '' | | Accidental exposure | None. Credentials stayed in '' | ||
| Line 175: | Line 175: | ||
| Four reviewers, all told explicitly that the author' | Four reviewers, all told explicitly that the author' | ||
| - | ==== Pass 1 — figures against the script ('' | + | ==== Pass 1 — figures against the script (sonnet) ==== |
| Re-ran all four scripts and diffed them against the committed outputs: **byte-identical, | Re-ran all four scripts and diffed them against the committed outputs: **byte-identical, | ||
| Line 190: | Line 190: | ||
| The pass also independently re-derived a long list of figures it found correct, including the whole tool-lineage table, the per-venue table, the platform split and the probe table. | The pass also independently re-derived a long list of figures it found correct, including the whole tool-lineage table, the per-venue table, the platform split and the probe table. | ||
| - | ==== Pass 2 — citations and quotes ('' | + | ==== Pass 2 — citations and quotes (sonnet) ==== |
| Independently re-extracted the page's citekeys and quoted spans rather than working from a supplied list, and verified all 22 new BibTeX entries against the corpus index, the paper PDFs and the PETS landing pages. | Independently re-extracted the page's citekeys and quoted spans rather than working from a supplied list, and verified all 22 new BibTeX entries against the corpus index, the paper PDFs and the PETS landing pages. | ||
| Line 202: | Line 202: | ||
| It also confirmed: 34 of 34 citekeys resolve, 0 key collisions, 0 DOI or title duplicates against the live bibliography, | It also confirmed: 34 of 34 citekeys resolve, 0 key collisions, 0 DOI or title duplicates against the live bibliography, | ||
| - | ==== Pass 3 — external currency ('' | + | ==== Pass 3 — external currency (sonnet) ==== |
| Fetched rather than recalled. Its findings and their verdicts are in the //External sources// table above rather than duplicated here; in summary it **corrected** the P3P currency claim, the PoliGraph GPU claim, the APP-350 licence symmetry and the artifact-discoverability footnote, **confirmed** every dataset host, every repository date and licence, and both platform-policy quotes, and **could not verify** two things now listed under //What could not be established// | Fetched rather than recalled. Its findings and their verdicts are in the //External sources// table above rather than duplicated here; in summary it **corrected** the P3P currency claim, the PoliGraph GPU claim, the APP-350 licence symmetry and the artifact-discoverability footnote, **confirmed** every dataset host, every repository date and licence, and both platform-policy quotes, and **could not verify** two things now listed under //What could not be established// | ||
| Line 208: | Line 208: | ||
| One of its corrections was itself incomplete and was corrected in turn: it proposed replacing the discoverability footnote with a claim about Calpric and Lalaine, but a direct GitHub name search showed the genuinely unfindable pair is **PolicyLint and PoliCheck**, | One of its corrections was itself incomplete and was corrected in turn: it proposed replacing the discoverability footnote with a claim about Calpric and Lalaine, but a direct GitHub name search showed the genuinely unfindable pair is **PolicyLint and PoliCheck**, | ||
| - | ==== Pass 4 — generic ('' | + | ==== Pass 4 — generic (fable) ==== |
| No checklist. It ran on the corrected page and on this log, and it was the most productive of the four. Its own disclosures, | No checklist. It ran on the corrected page and on this log, and it was the most productive of the four. Its own disclosures, | ||
| Line 266: | Line 266: | ||
| Every block below is the file itself, inserted by '' | Every block below is the file itself, inserted by '' | ||
| - | ==== The report script — '' | + | ==== The report script — report_policies.mjs ==== |
| <file javascript report_policies.mjs> | <file javascript report_policies.mjs> | ||
| Line 717: | Line 717: | ||
| </ | </ | ||
| - | ==== The fold — '' | + | ==== The fold — policy_fold.mjs ==== |
| <file javascript policy_fold.mjs> | <file javascript policy_fold.mjs> | ||
| Line 838: | Line 838: | ||
| </ | </ | ||
| - | ==== The full-text probe — '' | + | ==== The full-text probe — policies_fulltext_probe.mjs ==== |
| <file javascript policies_fulltext_probe.mjs> | <file javascript policies_fulltext_probe.mjs> | ||
| Line 1011: | Line 1011: | ||
| </ | </ | ||
| - | ==== The quote check — '' | + | ==== The quote check — policies_quotecheck.mjs ==== |
| <file javascript policies_quotecheck.mjs> | <file javascript policies_quotecheck.mjs> | ||
| Line 1309: | Line 1309: | ||
| </ | </ | ||
| - | ==== The significance test — '' | + | ==== The significance test — policies_significance.py ==== |
| <file python policies_significance.py> | <file python policies_significance.py> | ||
| Line 1380: | Line 1380: | ||
| </ | </ | ||
| - | ==== The external checks | + | ==== The cell-by-cell table check — policies_table_check.mjs ==== |
| + | |||
| + | <file javascript policies_table_check.mjs> | ||
| + | // Assert that privacy: | ||
| + | // CELL BY CELL, not merely that each numeral occurs somewhere in the output. | ||
| + | // | ||
| + | // node scripts/ | ||
| + | // | ||
| + | // WHY this exists in addition to check_page_numbers.mjs: | ||
| + | // whether every numeral on the page appears anywhere in the concatenated script | ||
| + | // output. A mutation test on 2026-09-10 changed the `llm` row's count from 12 to | ||
| + | // 77 and the guard still passed, because 77 occurs elsewhere in the output (the | ||
| + | // PROBE size, and the public-artifacts count). A wrong figure that collides with | ||
| + | // a live one is invisible to a membership test. This one re-derives each row | ||
| + | // from the report and compares positionally. | ||
| + | // | ||
| + | // Exits non-zero on the first mismatch, printing the row it disagrees with. | ||
| + | import fs from 'node:fs'; | ||
| + | |||
| + | const PAGE = process.argv[2] ?? ' | ||
| + | const REPORT = process.argv[3] ?? ' | ||
| + | const SIG = process.argv[4] ?? ' | ||
| + | |||
| + | const page = fs.readFileSync(PAGE, | ||
| + | const report = fs.readFileSync(REPORT, | ||
| + | const sig = fs.readFileSync(SIG, | ||
| + | |||
| + | let failures = 0; | ||
| + | const fail = (what, expected, got) => { | ||
| + | failures += 1; | ||
| + | console.log(`MISMATCH | ||
| + | }; | ||
| + | const ok = (what) => console.log(`OK | ||
| + | |||
| + | // A page table row -> array of trimmed cells, markup stripped. | ||
| + | const cells = (line) => | ||
| + | line.replace(/ | ||
| + | .map((c) => c.replace(/ | ||
| + | |||
| + | const pageRows = (headerMatch) => { | ||
| + | const lines = page.split(' | ||
| + | const i = lines.findIndex((l) => l.startsWith(' | ||
| + | if (i < 0) throw new Error(`no page table whose header matches ${headerMatch}`); | ||
| + | const out = []; | ||
| + | for (let j = i + 1; j < lines.length && lines[j].startsWith(' | ||
| + | if (!out.length) throw new Error(`page table ${headerMatch} has no rows`); | ||
| + | return out; | ||
| + | }; | ||
| + | |||
| + | const reportSection = (marker) => { | ||
| + | const i = report.indexOf(marker); | ||
| + | if (i < 0) throw new Error(`report has no section ${JSON.stringify(marker)}`); | ||
| + | const rest = report.slice(i + marker.length); | ||
| + | // A section ends at the next "--- " subsection header or " | ||
| + | // column-rule line under each table is also dashes, so match the space. | ||
| + | const end = rest.search(/ | ||
| + | return (end < 0 ? rest : rest.slice(0, | ||
| + | }; | ||
| + | |||
| + | // ---------------------------------------------------------------- 1. by era | ||
| + | { | ||
| + | const want = new Map(); | ||
| + | for (const l of reportSection(' | ||
| + | const m = l.match(/ | ||
| + | if (m) want.set(m[1], | ||
| + | } | ||
| + | if (want.size < 5) throw new Error(' | ||
| + | const rows = pageRows(/ | ||
| + | if (rows.length !== want.size) fail(' | ||
| + | for (const r of rows) { | ||
| + | const [name, all, ...eras] = r; | ||
| + | if (!want.has(name)) { fail(`method row " | ||
| + | const w = want.get(name); | ||
| + | const got = [all, ...eras.map((e) => e.replace(' | ||
| + | const exp = [w[0], ...w.slice(1).map((x) => String(parseFloat(x)))]; | ||
| + | const norm = got.map((x) => String(parseFloat(x))); | ||
| + | if (norm.join(',' | ||
| + | } | ||
| + | ok(`method-by-era table: ${rows.length} rows`); | ||
| + | } | ||
| + | |||
| + | // ---------------------------------------------------------------- 2. by year | ||
| + | { | ||
| + | const want = new Map(); | ||
| + | for (const l of reportSection(' | ||
| + | const m = l.match(/ | ||
| + | if (m) want.set(m[1], | ||
| + | } | ||
| + | const rows = pageRows(/ | ||
| + | for (const r of rows) { | ||
| + | const year = r[0].replace(' | ||
| + | if (!want.has(year)) { fail(`year row ${year}`, '(not in the report)', | ||
| + | const w = want.get(year); | ||
| + | const got = [r[1].replace(/,/ | ||
| + | const exp = [w[0], w[1], w[2], String(parseFloat(w[3]))]; | ||
| + | if (got.map((x) => String(parseFloat(x))).join(',' | ||
| + | fail(`year row ${year}`, exp.join(' | ||
| + | } | ||
| + | } | ||
| + | if (rows.length !== want.size) fail(' | ||
| + | ok(`per-year table: ${rows.length} rows`); | ||
| + | } | ||
| + | |||
| + | // --------------------------------------------------------------- 3. by venue | ||
| + | { | ||
| + | const want = new Map(); | ||
| + | for (const l of reportSection(' | ||
| + | const m = l.match(/ | ||
| + | if (m) want.set(m[1], | ||
| + | } | ||
| + | const alias = { ' | ||
| + | const rows = pageRows(/ | ||
| + | for (const r of rows) { | ||
| + | const v = alias[r[0]] ?? r[0]; | ||
| + | if (!want.has(v)) { fail(`venue row ${r[0]}`, '(not in the report)', | ||
| + | const w = want.get(v); | ||
| + | const got = [r[1], r[2], r[3].replace(/,/ | ||
| + | const exp = [w[0], w[1], w[2], String(parseFloat(w[3]))]; | ||
| + | if (got.map((x) => String(parseFloat(x))).join(',' | ||
| + | fail(`venue row ${r[0]}`, exp.join(' | ||
| + | } | ||
| + | } | ||
| + | if (rows.length !== want.size) fail(' | ||
| + | ok(`per-venue table: ${rows.length} rows`); | ||
| + | } | ||
| + | |||
| + | // ---------------------------------------------------------- 4. Fisher' | ||
| + | { | ||
| + | // Only the POLICY rows are published; the UNION rows stay on the provenance page. | ||
| + | const want = []; | ||
| + | for (const l of sig.split(' | ||
| + | const m = l.match(/ | ||
| + | if (m) want.push({ sub: `${m[2]}/ | ||
| + | } | ||
| + | if (want.length !== 5) throw new Error(`expected 5 POLICY significance rows, parsed ${want.length}`); | ||
| + | const rows = pageRows(/ | ||
| + | if (rows.length !== want.length) fail(' | ||
| + | // The page orders rows for reading; match on the subgroup fraction, not position. | ||
| + | const bySub = new Map(want.map((w) => [w.sub, w])); | ||
| + | for (const r of rows) { | ||
| + | const sub = r[1].replace(/,/ | ||
| + | if (!bySub.has(sub)) { fail(`significance row " | ||
| + | const w = bySub.get(sub); | ||
| + | const base = r[2].replace(/,/ | ||
| + | if (base !== w.base) fail(`significance base for " | ||
| + | // p is printed on the page in scientific form with superscript digits | ||
| + | // ("2.65 x 10^-8" | ||
| + | // and compare the value, exponent included. An earlier form of this check | ||
| + | // compared only the first three digits and a mutation test on 2026-09-10 | ||
| + | // showed it could not see an exponent moved from -8 to -5. | ||
| + | const SUP = { ' | ||
| + | const pageP = (() => { | ||
| + | const s = r[3].split(' | ||
| + | const sci = s.match(/ | ||
| + | if (sci) { | ||
| + | const exp = [...sci[2]].map((c) => SUP[c] ?? c).join('' | ||
| + | return Number(sci[1]) * 10 ** Number(exp); | ||
| + | } | ||
| + | return Number(s); | ||
| + | })(); | ||
| + | const repP = Number(w.p); | ||
| + | if (!Number.isFinite(pageP)) { | ||
| + | fail(`significance p for " | ||
| + | } else if (Math.abs(pageP - repP) > Math.abs(repP) * 0.02) { | ||
| + | fail(`significance p for " | ||
| + | } | ||
| + | } | ||
| + | ok(`significance table: ${rows.length} rows`); | ||
| + | } | ||
| + | |||
| + | console.log(failures ? `\n${failures} MISMATCHES` : '\nAll four tables match the report cell by cell.' | ||
| + | process.exitCode = failures ? 1 : 0; | ||
| + | </ | ||
| + | |||
| + | ==== The external checks — policies_external_checks.sh | ||
| <file bash policies_external_checks.sh> | <file bash policies_external_checks.sh> | ||
| Line 1549: | Line 1723: | ||
| </ | </ | ||
| - | ==== The GitHub name-search check — '' | + | ==== The GitHub name-search check — policies_gh_search.py ==== |
| <file python policies_gh_search.py> | <file python policies_gh_search.py> | ||
| Line 1591: | Line 1765: | ||
| </ | </ | ||
| - | ==== The W3C P3P check (Playwright: | + | ==== The W3C P3P check (Playwright: |
| <file javascript policies_w3c_p3p_check.mjs> | <file javascript policies_w3c_p3p_check.mjs> | ||
| Line 1640: | Line 1814: | ||
| </ | </ | ||
| - | ==== The PETS author fetch — '' | + | ==== The PETS author fetch — policies_fetch_pets_authors.py ==== |
| <file python policies_fetch_pets_authors.py> | <file python policies_fetch_pets_authors.py> | ||
| Line 1701: | Line 1875: | ||
| </ | </ | ||
| - | ==== This page's own generator — '' | + | ==== This page's own generator — build_provenance_policies.py ==== |
| <file python build_provenance_policies.py> | <file python build_provenance_policies.py> | ||
| Line 1731: | Line 1905: | ||
| ('The quote check', | ('The quote check', | ||
| ('The significance test', ' | ('The significance test', ' | ||
| + | ('The cell-by-cell table check', | ||
| ('The external checks', | ('The external checks', | ||
| ('The GitHub name-search check', | ('The GitHub name-search check', | ||
| Line 1750: | Line 1925: | ||
| (' | (' | ||
| ' | ' | ||
| + | (' | ||
| + | ' | ||
| (' | (' | ||
| ' | ' | ||
| Line 1799: | Line 1976: | ||
| for _name, _path, _tail in OUTPUTS: | for _name, _path, _tail in OUTPUTS: | ||
| _body = read(_path) | _body = read(_path) | ||
| - | if len(_body.strip()) < 200: | + | |
| + | # these outputs is legitimately six lines long. The terminal-string check | ||
| + | # below is what catches truncation. | ||
| + | | ||
| sys.exit(f' | sys.exit(f' | ||
| if _tail not in _body: | if _tail not in _body: | ||
| Line 1873: | Line 2053: | ||
| ===== The outputs, unedited ===== | ===== The outputs, unedited ===== | ||
| - | ==== Output of '' | + | ==== Output of report_policies.mjs ==== |
| <file text report_policies-output.txt> | <file text report_policies-output.txt> | ||
| Line 2593: | Line 2773: | ||
| </ | </ | ||
| - | ==== Output of '' | + | ==== Output of policies_fulltext_probe.mjs ==== |
| <file text policies_fulltext_probe-output.txt> | <file text policies_fulltext_probe-output.txt> | ||
| Line 2705: | Line 2885: | ||
| </ | </ | ||
| - | ==== Output of '' | + | ==== Output of policies_significance.py ==== |
| <file text policies_significance-output.txt> | <file text policies_significance-output.txt> | ||
| Line 2727: | Line 2907: | ||
| </ | </ | ||
| - | ==== Output of '' | + | ==== Output of policies_table_check.mjs ==== |
| + | |||
| + | <file text policies_table_check-output.txt> | ||
| + | OK method-by-era table: 11 rows | ||
| + | OK per-year table: 12 rows | ||
| + | OK per-venue table: 7 rows | ||
| + | OK significance table: 5 rows | ||
| + | |||
| + | All four tables match the report cell by cell. | ||
| + | </ | ||
| + | |||
| + | ==== Output of policies_quotecheck.mjs ==== | ||
| <file text policies_quotecheck-output.txt> | <file text policies_quotecheck-output.txt> | ||
| Line 2819: | Line 3010: | ||
| </ | </ | ||
| - | ==== Output of '' | + | ==== Output of policies_external_checks.sh ==== |
| <file text policies_external_checks-output.txt> | <file text policies_external_checks-output.txt> | ||
| Line 2964: | Line 3155: | ||
| </ | </ | ||
| - | ==== Output of '' | + | ==== Output of policies_w3c_p3p_check.mjs ==== |
| <file text policies_w3c_p3p_check-output.txt> | <file text policies_w3c_p3p_check-output.txt> | ||
| Line 2996: | Line 3187: | ||
| <bibtex bibliography></ | <bibtex bibliography></ | ||
| + | |||
| + | ===== Markup sweep, 2026-09-17 ===== | ||
| + | |||
| + | Mechanical rendering repair only: a fresh live raw/XHTML export of 188 pages was checked with '' | ||
provenance/privacy/policies.1789063613.txt.gz · Last modified: by karel.kubicek.claude
