Skip to content

SIPP income, assets, pension and SSI readers (D2) - #581

Merged
MaxGhenis merged 35 commits into
masterfrom
ir/d2-20261024
Nov 6, 2026
Merged

MaxGhenis merged 35 commits into
masterfrom
ir/d2-20261024

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Round 13 approved 9e4849f9 with no high or medium finding. The head, 25057f4d, merges master 02897ede into that commit and recounts the tiers, and CI passes there. The note guard, record_bearing_note, refuses every page 085a33b9's guard refuses across the 412,000 invented pages round 12's reviewer tried. It refuses none of the 101 stored captures, and each of its 49 twin pages isolates its rule or limit.

What it adds

  • src/populace_dynamics/data/sipp_income.py: read_sipp_income_months, a person-month reader.
    • It selects concepts explicitly and reads pipe-delimited files with usecols.
    • It reads quotes as literal characters (quoting=csv.QUOTE_NONE), so it sees the same physical records as the layout validator.
    • It keeps identifiers as strings and holds values to strict domains.
    • It can attach the calendar-year replicate weights.
  • src/populace_dynamics/data/sipp_income_registry.py: canonical concepts and each release's Census definitions: labels, source and storage types, codes, ranges, universes, ownership level, period, corrections and user notes. It needs no network access at run time.
  • data/external/sipp_income/: the evidence and its tooling.
    • Evidence: the captured Census metadata, 107.5 MB in 228 tracked files, none over 4.2 MB.
    • Tooling: the dictionary parser, the registry generator, the concept selection, the cross-release crosswalk, label and domain checks, refusals and source rulings.
    • The folder's .gitattributes marks every file -text, so git stores the bytes the registry hashes, CRLF line endings included.
  • Tests: tests/income_resources/test_sipp_income.py, test_sipp_income_evidence.py and test_sipp_income_refusal_properties.py.

Concepts: we select 561, covering:

  • SSI and Social Security receipt and amounts;
  • assets, debts and net worth, with each item's ownership level;
  • retirement accounts and pensions, with employer offer and participation from 2021;
  • disability;
  • citizenship, nativity and year of entry;
  • living arrangement;
  • the design and linkage variables (panel, wave, month, person and household identifiers, the monthly weight, the variance stratum and half sample, and 241 calendar-year replicate weights).

Verification sources: the Census variable API verifies the 2022–2025 releases. Its 2018–2021 endpoints return 404, so we verify each of those releases against its own dictionary and storage schema.

Each disagreement between sources carries a named ruling, or the registry generator refuses it:

  • source_rulings.json names 13 domain rulings covering all 64 release-and-variable disagreements in domain_checks.json, with both sources' locations and intervals;
  • the registry generator refuses a disagreement that lacks an exact, cited, settled ruling.

User notes: the inventory lists 355 Census user notes and indexes. The branch stores 101 of them (93 notes and 8 indexes). Eight notes quoted individual survey cases, so the inventory keeps them as exclusions: their URL, title, release and variables stay, but not their bodies. The capture script refuses those URLs before any request. A guard, record_bearing_note, refuses to store any new page holding a case record in any form it lists:

  • an identifier column, or a cell that has a cell of digits alone below it and begins with an identifier name or, in a first-row or header cell, names one anywhere;
  • an identifier name followed by a digit with only spaces or simple marks between, or by a run of three or more digits that is not a year, with at most 40 characters and no period between;
  • a table row with a cell that begins with an identifier name, or in a first-row or header cell names one anywhere, and, in any other cell, even one that names an identifier too, a run of three or more digits that is not a year, even in a variable-definition table;
  • a table row, or a preformatted line, of three or more numbers;
  • a run of nine or more digits in the text a reader sees, or of 14 or more, an SSUID's length, anywhere in the page;
  • a preformatted block with an identifier and a digit.

The two rules that follow an identifier name read three things, all with entities decoded, apart from xmp and plaintext contents in the browser views:

  • the visible text, read four times, each time with a tag counted as a space. Twice it keeps HTML comments as text: once with every span from a < to the next > taken for a tag and each script or style element that an end tag closes left out, and once as a browser with scripting off reads it, where a tag opens only with an ASCII letter, ! or /, an element's name, folded as ASCII, ends at whitespace, / or >, the reading leaves out script and style contents, and reads title, textarea and xmp contents as text, each up to </ and its name followed by whitespace, / or >, or to the end of the page, and plaintext contents as text to the end of the page. The third and fourth times it reads the text as a browser with scripting on and with scripting off shows it: the same way, but with comments left out where a browser starts and ends them, at --> or --!>, with <!--> and <!---> empty, and <? instructions up to the next >; with iframe, noembed, noframes and template contents left out too, and with scripting on noscript contents, all but template contents read as raw text, as a browser reads them, so that a script opening inside one opens nothing; with xmp and plaintext contents not decoded; and with a NUL dropped from the text outside title, textarea, xmp and plaintext contents, where a browser shows it as U+FFFD;
  • the page source with its tags, attributes and scripts, where the 40 characters also hold no angle bracket, quote or equals sign. This reading first removes href and src values, whose slugs name identifiers without values, from every tag outside scripts and styles and from their opening tags. It reads script and style contents, up to their end tag or the end of the page, as they stand, so no < in script code hides a src assignment. It reads the source twice: once with those elements found where a browser starts and ends them, in one scan from the start of the page, so that a <script inside a quoted value opens none, and once with one starting at every <script or <style, even inside another tag;
  • one at a time, each quoted attribute value other than href and src; each double- or single-quoted string in a script, as a scan from the script's start finds them, even one that an escaped quote mark opens; each template literal in a script; and each quoted string in a style, all read past their backslash escapes.

The guard reads the text and the source each more than one way because the browser's way, alone, would admit some pages that the first way refuses, and the text with comments kept reads comment text, which no browser shows, and noscript contents, which a browser with scripting on does not show. A tag opens with an ASCII letter, ! or /. The table and preformatted rules read each cell's or block's text without its tags; the row-header exemption also reads a header cell's scope attribute. The nine-digit rule skips comments and noscript blocks, whose tracking numbers are not page content. Each reading takes time linear in the page's length. A test times invented pages of 100 kB to 3 MB in the forms found to take quadratic time before (a long word in a tag, escaped quotes after an unpaired quote in a script, unclosed tags and comments, script start tags with no end tag, and table rows and cells that name an identifier) and in those where a slower form of a linear helper, found in review, does (a hyphenated word in a tag, unclosed tags after closed ones, script start tags with no >, after end tags or before one end tag, identifier rows after rows of digits, scripts that end tags close, empty or each holding a string, one script of many strings, many quoted attribute values, comments and style strings, together or the values and strings alone, and unclosed comments before tags).

We leave these forms out of scope: a value before its identifier name; <br>-separated CSV text without an identifier; div grids; an identifier name split by a tag or an invisible character, as in PN<b></b>UM or SS&shy;UID, or written in other characters, such as fullwidth letters; text that a style or the hidden attribute hides between an identifier and its value, since a browser hides it by its styles rather than as it parses the page; an identifier name and its value in different attribute values, script strings or style strings, or one of them in a string and the other in script code; a script string after a lone quote mark in a comment, a regular expression or a template literal, which the scan of double- and single-quoted strings takes to open one, as in a comment such as // it's; an identifier and its value split by a tag, quote or equals sign in script or style code outside a quoted string, as in a comparison PNUM < 101 or markup in a text/template script, or in an unquoted attribute value, as in alt=PNUM's-101; a value inside a tag after an identifier in the text, as in PNUM <i 101; a run of digits split by a tag, which the readings count as a space, after a mark the adjacency rule does not allow, a plural name or other words, as in PNUM &gt;1&#49;<th>7 or PNUMs 10<b>1; a script that a <!-- and a <script inside it double-escape, which a browser runs on past its first </script>, as in <script><!--<script>.</script> . </script> between an identifier and its value, where every reading ends it at that end tag and shows the period; template contents that a browser ends after the first </template>, which the views take to end the template, in a template nested in another or wherever a browser does not read that end tag as one: inside a tag, as in <e</template>, a comment, a <? instruction or raw-text contents; markup inside svg or math, where a browser reads a CDATA section as text and the name of a raw-text or plaintext element as an ordinary element's; markup that a browser ignores inside a select element, such as a style, noembed or plaintext opening; nine digits after a comment closed by --!>, which the nine-digit rule reads as still open; a quoted attribute value in an end tag or a <! declaration, or after a name with marks other than _ : . -, as in [alt]; an attribute value that holds a raw >, which ends its tag; a value in an href or src value, unless it is a run of 14 or more digits; and a cell outside a table's first row and header cells that names an identifier after other words.

The rules in turn refuse some pages that hold no case record, and such a page stops the capture loudly:

  • definition and index tables that name an identifier in a heading cell with digits below it, or beside a run of three or more digits that is not a year in its row, as in a definition row whose description reads SSUID. Values start at 101. or an index row SSUIDs. See 112 notes.;
  • a quoted value that names an identifier before a count across an apostrophe or decoded markup, as in a page summary The SSUID's format in the 2018 panel, as 112 notes say;
  • a template literal that holds JSON naming an identifier before a count, as in `{"name": "SSUID", "pages": "112"}`, since the rules read a template literal whole;
  • a link slug such as ssuid-2024 in a script string or a text/template script, or in an xlink:href, :href or img.src value: the source reading removes those values as link targets, but the quoted-value reading skips only values named href or src;
  • a malformed tag whose quoted href value holds a script opening, as in <a href="<script src="/ssuid-2024.js">: the browser scan of the source removes the href value, which closes at the quote mark before the slug, and reads the slug;
  • a definition that prints a comparison such as PNUM < 101 in the visible text;
  • a --> in a quoted attribute value, as in an arrow alt="Next -->", after an identifier and before a value in the value's rest, as in PNUM<img alt="--> 7">: the browser views end the tag at that > and read the rest of the value as text;
  • markup that the readings take another way than a browser does, which brings together an identifier and a value that a browser shows apart or not at all: a <plaintext> tag inside a comment or inside iframe, noembed, noframes or template contents, which the text with comments kept takes to open plaintext there, as in <!--<plaintext>PNUM<e101>; character references in plaintext contents, which that text decodes, as in <plaintext>PNUM<!&#49;01>; a plaintext or xmp opening or a NUL inside svg, math or select, or a NUL after a frameset opening; other markup that a browser ignores inside a select element or after a frameset opening; a reference to a control character, such as &#4, which Python's html.unescape drops; an empty end tag </>, which a browser drops and the readings count as a space, as in PNUM</>a567; and a page that ends inside a tag, whose rest a browser drops, as in <a/b="><plaintext><PNUM>101.

A decimal character reference of more than 4,300 digits makes Python's html.unescape raise, which stops the capture too.

Invariants

  • Only a verified release, concept, source type and value domain may enter. Every verified source must admit a value, apart from the API's documented missing sentinels.

  • A domain on which sources disagree enters only under an exact, cited, settled ruling.

  • One input person-month yields one output row, in input order. A quote never joins two physical records, and a carriage return never splits one: the reader yields one row per LF-delimited data line, or refuses. The reader refuses a malformed layout.

  • Person identifiers stay strings, stable within a panel across months, waves and releases.

  • Source codes and weights pass through unchanged. The reader does no household or joint allocation and no annualization.

  • The corrected 2018–2021 TSSSAMT requires a file downloaded on or after Census's 2025-07-24 correction.

  • Replicate joins need verified, unique, complete keys and matching base weights.

  • We use each release's own metadata. The frozen tables reproduce the hashed evidence without network access.

  • No stored page shows a form record_bearing_note refuses.

  • The guard still makes every reading the round-8 guard (085a33b9) made, with the same result, and only adds readings to them, so it admits no page that guard refuses.

    • Three Hypothesis properties state this rule against a frozen copy of that guard (tests/income_resources/fixtures/capture_user_notes_085a33b9.py.txt, with a test that pins its sha256). One draws pages from the parts of the round-9 review's forms. One draws lists of invented tokens, those of the differential property plus record tokens (adjacency marks, digit runs of each rule's length, comments, preformatted, noscript and table blocks, quoted values). The third builds a record from an identifier or none, a separator (each adjacency mark, a tab, a line end, &nbsp; or &#58;), a value of 1, 3, 9 or 14 digits, plain or as decimal or hexadecimal references, and a wrapper that puts it where one reading or rule reads it.
    • Before the verdict, all three check that the readings made by functions both guards share agree on each page: the text, with and without comment marks, the text a reader sees, the source split at every script or style, the quoted values, whose list begins with that guard's, the tables and preformatted blocks found with that guard's patterns, and _identifier_column on each table. The browser readings and the way the guard joins and reads its readings have no counterpart there, so only the verdict covers them.
    • Record pages pin the forms that only one reading refuses. A differential property checks each linear helper, the attribute pattern, the table rules and both browser views against the plain forms they stand for. Both sides of its view check use the same patterns, so a second test checks the views against 25 invented pages and the texts html5lib shows of them, written out; one, a nested template, holds the view's stated departure.
  • The guard reads a page in time linear in its length: a test reads 20 invented pages of 100 kB to 3 MB, one in each form that the earlier guard or a slower helper read in quadratic time, in under 5 s of CPU time each. The guard took at most 2.08 s on the first 18 under Python 3.13 and 2.19 s under 3.14.7, both on the 3 MB page, and under 1 s on the other two under 3.13.

  • Each guard rule, and each limit on what the identifier rules read, has a minimal invented page and a twin that differs only in what it reads (49 pairs): disabling the rule admits its page, and dropping the limit refuses its twin. Invented record and definition pages pin the exemptions, the bounds on link-target removal, and each path that only one of the readings takes, for the forms the reviews found.

  • Each mutant in these sets fails a test, apart from those named as changing no result:

    • round 9's 105 in Hypothesis seeds 0 and 7 at 16ea8c94, and at seed 0 at d7442ef5, and ported, at 955db507 and at d576600f;
    • the round-9 review's X1-X11, X15, X16 and Q1-Q7, X12r and round 10's N1-N14, in seeds 0 and 7 at 7ae8edbf, and ported, at seed 0 at 955db507 and at d576600f;
    • round 11's C1 and V mutants, the round-10 review's M1-M6 and RB1-RB9, and the slower forms T1, T3, T10, T11 and T13-T15, in seeds 0 and 7 at 955db507, and T12 in seeds 0 and 7 at 076e4310 and, ported, at seed 0 at d576600f;
    • this round's PT, VO, PI, NU and DX mutants of the new parts and the slower form SV1, and the round-11 review's RN1-RN8, RN10, RN11 and RN15, W1, W3, W4, W8, W9, W16, W18 and W19, and SF1-SF4 and SF6, in seeds 0 and 7 at d576600f, and SF1, SF2 and RN3 in seeds 0 and 7 at bc408371.

    Q1-Q7, G82-G89, T1, T3, T10-T15, SF1-SF4, SF6 and SV1 each fail the timing test on their own page. X12r (casefold for lower) and N14 (an end tag with no > ends nothing) give the head's result on every page: the browser readings' names are ASCII, which casefold and lower fold alike, and the first text reading reads any text after the last > as it stands. PT7, plaintext added to the browser scan of the source, gave the head's verdict on every test page and on 520,000 random invented pages, but one page tells them apart: the head's scan reads plaintext contents as tags, so it refuses <plaintext><script-x><a PNUM href="." 101>, which holds no record, and PT7 admits that page, as 085a33b9 does. C1a-src (the browser source alone with \b) differs from the head: the definition page <script-x>x</script\x0b><a href="/notes/pnum-101.html">Person number</a> tells it apart. At d576600f, SF2 passed the timing test in both seeds, SF1 passed it at seed 0, and RN3 failed only at seed 0, through a property. bc408371 answers all three, as the Round 11 entry says.

  • Each ruling member's PDF citations, with a page or without, match its dictionary entry's pages one for one, each as {year}_dictionary.pdf#page={page}.

Each invariant has a Hypothesis property or an example test; until round 10, the rule that the guard admits no page 085a33b9's guard refuses had neither. The domain rule and Census's own crosswalks give differential tests.

Open questions

Each of these refuses the affected concepts; refused_concepts.json and source_rulings.json list every case.

  • Early monthly Social Security receipt (ESSSMNYN, 2018–2020): it has no verified codes. Annual receipt and amounts remain.
  • ESSI_BRSN, 2021–2022: the category order conflicts.
  • The 2020 joint asset and debt families: they disagree with Census's correction note.
  • Disability definitions: 2020 TDIS6AMT and EDIS8MNYN conflict.
  • Missing codes or ranges: 2021 TSSI_ERSN and EBOATOWNER3 lack value codes, and the 2021 interview address identifier has an inverted printed range.
  • SSI0 inputs not yet verified: we have found no RMD identifier or retirement-fund access input.

Tests

Counted by collection over the three test modules:

Tier Tests
unit 183
artifact 415

All 598 pass locally with two workers at 0f016a81, b9f66c46 and 25057f4d, and so do 12 of the 13 birth-evidence seal tests. The other, the reachability test's track-U assertion, needs modules this sparse clone leaves out; the D2, CE1, D4 and SSI0 blocks before it pass. CI runs that test and the U2 isolation tests. At 0f016a81, CI run 41358633577 reported 19,847 unit tests against a manifest count of 19,664 and 4,686 artifact tests against 4,271; the other three tiers matched. At 9e4849f9, after the recount, CI passes, the tier test included: run 41623422531's four pytest shards report 30,005 passed, 1,116 skipped and 1 xfailed. At 25057f4d, after the second recount, CI passes, the tier test included: run 42204606713's four pytest shards report 32,287 passed, 1,116 skipped and 1 xfailed.

Review

  • Round 1 stopped before it finished, for a reason unrelated to the code.

  • Round 2 (fresh, at 1f51b60a): REVISE, with one high and two medium findings.

    • High: eight stored user notes quoted individual survey cases.
    • Medium: a quote in an unselected field could join two physical records, silently dropping person-months.
    • Medium: 64 admitted domain disagreements had no named rulings.
  • Fix (eb77fc19):

    • removes the eight bodies and adds the exclusions and guard;
    • sets QUOTE_NONE, with a minimized regression and a quote property;
    • adds the 13 rulings and the generator's refusal;
    • adds properties for the vintage gate, malformed layouts, replicate keys, source conflicts, ownership, periods and evidence pins.

    The regenerated concept, definition, release, replicate, correction and variable-to-note tables match 1f51b60a's. The note table keeps all 101 entries: the eight exclusions have an empty body path and the status excluded_record_bearing. The evidence pins lose the eight bodies and change only for source_rulings.json and user_notes_manifest.json.

  • Round 3 (delta, at eb77fc19): REVISE, with two medium and two low findings.

    • Medium: the capture guard missed record forms such as long-format value tables, row headers, prose with words between an identifier and its value, preformatted blocks and plain-text CSV.
    • Medium: a lone carriage return in an unselected field split one physical record into two person-months for pandas while the layout check saw one.
    • Low: the THEQ_BUS ruling misdescribed the dictionary's printed range.
    • Low: the generator did not check a ruling's PDF page citations.
  • Fix (7521c6d8):

    • the guard refuses the forms then listed under User notes. Tests add 16 invented record pages (the round-3 guard missed 13), 4 invented definition pages it still admits, and three properties. Every stored note still passes;
    • _check_layout strips one LF, then one CR, and refuses any other carriage return. Tests add the review's minimized regression, a CRLF file and a carriage return in the quote property's alphabet;
    • the THEQ_BUS reason now says the dictionary prints a malformed upper bound and a lower bound that differs from the API's. The fix regenerates the pin of source_rulings.json and changes no other table;
    • the generator checks PDF citations against the dictionary pages, and the damage property adds a wrong page and another release's PDF.
  • Round 4 (delta, at 7521c6d8): APPROVE, with seven low findings and four notes.

    • Low: three guard rules had no test that failed without them, because every invented record page also tripped the digit-run rules.
    • Low: the identifier rules skipped scripts and attributes, which the stored page holds and a browser does not show.
    • Low: a year beside an identifier name refused definition pages and index links.
    • Low: the three-number rule missed Census's negative-dollar form and preformatted lines. (Round 4 also listed <br> CSV text and div grids; those stay admitted and out of scope.)
    • Low: the notes, the docstring and the round-3 commit message overstated three rules.
    • Low: the PDF citation check ignored citations without a page.
    • Low: no test pinned the 40-character window.
  • Fix (04a2b89a):

    • each guard rule now has a minimal refused page and an admitted twin (eight pairs);
    • the identifier rules read the page source, and a 14-digit run refuses anywhere;
    • the 40-character and same-row rules exempt years from 1900 to 2099, with admitted year pages and a property;
    • numeric cells admit the signed-dollar, Unicode-minus and bracketed forms, and preformatted lines of three numbers refuse;
    • the generator checks every .pdf citation, and each damage case must fail at its own check;
    • a test pins the window at 40 characters;
    • the fix corrects the THEQ_BUS reason, the reader's docstring and the notes. Only the source_rulings.json pin changes in the registry.

    17 of the 129 evidence tests fail against 7521c6d8's guard, and the tests catch all 16 mutants. Every stored note still passes. (Round 5 corrected four claims in this fix's message: the eight twins covered five rules and three readings, not every rule; the 16 mutants had two for the window width and none for the digit-below rule or the row-header and definition-table exemptions; only pageless_pdf passes generation under the parent check, while pageless_other_release_pdf stops on a file missing from the invented folder; and 14 of the 16 invented record pages, not all, carried the invented SSUID. The heading correction also needs "that is not a year".)

  • Merge (ffc4d485): origin/master at 1ec57a5d (D1a, PSID income and wealth readers and field registry (D1a) #575). The birth-evidence check keeps D1a's exclusions and reachability assertion, then D2's.

  • Round 5 (delta, at ffc4d485): APPROVE, with five low findings and five notes.

    • Low: reading the page source refused pages that only name an identifier, and index links: the 40-character window ran across attribute boundaries, and the adjacency rule fired on link slugs.
    • Low: mutants of the new rules survived every test, because both source rules refused each source twin.
    • Low: four claims about the tests overstated them (listed above).
    • Low: the notes and this body overstated which rules read the source, and the same-row rule's condition.
    • Low: the guard still admitted some record forms; the review gave a fix for an identifier inside a heading.
  • Fix (c1a3c7b6):

    • in the source, the window holds no quote or equals sign, and the guard removes href and src values from tags before the identifier rules read it (the 14-digit rule still reads them). The guard now admits the review's pages that only name an identifier, as definition pages, and still refuses a 14-digit run in a link target;
    • twins now cover every rule and each source-reading limit (17 pairs), with record pages for the year exemption's lookahead and its absence from the adjacency rule, a row header outside a definition table, two numbers per preformatted line, and a property over preformatted lines;
    • the guard refuses a first-row or header cell that names an identifier anywhere, with a cell of digits alone below it;
    • the fix corrects the notes, the docstring and the reader's carriage-return sentence;
    • found by this fix's own mutants: the visible-text window stopped at an angle bracket, though the text holds no tags by then, so a decoded &lt; or &gt; between an identifier name and a value admitted the page. The window there now stops only at a period, as the round-3 rule meant ("within 40 characters of the same sentence"); the source reading still stops at tags. A twin pins it, 18 pairs in all.

    With ffc4d485's guard, 9 of the 153 evidence tests fail, the same 9 in each of three Hypothesis seeds. The tests catch all 32 guard mutants in seeds 0 and 7, each of the 18 twins isolates its rule or limit, and every stored note still passes. (Round 6 corrected two claims in this fix: the 18 twins did not cover the heading-cell rule, each mark that stops the source window alone, unquoted href and src link targets, or adjacency in the text alone; and only this body, not the docstring, put the other L5 forms out of scope. The parent already admitted two of the "now admitted" definition pages, tracking_number_in_link_target and row_header_outside_definition_table.)

  • Merge (60b67f82): origin/master at d8302233 (SM1, SM1: differential mortality by lifetime earnings, education and race (normalized within age-sex-year cells) #570). It has no conflicts; git auto-merged the birth-evidence check and its script, which both branches changed.

  • Round 6 (delta, at 60b67f82): APPROVE, with three low findings and four notes.

    • Low: the new source limits admit an identifier and its value in different attribute values or script strings, or split by a quote or equals sign within one, and values in link targets; the docstring's "stay inside one attribute value or string" overstated the window.
    • Low: nine of the review's ten mutants of the changed rules survived, so the twin claims overstated the tests.
    • Low: the guard admitted a header cell that names an identifier after other words, with a value elsewhere in its row.
    • Notes: the heading-cell rule refuses some definition and index tables; an angle bracket in a script comparison hid a src assignment from the source reading; four wording corrections; the guard now refuses PNUM &gt; 100.
  • Fix (c8dfe4f1):

    • the docstring, the notes and this body list the forms the source limits admit as out of scope, with the review's other residual forms;
    • twins pin each mark that stops the source window alone, unquoted href and src link targets, adjacency in the text reading alone, and both heading-cell rules, 26 pairs in all. Record pages pin the heading-names rule's two cell conditions, and a definition page pins its limit to those cells;
    • a first-row or header cell that names an identifier anywhere now also counts for the same-row rule, as the review proposed;
    • a tag opens with a letter, ! or /, so a comparison in a script no longer hides a src assignment;
    • the fix corrects the notes, the docstring and the test comments.

    With c1a3c7b6's guard, 2 of the 165 evidence tests fail, the same 2 in each of three Hypothesis seeds: the two that pin this fix's code changes. The tests catch all 44 guard mutants in seeds 0 and 7, each of the 26 twins isolates its rule or limit, and every stored note still passes. (Round 7 corrected two claims in this fix: a minified comparison, a CDATA marker or a</b in a script still hid a src assignment, and the tests pinned the bounds on link-target removal only against a < followed by a space or digit.)

  • Merge (695798ed): origin/master at 0f69bbb2 (OP1, OP1: open-population harness around the sealed loop #566, and its report fix, Fix review r10's report findings for OP1 #589). It has no conflicts; git auto-merged the birth-evidence check and its script, which both branches changed.

  • Round 7 (delta, at 695798ed): APPROVE, with four low findings and four notes.

    • Low: a < followed by a letter, / or ! in a script still opened a "tag" whose href and src values the guard removed, so a minified comparison, a CDATA marker or a</b hid a src assignment.
    • Low: the source window stops at any angle bracket, raw or decoded, which the lists did not say, and the out-of-scope list left out angle-bracket splits and script comparisons.
    • Low: the same-row rule skipped any other cell that began with an identifier name, which no list stated.
    • Low: the review's mutants of the tag pattern and of the same-row rule's cell conditions survived.
    • Notes: the heading-cell rules also refuse some definition and index rows that hold a value in their row; the quote twins depend on their comma; a condition always held; the guard now refuses round 5's L4b page.
  • Fix (6e96c64d):

    • the source reading splits out script and style elements, keeps their contents as they stand, and removes href and src values only from their opening tags;
    • the identifier rules also read each quoted attribute value other than href and src, and each quoted string in a script, one at a time, decoded and joined by periods;
    • the same-row rule reads every other cell, even one that begins with an identifier name;
    • record and definition pages pin each of the review's surviving mutants, and seven new twins pin the new rules and limits, 33 pairs in all. Two older twins now use forms that only the source reading refuses;
    • the docstring, the notes and this body name the angle-bracket limit, the new out-of-scope forms and the rows the table rules refuse.

    With 695798ed's guard, 26 of the 201 evidence tests fail, the same 26 in each of three Hypothesis seeds: the ones that pin this fix's code changes. The tests catch all 67 guard mutants in seeds 0 and 7; this fix's own checks found four of them unpinned, and record pages now pin them. Each of the 33 twins isolates its rule or limit, the guard admits no page the parent guard refuses over the test pages, the review's probes and round-6 forms, and 2 × 100,000 random invented pages (round 8 found one that it admits: <a href="<script src="PNUM=101">), and every stored note still passes.

  • Merge (085a33b9): origin/master at 172f690f (Held-out data registry: round 7's follow-ups (unset event path, record ids, zero push boundary) #580 and the commits before it). It has no conflicts and changes no D2 path.

  • Round 8 (delta, at 085a33b9): APPROVE, with four low findings and five notes.

    • Low: two new value patterns took quadratic time on long runs, a word in a tag or escaped quotes in a script (78 s for a 40 kB word).
    • Low: the script and style split found <script inside a quoted value, so the guard admitted <a href="<script src="PNUM=101">, which the parent refused.
    • Low: template literals, text/template scripts, style strings, a raw < in visible text, textarea and title contents and an unquoted attribute value admitted record forms that no list named.
    • Low: ten of the review's mutants of the changed rules survived.
    • Notes: Hypothesis deadline flakes under load; href and src read two ways; newly refused definition and index forms; the commit's mutant wording; twin construction (no change).
  • Fix (c501f04d), under one added rule, that the guard may admit no page 085a33b9's guard refuses:

    • the fix keeps every reading that guard made, with the same result, and makes each run in linear time: attribute names start only at the start of a run; the guard reads script strings with each mark that no unescaped mark of its kind follows set aside; element and tag patterns stop searching at the page's last >; the guard skips a script or style opening that no end tag of its name follows; comment and noscript removal stop at the last end tag; and the table rules read each row and column once. A differential property checks each helper against its plain pattern, and a timing test reads seven 200 to 600 kB pages, one in each form that took quadratic time before;
    • the guard also reads the source with script and style elements found where a browser starts them, so it refuses <a href="<script src="PNUM=101">; the earlier reading stays, so the guard admits no page that reading refused;
    • the guard also reads the text as a browser reads it, which refuses PNUM < 101, PNUM <- 101 and PNUM: <101> in visible text, the review's textarea and title forms and an xmp form; it reads template literals in scripts and quoted strings in styles alone;
    • the fix names the remaining admitted forms (text/template markup, an unquoted attribute value, a value inside a tag, and attribute values in end tags, declarations or after bracketed names) out of scope, with a script string after a lone quote mark in a template literal; it names the newly refused definition and index forms and the href and src asymmetry among the pages the rules refuse;
    • this fix's own checks found two readings that differed from round 8's for script names that (?i) reads with a long s or a dotted or dotless i: the text reading raised on <SCRİPT>x</script>, and the script-string reading admitted a page the parent refuses. Both now match round 8's on every such spelling, on Python 3.10 to 3.14;
    • record pages pin each of the review's O1-O10, and five new twins pin the new readings, 38 pairs in all; the guard's Hypothesis properties run without a deadline.

    With 085a33b9's guard, 26 of the 171 guard tests fail, the same 26 in each of three Hypothesis seeds: the ones that pin this fix's code changes (13 record pages, 5 twins, the 7 long pages and the differential property). The tests catch all 105 guard mutants, the review's ten among them: 104 in seeds 0 and 7 at c501f04d, and G89, which gives the same results in quadratic time, in both seeds at 16ea8c94. That commit enlarges the timing test's row to 60,000 identifier cells (780 kB), and corrects c501f04d's message, which said the timing test alone pinned 8 quadratic mutants; it pinned 7. G82 to G88 still fail it in seed 0. Each of the 38 twins isolates its rule or limit. Over the test pages, both reviews' probes and 1,200,000 random invented pages, the guard admits no page the parent guard refuses, and every stored note still passes. (Round 9 corrected six claims in these commits: the differential property left out the attribute pattern and the table rules; O1, O3 and O5, ported to the split alone, survive; both guards refuse F6, the <b> form of F3, but 085a33b9 admits F3; round 7's fix had 8 pages, not 7; the 458 tests include the 12 birth-evidence tests; and 16ea8c94 also rewrote the timing test's comment.)

  • Round 9 (delta, at 16ea8c94): APPROVE, with four low findings and six notes.

    • Low: no test stated the rule that the guard admits no page 085a33b9's guard refuses, and mutants that break it, of the split alone and of template literals, passed every invented page.
    • Low: no list named some admitted record forms; some came from the browser readings' Unicode case folding and end tags.
    • Low: most of the review's mutants of the new readings survived.
    • Low: the timing test pinned only its seven forms; six slower forms of the linear helpers passed it.
    • Notes: claim gaps in the round-8 fix and this body; newly refused definition forms; timing-test robustness and its comment (no change); _finditer's reliance on (?s); the exact test command outlasts one shell call.
  • Fix (7ae8edbf, and d7442ef5 for one passage of the notes), under the same rule:

    • a frozen copy of 085a33b9's guard sits in the test tree as text, out of collection and out of the tier classifier's reach, and a Hypothesis property asserts that the guard refuses every page the copy refuses. It draws pages from the parts of the review's forms with invented tokens between them; its explicit examples and four record pages reproduce the review's X1, X3, X5 and X9. The differential property also checks the attribute pattern and the table rules;
    • the browser readings fold ASCII letters only, in element names, end tags and a tag's first letter, and end a raw-text element at </ and its name followed by whitespace, / or >; shown() compares names in lower case. They now refuse E1, E2, E4, U1, U4 and L1-L4, and record pages hold them. The ASCII flags cover only those parts, since a global one would also change \b;
    • these changes touch only the browser readings, so the rule holds; they do not only add refusals against 16ea8c94. Of 400,000 random invented pages, the guard admits 91 that 16ea8c94 refuses, each of which 085a33b9 admits too, where 16ea8c94 departed from a browser: it read a <SCRİPT> or <scrıpt> element's contents as raw text, ended a script at </scrİpt>, or took <ſ for a tag;
    • record pages pin X2, X4, X7, X8 and X10, a definition page pins X11, and X12's page holds a record; pages pin this fix's own mutants of the new parts;
    • five long pages, and the escaped-quote page at 1 MB, pin Q1-Q6;
    • the docstring, the notes and this body name the admitted forms the review listed, and a tag that ends with -->, which this fix's checks found; they name D8 and F2 among the pages the rules refuse, and _finditer's docstring names its reliance on (?s).

    With 085a33b9's guard, 25 of the 38 new or changed tests fail, the same 25 in each of three Hypothesis seeds: 18 record pages, the six long pages and the differential property, which calls _finditer. The other 13 pass: the four record pages of forms that guard refuses itself, the seven definition pages, which pin mutants that refuse more, the rule property, which holds for the guard it names, and the fixture's pin. Of this round's 35 mutants, 33 fail a test in seeds 0 and 7, and the other two change no result; all of round 9's 105, rerun at seed 0 at d7442ef5, fail a test. Each of the 38 twins isolates its rule or limit. Over the test pages, the probes of rounds 6 to 9 and 400,000 random invented pages, the guard admits no page the parent guard refuses, and it refuses none of the 101 stored captures. (Round 10 corrected three claims in 7ae8edbf's message: the guard refuses 25 of the round-9 reviewer's 34 record probes, up from 16, not 26 up from 17; <ſcript> stays text because the ASCII-scoped name never captures ſ, so casefold and lower agree on every name, not because lower keeps it apart; and the claim that plain token lists found none of the four forms in 3,000 pages holds for one list alone, since a 24-token list of the forms' chunks found X1, X3 and X5 first after 306, 101 and 526 pages.)

  • Round 10 (delta, at d7442ef5): APPROVE, with five low findings and five notes. The rule held over 105,000 pages of a tokenizer-edge grammar, and 085a33b9 refuses none of the 91 pages that 16ea8c94 refused and the head admits.

    • Low: a comment or text a browser hides, between an identifier and its value, hides the record, in forms no list names.
    • Low: \b after a browser element's name departs from a browser, and accounts for one of the 91 pages.
    • Low: the rule property reaches only the four round-9 forms, and two rule-breaking mutants pass every guard test.
    • Low: the tests pin this round's end-tag and opening rules only in part.
    • Low: a slower form of the closed-script helper passes the timing test.
    • Notes: claim gaps in 7ae8edbf's message and the token-list claim; no third pair of readings for the 91 pages; timing-test robustness (no change); S5 and other contrived forms unnamed; the notes-only commit holds (no change).
  • Fix (955db507), under the same rule:

    • a browser element's name ends where a browser ends it, at whitespace, / or >, so <script-x>, <script.x>, <scripts> and the other forms the review listed open no script or style. Record pages hold seven forms, one for each mark another check of a name's end would pass;
    • the text has a third reading, as a browser with scripting on shows it: comments left out where a browser starts and ends them, even one that holds a >, and iframe, noembed, noframes, noscript and template contents hidden. The reading that keeps comments still reads noscript contents, so <noscript>PNUM <b>101</b></noscript> stays refused. The source reading needs no such change: a script a browser would not open there hides no text from it, and reading those elements as raw text would read their tags' link targets. Record pages hold the review's six pages and 13 more, and the view also refuses the review's <!-->, <!--->, <![CDATA[-->, <!- --> and noembed forms;
    • a second property draws pages from invented tokens plus record tokens, and both rule properties check the shared readings before the verdict. Record pages pin #, [, ' and ( before a digit and a noscript block. By itself, the property catches RB3, RB4 and RB7 in seeds 0 and 7; every RB mutant changes guard test pages;
    • record pages hold M1, M2, M3 and M5's forms;
    • three long pages join the timing test: script start tags before one end tag, 120,000 closed scripts (2.2 MB, twice the brief's size, since at 60,000 T1 and T3 took about twice the 5 s bound) and unclosed comments before tags. In CPU-capped children, T10 passes 25 s, and T1, T3 and this round's T13-T15 pass 20 s, on their pages; the guard takes at most 1.65 s on any of the 15;
    • 076e4310 adds a sixteenth page, 200,000 closed scripts that each hold a string (4 MB). The host pass at 955db507 showed that T12 passed the timing test, because no page held scripts with strings in them, though the draft of this body said it failed. The guard takes 1.2 s on the new page and T12 15.7 s. That commit's message calls 1.2 s a tenth of 15.7 s; it falls short of a tenth, at about a thirteenth;
    • the view refuses on its own the pages that pinned eight ported mutants of the browser text alone, so seven record pages pin them again inside a comment or a noscript block, or with a textarea after a comment. Two round-8 twins move inside a comment for the same reason, and four new twins pin the view and the name's end, 42 pairs in all;
    • the docstring, the notes and this body name the forms that stay admitted, S5 among them, and a new form the rules refuse that holds no case record: a --> in a quoted attribute value.

    Over the test pages, the probes of rounds 6 to 9 and 400,000 random invented pages, the guard admits no page the parent guard refuses. It admits 171 of those pages that d7442ef5 refuses, all of which 085a33b9 admits; html5lib shows a record on 9, each a named form. Over the round-10 review's grammar seeds 2 to 4, it admits 217 more pages that d7442ef5 refuses; 085a33b9 refuses none of them, and html5lib shows a record on 5, each a named form. Over those seeds, the record forms it admits fall from 611 to 330. It refuses none of the 101 stored captures, and each of the 42 twins isolates its rule or limit. With 085a33b9's guard, 47 of the 58 new or changed tests fail, the same 47 in each of three Hypothesis seeds (the timing part at 076e4310, the rest at 955db507). The 58 count the fixture's pin, which neither commit changed. The other eleven pin what that guard already does: five record pages, two definition pages, three long pages and the fixture's pin. Of this round's 50 mutants, 48 fail a test in seeds 0 and 7 at 955db507, T12 fails its new page in seeds 0 and 7 at 076e4310, and C1a-src passes every test. Of the 140 ported mutants, 138 fail a test at seed 0, and X12r and N14 give the head's result. (Round 11 corrected three claims in this entry and the commits: the entry and the commits did not name every record form that stays admitted; a page tells C1a-src apart from the head; and 1.2 s comes to about a thirteenth of 15.7 s, not an eighth.)

  • Round 11 (delta, at 076e4310): APPROVE, with six low findings and four notes. The rule held on every page the reviewer tried, among them 88,000 pages of a new grammar and 60,000 drawn pages.

    • Low: two rule-breaking mutants, RN4 (the SSUID-length rule on the undecoded page) and RN6 (only a literal space before a digit), pass every guard and timing test.
    • Low: round 10's comment and hidden-content forms survive inside noscript contents, and no list names them.
    • Low: a page tells C1a-src apart from the head, though this body said none did.
    • Low: four mutants of the view's own rules survive, since the differential property builds both of its sides from the view's pattern.
    • Low: four slower forms of linear helpers pass the timing test.
    • Low: no list names several more admitted record forms, so "all named" overstates.
    • Notes: claim gaps (the view's "hides no text a browser shows", the "eighth", the 58 tests, the timing test's comment, _identifier_column left out of the rule's comparison, seeds 2 to 4's 217 departures); the 4 MB page cut the timing margin to about 1.55×; X12r and N14 give the head's result on every page and C1a-src does not; 076e4310 holds (no change).
  • Fix (d576600f), under the same rule:

    • the text has a fourth reading, the view with scripting off: the view's pattern without noscript. It refuses all 11 of the review's noscript probes, the named <b--> form among them, so that out-of-scope clause goes. Record pages hold four of them;
    • a plaintext element runs to the end of the page in the browser text and both views, as in a browser, and the views show its contents undecoded; five record pages pin it, in all three readings at once and in each alone. The browser scan of the source does not change: plaintext there changed no verdict on 520,000 pages;
    • the views read a <? instruction as a comment up to the next >, drop a NUL from the text and show one in title, textarea, xmp and plaintext contents as U+FFFD, and show xmp contents undecoded, as html5lib does outside svg and math. These close the review's NUL, <?, plaintext and xmp forms, and the clauses that named some of them go;
    • the double-escaped script and a template that a browser ends after the first </template> stay admitted, since closing them needs a reading that tracks the script escape states or template nesting; the docstring, the notes and this body name them;
    • record pages hold <!-- &#49;2345678901234 -->, <img alt="PNUM\t7"> and <script>PNUM\n7</script>, so RN4 and RN6 fail them. A third rule property builds records from their parts, with RN4's and RN6's forms as examples, and the rule's shared check also compares the tables and preformatted blocks and _identifier_column;
    • a definition page holds the review's C1a-src page: html5lib shows the link's text and no slug, and the guard admits it;
    • record pages hold W3, W9, W18 and W19's forms after a comment, and a new test checks both views against 25 invented pages and the texts html5lib shows of them, written out. Every mutant of the views' own rules changes one of those examples, apart from the scripting-off comment rule and a NUL after the last match, which the differential property catches;
    • two long pages join the timing test, a script of 500,000 strings (2 MB) and 60,000 quoted values, comments and style strings (2.2 MB), and the page of closed scripts with strings falls to 3 MB. In CPU-capped children, SF1 took 10.7 s, SF2 7.0 s and SF3 28.4 s, SF4 passed 30 s, and T12, SF6 and this round's SV1 passed 15 s on their pages. The guard takes at most 2.08 s on any of the 18 under Python 3.13 and 2.19 s under 3.14.7, against the 5 s bound;
    • the view's docstring now says that after a double-escaped script it can open an element a browser never opens, and the timing test's comment gives the measured times;
    • the view with scripting off masks three older mutants' pages, so record pages put N3's and G76's forms in a comment and RB8's comment text in a noscript block; the mutant set ports three round-10 mutants, X6, N5 and N6, to the new end tag; five twins pin the new readings, 47 pairs in all.

    Over the test pages, rounds 10 and 11's probes, 300,000 token-set pages, 100,000 pages of round 10's review grammar and 100,000 of round 11's, the guard admits no page the parent guard refuses. It admits 55 of those pages that 076e4310 refuses, all of which 085a33b9 admits; html5lib shows a record on one, a plaintext opening inside math, a named form. That count covers the builder's sets; round 12's grammar finds 14 more, listed under round 12. Over the round-11 review's seeds 21 to 23, it admits 353 record forms in 3,189 hits, against the review's 526 in 3,385, and a classifier that puts each form in one bucket, by the named cause whose removal ends the hit, explains all of them: a raw > in a quoted value 249, a value split by a tag 59, double-escaped scripts 23, template ends 18, svg or math 4. It refuses none of the 101 stored captures, and each of the 47 twins isolates its rule or limit. With 085a33b9's guard, 53 of the 64 new or changed tests fail, the same 53 in each of three Hypothesis seeds. Of this round's 45 mutants, 41 fail a test in seeds 0 and 7, and of the 190 ported mutants, 188 fail a test at seed 0; X12r and N14 give the head's result. PT7 gives the head's verdict on the builder's test and random pages; round 12 gives a definition page that tells them apart. SF1, SF2 and RN3 led to the next fix.

  • Fix after the host pass (bc408371), tests only:

    • on the host, SF2 read the shared page in about 3.4 s and SF1 in about 4.9 s, so SF2 passed the 5 s bound in both seeds and SF1 at seed 0. Two pages join the timing test, 300,000 quoted values and 150,000 style strings (2.7 MB each). SF2 reads the first for about 35 s, SF1 reads the second for about 9.5 s, and the guard reads each in under 1 s;
    • RN3 failed only when a property drew a year outside the range. SSUID in 1899 against SSUID in 1900 pins that floor, and SSUID in 2100 against SSUID in 2099 pins the ceiling.

    SF1, SF2 and RN3 each fail in seeds 0 and 7 at bc408371.

  • Round 12 (delta, at bc408371): approved, with no high or medium finding, four low findings and six notes. No page that 085a33b9's guard refuses gets past the head, over the test pages, 412,000 pages of the reviewer's grammar and every probe. The head refuses none of the 101 stored captures, all 49 twins isolate, and the parent check reproduces. Load stopped 16 of the 20 chosen builder mutants in the review; the builder's host logs show 42 of 45 caught at each of seeds 0 and 7.

  • A review's exit rule now applies. A finding that only invented inputs reach, with zero effect on pinned inputs, stays as a stated limit. A regression, a falsified claim, a changed record on a pinned input, or a misread real rule still gets a fix or a narrowed claim.

  • 0f016a81 narrows the claims round 12 falsified, in text only: the guard's and the test module's syntax trees match bc408371's, docstrings aside. This body narrows the same claims.

    • The out-of-scope clause names a template's end tag wherever a browser doesn't read it as one, where it named two forms.
    • The refused list gains markup a browser ignores inside a select element or after a frameset opening, and references to control characters with or without a NUL.
    • The comments on the view's rules, the token lists and the timing test now match the review's measurements. This body's PT7 and RN3 sentences change too.
  • Stated limits, one line for each form:

    • Three mutants survive: ending the text window at ! or ?, exempting an exact identifier in a definition table's first row, and requiring a digit after an identifier in a preformatted block (RR4, RR5 and RR22).
    • Eight slower helpers pass the timing test: SCRIPT, STYLE, TABLE, PRE, ELEMENT, ROW and CELL read with finditer, and the mark regex without its lookbehind. Each ran past 20 s on its own page under 3 MB, where the head takes under 0.6 s.
    • The template forms above reach a record on invented pages, as in <p>PNUM <template><!--</template>-->.</template> 101</p>.
    • Plaintext in all three browser readings brings back forms 076e4310 refused: 14 pages of the reviewer's grammar, all plaintext inside svg or math, and each sits in a named clause, and 085a33b9 admits them all.
    • One of the 41 newly refused pages fits no named category: <template><e</template>Ssuid<noscript>.</noscript><?>1.
    • The 3 MB page leaves about twice the head's time against the 5 s bound, so a CI core much more than twice as slow as this host fails the timing test.
  • Merge turn (b9f66c46, 9e4849f9): b9f66c46 merges master 97003ac2 and with it Federal SSI interface and source-to-input contract (SSI0) #573 (SSI0) and Aggregate-table captures and readers, part 1 (D4) #574 (D4). Each had added a block to the birth-evidence exclusions, their expected tuple and the reachability test, where D2 adds its own. The merge keeps master's blocks and puts D2's after them, so the exclusions and the expected tuple list the same 221 paths in the same order. Both sides add the same empty tests/income_resources/__init__.py. 9e4849f9 recounts the tiers on 97003ac2: master's 30,508 tests plus D2's 183 unit and 415 artifact make unit 23,836, artifact 4,942, integration_psid 1,471, reproduction_legacy 520 and oracle_policyengine 337, 31,106 in all.

    From 0f016a81 to 9e4849f9, 339 paths changed: the two tier files, the two birth-evidence files and 335 other paths that master changed since 172f690f. All 335 match master's blobs, and none of D2's other paths moved.

  • Round 13 (delta, at 9e4849f9): approved, with no high or medium finding, four low findings and two notes, all on this body's wording. The reviewer replayed resolve.py and the merge proof to the same bytes and found no path outside the four merge exceptions that differs from both parents. The two Python files 0f016a81 changed match bc408371's syntax trees, docstrings aside. The reviewer's own collection gives 183 unit and 415 artifact tests. With host load at 147, the reviewer skipped the other three tiers, where the host collections add none and CI counts them all.

    • Low: the ! or ? limit described RR4's mutation as the head's rule; the head's window ends only at a period.
    • Low: the Tests section called CI's manifest the branch's, whose blob holds 18,789 unit tests to CI's 19,664.
    • Low: the round 11 entry's sentences on the view examples and PT7 still made the claims 0f016a81 narrowed.
    • Low: "the concept selection picks" and "No release borrows metadata" gave actions to concepts.
    • Notes: nothing the reviewer could open supported "every file" in the evidence bullet or "an Opus 5.5 reviewer" in the round 12 entry.
  • Body update (no commit; the head stays 9e4849f9): this body takes the review's wording for all four lows and drops the reviewer's model from the round 12 entry. The folder's .gitattributes reads * -text, so "every file" stands.

  • Merge turn on 02897ede (2ca74c50, 25057f4d): 2ca74c50 merges master 02897ede and with it Coverage-rule registry (CE1) #583 (CE1), which added a block to the birth-evidence exclusions, their expected tuple and the reachability test, where D2 adds its own. The merge keeps master's blocks and puts D2's after them, so the exclusions and the expected tuple list the same 222 paths in the same order. 25057f4d recounts the tiers on 02897ede: master's 32,790 tests plus D2's 183 unit and 415 artifact make unit 23,919, artifact 7,141, integration_psid 1,471, reproduction_legacy 520 and oracle_policyengine 337, 33,388 in all.

    From 9e4849f9 to 25057f4d, 272 paths changed: the two tier files, the two birth-evidence files and 268 other paths that master changed since 97003ac2. All 268 match master's blobs, and none of D2's other paths moved. Each birth-evidence file holds master's text plus the lines D2 inserted from 97003ac2 to 9e4849f9 and nothing else, and running resolve.py on a fresh git merge-tree reproduces both byte for byte.

The eight removed bodies remain in this branch's earlier commits. A squash merge keeps them out of master's history.

Status

  • The build ran in three attempts; two stopped on network errors. Commits 0482dc31 and 5bdd595b hold the later attempts' edits. 1f51b60a lists the two reader modules among the birth-evidence check's post-review exclusions; dac0d5f9, ffc4d485, 60b67f82, 695798ed, 085a33b9, b9f66c46 and 2ca74c50 merge master, and 9e4849f9 and 25057f4d recount the tiers.
  • The coordinator merges this PR in the tier-count slot, by squash. If another PR lands first, this branch merges master and recounts the tiers.
  • No reviewer has read 2ca74c50 or 25057f4d. The coordinator accepted the merge proof above in place of a delta review under four conditions, and reran it at 25057f4d.

Data

We downloaded no SIPP microdata, opened none and computed no statistic from it, because we will hold a SIPP release out for the SSI gate. Tests use invented records and committed metadata.

axiom: n/a: survey-file readers and metadata registry for the microsim; no PolicyEngine country-model or RuleSpec encoding changed

🤖 Generated with Claude Code

MaxGhenis and others added 9 commits October 24, 2026 09:31
Pins the documentation the SIPP income-and-resources registry is verified
against, with no microdata requested or opened:

- the Census SIPP variable API for 2022-2025 (2018-2021 return HTTP 404;
  the 404 bodies are kept);
- each release's data dictionary PDF, its pdftotext layout and the parsed
  per-variable metadata, the public-use and replicate storage schemas,
  the replicate dictionaries and Census's Python input examples;
- the locator pages they were found from.

capture_manifest.{txt,json} records every URL, UTC retrieval time, byte
size and SHA-256; derived_manifest.json pins the derived files and the
parser. The parser now refuses an inverted range (min above max) instead
of admitting an empty domain: the 2021 dictionary prints EHRESIDENCID as
"Minimum 100001, Maximum 40999". A folder .gitattributes keeps the CRLF
pages and input examples byte-identical to their pinned hashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sipp_income_registry.py embeds, per release 2018-2025, each admitted
concept's Census name, label, type, value codes and ranges, generated by
data/external/sipp_income/build_registry.py from the committed metadata
only (API for 2022-2025, the release dictionary and storage schema for
2018-2021). Unknown releases, concepts and codes raise; a concept absent,
suppressed or without a verified domain in a release is refused there,
with the reason in refused_concepts.json.

sipp_income.py reads a pipe-delimited pu{year} file with usecols, every
column as a string first, then validates each token against that
release's exact domain before conversion. SSUID stays a string; the
person key is panel:SSUID:PNUM; replicate weights attach many-to-one on
SSUID, PNUM and MONTHCODE only when the sidecar's keys are unique and
complete and REPWGT0 equals WPFINWGT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Invented pipe-delimited files only (INVENTED_* names); no staging
directory is read. Covers column selection, dtypes, refusal of
unverified releases, concepts and codes, release-specific sentinels,
the entry-year and roster crosswalks, replicate linkage, and Hypothesis
properties: row count and order preserved, person ids stable across
months and waves, weights carried unchanged, unverified codes refused.
The evidence tests replay the registry from the committed metadata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…otes

Verification now uses every Census source a release has and records each
disagreement:

- 2018-2021 labels come from the release dictionary (the verifying
  source); the storage-schema label is kept and checked against it. Four
  conflicts: 2018 EJSOWNRE/EJSOWNRP resolve to the dictionary on Census's
  user note; 2020 TDIS6AMT (state vs local) and EDIS8MNYN (military vs
  railroad) stay open and are refused.
- 2022-2025 values must satisfy both the variable API and the release
  dictionary (API missing codes exempt). This admits GHLFSAM and GVARSTR,
  whose API entries list only a missing code, and narrows owner pointers
  to Census's documented codes (domain_checks.json lists all 72 cases).
- source_rulings.json holds every hand ruling with its evidence; the
  generator stops on an unruled label conflict. Open: ESSI_BRSN in 2021
  and 2022 (code order flips against Census's 2018 correction note) and
  70 joint-asset concept-years in 2020 whose metadata predate Census's
  2020 joint-assets note.
- TSSSAMT for 2018-2021 is gated on the download date: Census re-released
  those files on 2025-07-24 with corrected amounts under the same names.
- Captured the release notes, Census's 2018-2022 crosswalks, the 2018
  metadata and topic dictionaries, and screened all 355 user-note pages;
  the 109 naming an admitted variable are stored and attached per concept.
- release_changes.json lists every change in name, label, universe,
  codes, domain, missing codes or admission across releases.
- Census's input examples are stored as .py.txt (bytes unchanged) so the
  repository-wide black and ruff checks skip them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…walk

Unit tier (107, invented pipe-delimited files only):
- design codes in 2022-2025 come from the dictionary when the API lists
  only a missing code; a value one source admits and the other does not
  refuses (owner pointers 70 versus 80);
- the TSSSAMT gate refuses 2018-2021 reads without, or before, a
  2025-07-24 download date, before any file is opened;
- ESSI_BRSN, the 2020 joint families and the 2020 disability sources are
  refused where their sources conflict; universes travel per release;
- Hypothesis: row count and order preserved, person ids stable across
  months, waves and releases, weights and replicate weights carried
  unchanged, an unverified code always refuses, and a differential test
  that the reader admits exactly what an independent restatement of the
  domain rule admits (400 examples over every numeric concept).

Artifact tier (72): the registry replays from each release's own
metadata; schema labels agree with the dictionary or carry a ruling;
every refusal has a cause in the evidence; each ruling's claim appears in
the Census note it cites; the crosswalk agrees with Census's 2018-2022
crosswalk workbooks except one recorded Census anomaly; the change ledger
holds the documented universe and label changes; user notes, captures,
derived files and the regenerated tables match their pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The variable API documents a missing code for WPFINWGT in 2022-2025
(-99900000000 in 2022, -999.00000000 in 2023-2025). The reader admitted
it as a value and never checked the weight, so a file carrying it would
have entered with a weight of -999. Every structural key and the weight
must now be present and differ from each of that release's documented
missing codes, compared as decimals. A parametrized test covers all 32
documented key and weight missing codes; on the previous reader the four
weight cases loaded without error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
metadata_notes.md now records the captures, the per-release verification
rules, every ruling and open question, and the Census anomalies found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two adversarial reviews (code, and claims against the Census evidence)
found problems; each fixed one was reproduced first.

Reader (sipp_income.py):
- Count fields on every line before parsing. With usecols pandas stops
  checking, so a short row's missing fields read as structural blanks
  and an extra field shifted values between columns. Repeated header
  names and corrupt or truncated gzip files also refuse.
- Tokens must be plain ASCII decimals, and string-stored identifiers the
  dictionary's fixed-width digits (SSUID 14, ERESIDENCEID 6, SHHADID 3,
  GVARSTR 3, GHLFSAM 2). Decimal() accepted "+11", "1.2345E4", "0_1"
  and full-width digits, which split one key into several or put the
  record value into a pandas error message.
- downloaded_on accepts a datetime.

Registry and generator:
- Blanks are refused for items whose universe is all persons or all
  households (citizenship, nativity, household status, design codes).
- 2022-2025 API labels are checked against the dictionary too (no
  conflicts); a variable one source suppresses and another documents is
  a ruled conflict (2018 EDIS8MNYN, open).
- 2018 EJSOWNRE/EJSOWNRP take label and universe verbatim from Census's
  correcting user note; the dictionary's EJSOWNRP universe read "or".
- Misprinted universes are cleared under cited rulings: 2021 ERETTYPnYN
  (empty as printed) and 2024-2025 TRET1AMT-TRET7AMT (all name source 8).
- The dictionary parser reads comma-grouped codes (TSCNTAMT1/2's
  9,999,999 special code now verifies and is labelled) and marks 2018-2020
  entries whose value list runs onto an unjoined page as unverified
  (30, 33 and 33 entries; none selected).
- Census record levels: SSI spell items and child Social Security flags
  are person-month (period monthly); ETENURE and TLIVQTR household;
  SSUID, SPANEL, SWAVE design keys; EHRESIDENCID person.
- The registry embeds its tables as one JSON string literal, so
  regeneration needs no formatter pass over megabytes of tables.
- Text corrections: 101 user notes are stored (plus 8 index pages; the
  earlier commit said 109 notes), the 2020 joint-debt treatment, the
  roster slot 15 years, the ERETANY and TSSAGESS changes, and the
  correction ruling no longer claims unchanged file names.

Tests: the evidence suite passes (77) with an independent boundary probe
of every API-dictionary difference and a record-level check; reader tests
gain layout, spelling, universe-blank, datetime and string-identifier
differential cases (full reader run pending on a loaded host).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Attempt a2 stopped on a network error while rerunning the reader tests;
these edits have not yet passed that run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
social-security-model Ready Ready Preview Nov 6, 2026 10:42am UTC

Request Review

MaxGhenis and others added 3 commits October 24, 2026 16:07
Eight design-code cases now supply zero-padded tokens and check that an
unpadded token is refused; the ninth expects the recorded Census
correction note as the label source. The notes' count of concept-years
where the API and dictionary domains differ is 64, not 72.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The birth-evidence identity check seals every source under
src/populace_dynamics except listed opt-in modules. List the two new
reader modules as post-review exclusions, in the test's expected tuple,
and in the reachability test, which asserts the historical reducer
cannot import them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Record-bearing user notes (high). Eight stored Census user notes carried
individual survey identifiers with case values. Their whole-page captures
are removed. The note inventory keeps each one's URL, title, release and
variables, marked excluded with no stored body. The capture script now
refuses those URLs before any request and refuses any note page whose
tables or text carry identifier columns or identifiers followed by
values. A test checks every stored capture with that guard, and its
failure message names only the file. The evidence pins drop the eight
bodies, and metadata_notes.md corrects its claim and its counts (93
stored notes).

CSV quoting (medium). The reader's pandas call now passes
csv.QUOTE_NONE, so its record boundaries agree with the physical-line
layout validator. A quote in an unselected field can no longer absorb
the next person-month. Adds the minimized invented regression and a
property with quotes in selected and unselected fields.

Domain rulings (medium). source_rulings.json gains 13 named
domain-conflict rulings covering all 64 API/dictionary disagreements,
each listing its members and citing both sources' locations. The
generator now refuses a disagreement without an exact, cited, settled
ruling. Every admitted domain is unchanged: only the ruling references
and their pins move.

Also adds Hypothesis properties for the vintage gate, malformed layouts,
replicate keys and the registry's source-conflict, ownership and
evidence-pin invariants, on invented inputs through the production code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M1, the capture guard. record_bearing_note now refuses a page holding any
of these forms, and its docstring lists them:
- a column headed by an identifier name, including a heading that opens
  with one, such as "SSUID (sample unit)";
- a table row with an identifier name and, in another cell, three or
  more digits, even in a variable-definition table;
- a table row of three or more numeric cells;
- an identifier name followed by digits with only punctuation between,
  or by three or more digits within 40 characters of the same sentence,
  in the text or inside an HTML comment, which is stored though not
  shown;
- a run of nine or more digits in the text a reader sees;
- a preformatted block naming an identifier and holding a digit.
Variable-definition rows and prose may still name an identifier without
a value. Tests add 16 invented record pages, which the guard refuses
(the parent guard missed 13 of them), and 4 invented definition pages,
which it admits. Three properties cover identifier-and-value sentences,
long digit runs where rendered, and rows of three numbers. Every stored
user note still passes the guard. metadata_notes.md now states the
guard as these forms.

M2, physical records. _check_layout strips one LF and then one CR from
each line, and refuses any other carriage return in a record. The line
check and pandas therefore see the same records: QUOTE_NONE already kept
quotes from joining lines, and a lone CR can no longer split one. Tests
add the review's minimized regression (two invented rows joined by a CR
in an unselected field, which gave two person-months before), a CRLF
file that keeps every record, and a carriage return in the quote
property's alphabet. The property expects one row per LF-delimited data
line, or a refusal.

L1. The THEQ_BUS ruling's reason now describes the printed range as it
is: its upper bound is malformed, and its lower bound differs from the
API's. The registry pin of source_rulings.json is regenerated.

L2. build_registry checks that a ruling member's PDF citations name its
own release's dictionary at exactly the pages its dictionary entry
holds. The invented ruling cites its page, and the damage property adds
a wrong page and another release's PDF.

Invariants:
- No stored page shows a form record_bearing_note refuses.
- The reader yields one person-month per LF-delimited data line, or
  refuses.
- Every ruling member's PDF citations equal its dictionary pages.

Tests (invented data only): test_sipp_income 183 passed;
test_sipp_income_evidence 106 passed; test_sipp_income_refusal_properties
6 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 25, 2026 07:58
Round 4 approved 7521c6d with seven low findings and four notes. This
commit answers each.

L1, rules without a failing test. Every invented record page carried the
15-digit invented SSUID, so the digit-run rules refused each page before
the rule it was meant to test. INVENTED_RULE_TWINS pairs a refused page
with a twin that differs only in what one rule reads, and the test
requires the rule to refuse the page and admit the twin. There are
eight pairs: an identifier beside a three-digit value in a definition
row, an exact identifier heading, a preformatted identifier with a
digit, a preformatted line of three numbers, a table row of three
numbers, and short values in an attribute, a script and a comment with
markup inside it.

L2, stored content not shown. The identifier rules now also read the
page source with its tags, attributes and scripts, entities decoded, and
a run of 14 or more digits, an SSUID's length, refuses the page wherever
it appears. The nine-digit rule still skips comments, noscript blocks,
scripts, styles and tags, where tracking numbers sit; the tracking
definition pages now put a 12-digit number in comment text after a ">",
in a noscript block and in an attribute, each admitted.

L3, years beside identifier names. The 40-character and same-row rules
no longer count a year from 1900 to 2099. Admitted pages add a universe
footer without a period, a panel year, an index link titled with a year
and a definition row with a year, and a property draws identifier
sentences ending in any such year, in text, a comment or an attribute.
An identifier followed directly by digits, such as "SSUID 2022", still
refuses.

L4, numeric forms. A numeric cell now admits Census's negative-dollar
forms (-$250, $-250, the Unicode minus) and bracketed amounts, and a
preformatted line of three or more such numbers refuses. The property's
numeric text draws these forms too.

L5, claims. The docstring, metadata_notes.md and the module header now
state the rules as the code applies them. A correction to 7521c6d's
message: it said a column headed by a heading that opens with an
identifier, "such as SSUID (sample unit)", refuses. Only a cell that is
exactly an identifier name refuses as a heading; a cell that opens with
one refuses only when a cell of digits alone sits below it or a value of
three or more digits sits in its row.

L6, PDF citations. Every citation containing ".pdf", in any case, now
counts: a member's PDF citations must equal one
"{year}_dictionary.pdf#page={page}" per page of its dictionary entry.
The damage property adds a pageless own-release PDF, a pageless
other-release PDF and a release-notes PDF with a page. Each damage now
runs as its own case and must fail at its own check, by message. With
the parent check, the two pageless damages pass generation.

L7, the window. A test pins the 40-character window: 40 characters
between an identifier and a three-digit value refuse, 41 admit.

Notes. The THEQ_BUS reason now says only the malformed upper bound makes
the entry unverified, and that its lower bound also differs from the
API's; the regenerated registry changes only the source_rulings.json
pin. The reader's docstring now says quotes are literal characters, so
the line check and pandas split the same records, and only a stray
carriage return refuses.

Invariants:
- No stored page shows a form record_bearing_note refuses (all 101
  stored captures pass).
- Each guard rule alone refuses its minimal page and admits its twin.
- Every ruling member's PDF citations equal its dictionary pages, one
  citation per page.

Results:
- 17 of the 129 evidence tests fail against 7521c6d's guard.
- 16 mutants, one per guard rule or exemption, are all caught.
- Tests (invented data only): test_sipp_income 183 passed;
  test_sipp_income_evidence 129 passed;
  test_sipp_income_refusal_properties 15 passed. Tiers: unit 183,
  artifact 144.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	scripts/first_estimates_birth_evidence.py
#	tests/estimates/test_birth_evidence_artifact.py
MaxGhenis and others added 2 commits October 25, 2026 15:18
The round-5 delta review (an Opus 5.5 reviewer, at ffc4d48) approved, with
five low findings and some notes.

- L1: reading the page source refused pages that only name an identifier,
  and index links, because the 40-character window ran across attribute
  boundaries and the adjacency rule fired on link slugs such as
  "ssuid-2024". In the source, the window now holds no quote or equals
  sign as well as no period or tag, so it stays inside one attribute value
  or string, and href and src values are removed from tags before the two
  identifier rules read it. The 14-digit rule still reads them. The
  review's four pages that only name an identifier, and an anchor whose
  title is an identifier name, are now admitted definition pages, as are
  a tracking number in a link target outside noscript and a row header
  outside a definition table. A 14-digit run in a link target, such as a
  timestamp, is still refused.
- L2: the mutants that survived now fail tests. Twins pin the window and
  the adjacency rule each alone in the source, a tag and a quote as
  window limits, and the removal of link targets; record pages pin the
  year exemption's lookahead and its absence from the adjacency rule; a
  definition page with two numbers per preformatted line and a property
  over preformatted lines pin that rule's threshold and line split; and a
  row header outside a definition table pins that exemption.
- L3: twins now cover the digit-below, adjacency, nine-digit and 14-digit
  rules too, so every rule, and each limit on what the identifier rules
  read, has a refused page and an admitted twin (17 pairs). The
  40-character window in visible text is pinned by the width test, which
  has letters between.
- L4: the notes and the docstring now say which rules read the source
  ("the two rules that follow an identifier name"), that the same-row rule
  needs a cell that begins with an identifier name, and that the window
  allows no period between, and in the source no tag either.
- L5a: a first-row or header cell that names an identifier anywhere, as
  in "Person number (PNUM)", with a cell of digits alone below it, is now
  refused. The other forms the review lists (a value before its
  identifier, <br> CSV text with no identifier, div grids) stay out of
  scope, as the docstring states.
- Notes: the reader's carriage-return sentence now says a CR is allowed at
  a line's end, before its LF or last in a final line without one. The
  other notes need no change: the top-code row is the three-number rule's
  accepted cost, and the year exemption admits a 1900-2099 value after an
  identifier with words between.

Found by this fix's own mutants: the visible-text window stopped at an
angle bracket, though the text holds no tags by then, so a decoded &lt; or
&gt; between an identifier name and a value admitted the page. The window
there now stops only at a period, as the round-3 rule meant ("within 40
characters of the same sentence"); in the text and comments a tag counts
as a space, and the source reading still stops at tags. A twin pins it,
18 pairs in all, and mutant G23 puts the old limit back.

Corrections to 04a2b89's message:
- "Each guard rule alone refuses its minimal page and admits its twin"
  held for the eight pairs then present, which covered five rules and
  three readings; the digit-below, adjacency, nine-digit and 14-digit
  rules had no twin until now.
- "16 mutants, one per guard rule or exemption": the 16 had two for the
  window width and none for the digit-below rule or the row-header and
  definition-table exemptions.
- "With the parent check, the two pageless damages pass generation":
  pageless_pdf does, but pageless_other_release_pdf stops earlier on a
  file missing from the invented folder.
- "Every invented record page carried the 15-digit invented SSUID": 14 of
  the 16 did; header_after_title_row_with_dashed_values and
  person_months_without_identifier don't.
- The heading correction's "a value of three or more digits sits in its
  row" omits "that is not a year".

Checks:
- Each of the 18 twins isolates its rule or limit: disabling the rule
  admits its page, and dropping the limit refuses its twin, with the head
  refusing each page and admitting each twin.
- All 32 guard mutants fail at least one test, in Hypothesis seeds 0
  and 7, and the guard is restored after each.
- With ffc4d48's guard, 9 of the 153 tests in
  test_sipp_income_evidence.py fail, the same 9 in each of three
  Hypothesis seeds.
- Every stored capture still passes the guard (the stored-capture test).
- The three D2 test modules through the wrapper: 351 passed. By
  collection, unit 183 and artifact 168, 24 artifact tests more than at
  ffc4d48.
- black and ruff are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master brings SM1 (#570). The merge has no conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 25, 2026 22:23
Round 6 reviewed c1a3c7b and its merge 60b67f8 and returned APPROVE,
with three low findings and four notes. This commit answers each.

- Low 1: the source window's limits admit an identifier and its value
  that sit in different attribute values or script strings, or that a
  quote or equals sign splits within one, and a value in an href or src
  value other than a 14-digit run. The docstring said the window stays
  "inside one attribute value or string", which overstated it. This
  commit takes the review's first option: the docstring, metadata_notes.md
  and the PR body now list these forms as out of scope, with a value
  before its identifier, CSV text, div grids and a body cell that names
  an identifier after other words. The review's second option, reading
  each attribute value and script string alone, would close three of the
  seven forms; the script forms would need a rule of their own, and the
  link-target form would stay.
- Low 2: nine of the review's ten mutants of the changed rules survived,
  so "every rule and each limit has a twin" overstated the tests. New
  twins pin each mark that stops the source window alone (equals sign,
  single quote, double quote), unquoted href values and src values,
  adjacency in the text reading alone, and both heading-cell rules: 26
  twins in all. Two record pages pin the heading-names rule's cell
  conditions, a header cell after the first row and a first-row cell that
  is not a header cell, and a definition page pins its limit to those
  cells.
- Low 3: a first-row or header cell that names an identifier after other
  words now also counts for the same-row rule, so a run of three or more
  digits that is not a year elsewhere in its row is refused (the review's
  K2). A twin pins it. A body cell that names an identifier after other
  words stays out of scope; the review's K1 would also refuse such
  definition tables.
- Note 1: the heading-cell rule refuses some definition and index tables.
  The docstring and the notes now say so; no stored capture is refused.
- Note 2: a tag now opens with a letter, "!" or "/", so a comparison such
  as "1 < 2" in a script no longer hides a src assignment from the source
  reading. A record page pins it, and the test comment says so.
- Note 3, corrections to c1a3c7b's message and to the notes:
  - "the other L5 forms stay out of scope, as the docstring states": only
    the PR body stated it; the docstring does from this commit;
  - "are now admitted definition pages": the parent already admitted
    tracking_number_in_link_target and row_header_outside_definition_table;
    they are new test pages, not new admissions;
  - "The 40-character window in visible text is pinned by the width test":
    the width page is also refused by the source reading, so the test
    pins the width in both readings; comment_text and
    text_window_crosses_decoded_brackets pin the text reading alone;
  - the notes said the table rules read cells without their attributes,
    but the row-header exemption reads scope. The notes now say so, and
    their unreflowed line is reflowed.
- Note 4: "PNUM &gt; 100" is now refused, as "PNUM greater than 100"
  already was. No change.

Checks:
- each of the 26 twins isolates its rule or limit
  (twin_isolation_r7.py);
- all 44 guard mutants fail a test in Hypothesis seeds 0 and 7: round 6's
  32, and 12 new ones, for the review's R1-R7, R9 and R10, the double
  quote, the same-row rule's heading cells and the tag pattern
  (mutate_guard_r7.py);
- with c1a3c7b's guard, 2 of the 165 evidence tests fail, the same 2 in
  seeds 0, 1 and 2: the script-comparison record page and the twin for
  the same-row rule's heading cells, which pin this commit's two code
  changes. The other new tests pin limits the parent already kept;
- the guard refuses none of the 101 stored captures (counts only), and
  the stored-capture test passes;
- 375 passed and 1 failed over D2's three modules and the birth-evidence
  check: 363 from D2's modules and 12 birth-evidence tests. The failure
  is the track-U assertion, which needs modules this sparse clone leaves
  out; CI runs it;
- tiers by collection: unit 183, artifact 180 (12 more artifact tests);
- black -l 79 --check, ruff check and git diff --check are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 26, 2026 19:53
Round 8 reviewed 6e96c64 and its merge 085a33b and returned APPROVE,
with four low findings and five notes. This commit answers each under one
added rule: no page that 085a33b's guard refuses may be admitted. So
every reading that guard made is kept, with the same result, and now runs
in linear time; the new readings are only added to it.

- Low 1: two new value patterns took quadratic time on long runs, a word
  in a tag (78 s for 40 kB) or escaped quotes after an unpaired quote in a
  script. Attribute names now start only where a run of name characters
  starts, as the review proposed. Script strings keep round 8's reading,
  in which an escaped quote mark outside a string can still open one; a
  mark after the last unescaped mark of its kind can open no string, so
  it is set aside before the scan rather than read to the end each time.
  (The review's form consumes escapes outside strings, which would change
  which strings are read.) Timing every slow form found more in the
  parent's patterns: tag, script, style, table, row, cell and
  preformatted openings with no closing ">", script and style start tags
  with no end tag (round 8's 0.25 MB case), unclosed "<a" tags (its
  0.5 MB case), unclosed comments and noscript blocks, and table rows and
  cells that name an identifier. Each is now linear: element and tag
  patterns stop searching at the page's last ">", a script or style
  opening that no end tag of its name follows is skipped, comment and
  noscript removal stop at the last end tag, and the table rules read
  each row and column once. A timing test reads seven invented pages of
  200 to 600 kB, one in each form, in a child process that the operating
  system stops after 6 s of CPU time, and needs each under 5 s. They take
  0.03 to 0.68 s of CPU here; by their growth over a tenth of each page,
  085a33b's guard would take 2.3 to 7.1 minutes. A differential property
  checks each linear helper against the plain pattern it stands for.
- Low 2: the script and style split found "<script" inside a quoted
  value, so '<a href="<script src="PNUM=101">' was admitted, which the
  parent refused. The source is now also read with script and style
  elements found where a browser starts them, in one left-to-right scan
  (the review's C1). The split reading stays, since C1 alone admits pages
  it refuses. Record pages pin F3 and its "<b>" form F6, which both guards
  refuse, and a twin that moves the quote so src is a real attribute pins
  the browser scan alone. Two record pages pin each reading alone.
- Low 3: template literals are now read as script strings, apart from
  the double- and single-quoted strings, so that a backtick changes no
  other string; record pages pin T1, T3, T4 and T5. The text is also read
  as a browser reads it: a tag opens only with a letter, "!" or "/",
  script and style contents are left out, and title, textarea and xmp
  contents are text. That refuses V1-V3 and R1 and R3 (and an xmp page),
  and quoted strings in styles are read alone, which refuses S16; record
  pages pin each, and twins pin the browser text's "<" rule and title
  contents, template literals and style strings. U1 and U2 (markup in a
  text/template script) and Q12 (an unquoted attribute value) stay
  admitted and are named out of scope, in the review's words. The lists
  now say what the code reads: "each double- or single-quoted string in a
  script, as a scan from the script's start finds them, even one that an
  escaped quote mark opens", and the text "read twice, each time with a
  tag counted as a space".
- Low 4: record pages pin each of the review's O1-O10, and each of the ten
  mutants, ported to this code, now changes its page in process.
  Full-module runs of the mutants at seeds 0 and 7 follow this commit.
- Note 1: the guard's Hypothesis properties, and the three tests the
  review saw miss their deadline, run with deadline=None.
- Note 2: the quoted-value reading still skips only values named href or
  src, since narrowing it would admit pages the parent refuses. The
  docstring and the notes name the asymmetry and the xlink:href, :href
  and img.src slugs it refuses.
- Note 3: the newly refused definition and index forms (W1, W3, N1 and
  N3-N7) are named among the pages the rules refuse, W1's definition row
  and N4's page summary among them.
- Note 4: of the four mutants that only round 8's record pages pinned,
  three were new in round 8 (G64, G65, G67); G32, round 5's "data-src
  removed too", was left unpinned by the new reading of quoted values.
- Note 5: no change.

Found by this commit's own checks:
- the linear text reading raised on a script name that "(?i)" reads with
  a long s or a dotted capital I, as in "<SCRİPT>x</script>": its
  end-tag lookup keyed names by casefold, where Python's re compares a
  repeated name a letter at a time in lower case. Names are now keyed
  that way, which agrees with re over every spelling of script and style
  on Python 3.10 to 3.14. A record page, a definition page and two
  examples of the differential property pin it;
- the script-string reading had moved to an element pattern whose end
  tag repeats the opening's name, so '<ſcript>a = "x</script>' read on
  past "</script>" and admitted a later script's string that the parent
  refuses. It ends at any "</script>" again, as before, and a record page
  pins it;
- a script string after a quote mark in a template literal, as after a
  comment's "it's", is admitted by both guards; the out-of-scope list now
  names a lone quote mark in a comment, a regular expression or a
  template literal;
- the over-refusal list names "a definition that prints a comparison such
  as PNUM < 101 in the text", now that the text reading refuses it.

Checks:
- 188 invented pages behave as expected (44 new);
- no page 085a33b's guard refuses is admitted: over the test pages,
  round 8's probes (69 pages, and 16 same-row pages written from the
  names of its 13, whose text was not kept) and round 7's (34 probes, 6
  round-6 forms and 7 fix pages), and 4 x 100,000 random invented pages
  from each of prescreen_r8's and fuzz8's generators, with 4 x 100,000
  more that add unicode script names; 201, 1,657 and 1,305 of those
  pages are refused at the head alone, none by the parent alone;
- each of the 38 twins isolates its rule or limit;
- 105 guard mutants: round 8's 67, ported where the code moved, the
  review's O1-O10 and 28 new ones. In process, 97 change an invented page
  or an example of the differential property; the other 8 only make a
  reading quadratic, and the timing test alone pins them;
- with 085a33b's guard, 26 of the 171 guard tests fail in seeds 0, 1
  and 2, the same 26 in each: 13 new record pages, the 5 new twins, the 7
  long pages and the differential property. The other 21 new tests pass
  there: 17 record pages it already refuses, which pin readings kept from
  it and the review's ten mutants, and 4 definition pages it admits;
- the guard refuses none of the 101 stored captures (counts only); they
  take 5.58 s at the head and 2.66 s at the parent, the worst page 0.09 s
  and 0.04 s (CPU);
- the four test modules give 458 passed and 1 failed, the known
  test_post_review_exclusions_are_unreachable_from_birth_evidence, which
  needs modules this sparse checkout leaves out. The exact command passed
  the shell's 10-minute limit, so each module ran alone, or in parts of
  its test functions, through the same wrapper with -n 2;
- tiers by collection: unit 183, artifact 263 (47 new), and none in
  integration_psid, reproduction_legacy or oracle_policyengine;
  _classify_test_module still gives unit, artifact and artifact;
- black -l 79 --check, ruff check and git diff --check are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ger page

Mutant G89 sums the same row's values once per cell, in quadratic time. It
survived at c501f04, because the timing test's row of identifier cells
stayed under the 5 s limit even with that sum. The test now reads 60,000
identifier cells in one row (780 kB), and G89 fails it in Hypothesis seeds
0 and 7. G82 to G88, the other mutants that only make a reading quadratic,
still fail it in seed 0.

This corrects c501f04's message, which said the timing test alone pins
those 8 mutants. It pinned 7 of them. metadata_notes.md and the test's
header now give the pages' range as 200 to 780 kB. The other 97 mutants
were not rerun, since this commit changes only one test page and two
notes.

Checks, on these bytes: the guard and reader tests pass, 458 in all with
the seven linear-time pages; the one failure is the birth-evidence
reachability test, which needs modules this partial checkout leaves out,
and CI runs it. black, ruff and git diff --check are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 31, 2026 16:12
Round 9 approved 16ea8c9 with four low findings and six notes. This
commit answers them under the round's rule, that the guard admits no
page 085a33b's guard refuses, and adds nothing else.

Low 1 needed a test of that rule. A frozen copy of 085a33b's guard now
sits in the test tree as text,
tests/income_resources/fixtures/capture_user_notes_085a33b9.py.txt, and
a test pins its sha256; no run collects it and the tier classifier never
reads it. A Hypothesis property draws pages from the parts of the forms
the review found (a script in an unclosed tag, over lines, in upper case
or after an end tag in a string, and a string across a template
literal), with invented tokens between them, and asserts that the guard
refuses every page the copy refuses. Its explicit examples and four new
record pages are the review's X1, X3, X5 and X9. The differential
property now also checks the attribute pattern against the pattern
without its lookbehind, and the table rules against the copy's
cell-by-cell reading.

Low 2 needed the browser readings to read names as a browser does. The
browser text and the browser scan of the source now fold ASCII letters
only in element names, in an end tag's repeat of its opening's name and
in a tag's first letter, so a long s, the Kelvin sign and a dotless or
dotted capital i open no tag and name no element. The ASCII flags cover
only those parts: a global one would also change \b, so that "<scripté>"
opened a script the round-9 head shows as text. shown() compares names
in lower case, not casefold, so "<ſcript>" stays text. Raw-text elements
end at "</" and the name followed by whitespace, "/" or ">", through the
next ">". The guard now refuses E1, E2, E4, U1, U4 and L1-L4, and record
pages hold them. The docstring, metadata_notes.md and the PR text now
name the forms that stay admitted: an identifier name split by a tag or
an invisible character, values in separate style strings, a comment that
holds a script or style opening, a tag that ends with "-->", which the
text readings run on to the next ">", and nine digits after a "--!>"
comment end. This commit's own checks found the "-->" form.

These changes touch only the browser readings, so 085a33b's readings
stand and the rule holds. They do not only add refusals against
16ea8c9, though: they follow a browser where 16ea8c9 did not, and some
pages that 16ea8c9 refused for that reason the guard now admits;
085a33b admits each of them too. The most common is a "<SCRİPT>" or
"<scrıpt>" element, whose contents 16ea8c9 read as raw text and a
browser parses as markup; others are an end tag "</scrİpt>", which
16ea8c9 took to end a script and a browser does not, and "<ſ", which
16ea8c9 took for a tag.

Low 3 needed the new readings pinned. Record pages pin X2, X4, X7, X8
and X10, a definition page pins X11, and X12's page is a record page,
since a browser shows a "<ſcript>" element's contents. Record or
definition pages pin this commit's own mutants of the new parts, N1-N13.
N14 (an end tag with no ">" ends nothing) and X12r (X12's port: casefold
for lower) give the head's result on every page: the first text reading
reads the text after the last ">" as it stands, and the browser
readings' names are ASCII, which casefold and lower fold alike.

Low 4 needed the timing test to pin Q1-Q6. Five new long pages, of
100 kB to 2.1 MB, take one form each, and the escaped-quote page grows
from 200 kB to 1 MB for Q5. By the mutants' growth, each takes 20 s or more
of CPU on its page; the head reads each of the 12 pages in under 1 s.

N1: this message and the PR text correct the claims N1 lists. c501f04's
differential property left out the attribute pattern and the table
rules; its ten ported mutants survived when ported to the split alone;
both guards refuse F6, but 085a33b admits F3; its "7 fix pages" were 8;
16ea8c9's 458 tests include 12 birth-evidence tests, and that commit
also rewrote the timing test's comment. N2: the docstring, the notes and
the PR text name D8 (a template literal that holds JSON) and F2 (a
malformed href value that holds a script opening) among the pages the
rules refuse. N5: _finditer's docstring says it relies on (?s) in each
pattern that can end with $. N6: the tests ran in parts. N3 and N4 need
no change.

Checks:
- Pages and probes: all 217 invented test pages behave as expected, 29
  of them new; round 8's probe8 (69), round 7's probes (34), round 6's
  forms (6), round 7's fix pages (8), round 9's own probes (16) and
  same-row pages (16) give no page that 085a33b or 16ea8c9 refuses and
  the head admits. Of the round-9 reviewer's 34 record probes, the head
  refuses 26, up from 17. Its definition probes give the round-9 head's
  results.
- The rule: 300,000 random invented pages from round 9's three token
  sets (100,000 each) and 100,000 from the round-9 reviewer's alphabet,
  with its identity checks of every linear helper: 0 pages that 085a33b
  refuses and the head admits, and 0 identity failures. The head alone
  refuses 40, 437, 390 and 545 of them. 16ea8c9 refuses 0, 0, 27 and 64
  pages that the head admits, for the reasons above.
- Stored captures: the head and 085a33b each refuse 0 of 101.
- Twins: each of the 38 isolates its rule or limit, as in round 9.
- Tests, in parts through the wrapper with two workers: the evidence
  module's guard tests other than the timing test, 194 passed; the
  timing test, 12 passed; its other tests, 78 passed in two parts; the
  refusal properties and birth-evidence tests, 27 passed and the known
  test_post_review_exclusions_are_unreachable_from_birth_evidence
  failure. Of test_sipp_income.py, which reads no file this commit
  changes, the first of four parts passed 74 tests before the 575 s cap
  stopped it; the other parts run after this commit.
- Lint: black -l 79 --check and ruff check pass on the two Python files,
  and git diff --check is clean on all four changed files.

The mutant module runs, the parent check and the tier counts follow this
commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7ae8edb added five long pages to the timing test and enlarged a sixth,
but metadata_notes.md still said the test reads pages of 200 to 780 kB
in round 9's forms. The notes now give the range, 100 kB to 2.1 MB, and
name the forms where a slower form of a linear helper, found in review,
takes quadratic time. No test or hash pins the notes, and this commit
changes no other file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 10 approved d7442ef with five low findings and five notes. This
commit closes them under the same rule: the guard admits no page that
085a33b's guard refuses.

Low 2, where a browser element's name ends. _browser_element now ends an
opening's name where a browser ends it, at whitespace, "/" or ">", with
the review's lookahead (?=[\t\n\f\r />]) in place of \b. So <script-x>,
<script.x>, <script\0>, <script\v>, <script"x">, <script=x> and
<style:x> open no script or style in the browser readings, and a browser
shows their contents. Seven record pages hold those forms: each mark
passes \b, and the vertical tab also passes \s. <textarea.x> needs no
page, since d7442ef already refuses it. A definition page holds a script
opening ended by each of the six marks.

Low 1, a comment or hidden text between an identifier and its value. The
text has a third reading, _browser_view, which reads the page as a
browser with scripting on shows it. It keeps the browser text's rules,
leaves comments out where a browser starts and ends them (COMMENT: at
"-->" or "--!>", or the end of the page, with "<!-->" and "<!--->"
empty), and hides iframe, noembed, noframes, noscript and template
contents. A browser reads the first four as raw text, so a script opening
inside one opens nothing.
- This goes beyond candidate C2 as written. _browser_text(body) reads a
  comment as a tag that ends at its first ">", so commented-out markup
  such as <!-- <span class="label">Person no.</span> --> still shows text
  and its period. A record page holds that form.
- The review's fifth page needs template hidden. A browser parses
  template contents but shows none of them. The view ends them at the
  first </template>, and a browser's end falls there or later, so the
  view hides no text a browser shows.
- The browser text over comments kept, the scripting-off reading, stays
  unchanged apart from Low 2's opening. So every reading that read
  noscript contents still reads them, and <noscript>PNUM <b>101</b>
  </noscript> (RB8) stays refused.
- The browser source reading needs no such change. It reads a script's
  contents as they stand, so a script that a browser would not open
  there hides no text from it; it only leaves the href and src values of
  the tags it spans in place. Reading those elements as raw text there
  would either read their tags' link targets, refusing the admitted
  noscript_link_target and iframe_link_target pages, or drop their
  contents from the reading meant to read the whole source.
- Record pages hold the review's six pages and 13 more: the other three
  raw-text elements with a script opening inside, a period inside each of
  the four raw-text elements, commented-out markup, "<!-->", "<!--->" and
  "--!>" ends, and two forms the old lists named: a comment that holds a
  script opening and a tag ending with "-->". Both stay admitted inside
  noscript contents, which only the readings that keep comments read
  there, and the lists now say so.

Low 3, the rule property. Record pages hold <p>PNUM #7</p>, [7, '7 and
(7, each refused by the adjacency pattern and admitted when its mark
leaves the pattern, and <noscript>PNUM <b>101</b></noscript>.
- A second test draws pages from LINEAR_TOKENS plus RECORD_TOKENS (the
  adjacency marks, digit runs of each rule's length, comment,
  preformatted, noscript and table tokens and quoted values).
- Both rule tests call _assert_round_8_rule. Before the verdict, it
  checks that the readings made by functions both guards share agree on
  the drawn page: note_text (with and without comment marks),
  _rendered_text, the source split at every script or style, and the
  quoted values, whose head list begins with the fixture's.
- No comparison exists for the browser readings, the head's browser scan
  of the source, its template literals and style strings, or the way
  record_bearing_note joins and reads the readings. The fixture has no
  counterpart for any of them.
- The property alone catches RB3, RB4 and RB7, in seeds 0 and 7 in
  process. RB1, RB2, RB5, RB6, RB8 and RB9 pass it. Every RB mutant
  changes guard test pages: RB6 and RB8 fail number_sign_before_digit and
  identifier_in_noscript, and RB9 the 40-character window test.

Low 4, this round's end-tag and opening rules. Record pages hold M1's form
feed, M2's carriage return and M3's no-break space in a script's end tag,
and M5's <scripts>, which an opening that checks no name end would read
as a script whichever check item 1 used.

Low 5, the timing test. Three long pages join it:
- "<script>" * 50_000 + "</script>";
- "<script>x</script>" * 120_000, twice the brief's 60,000: at 60,000 T1
  and T3 took 10.6 s and 9.6 s of CPU, about twice the 5 s bound;
- "<!--<b>" * 50_000, the slow form of the view's comment without the end
  of the page (T14).
In CPU-capped children:
- T10 passed the 25 s limit on the first page, and T1, T3 and the view's
  list concatenation (T13) passed the 20 s limit on the second;
- T14 passed it on the third, and a view searched past the last ">"
  (T15) passed it on unclosed_tags;
- the head took 0.19 s, 1.02 s and 0.25 s, and at most 1.65 s on any of
  the 15 pages, against 1.55 s for d7442ef on the same loaded host.

Notes.
- N1: 7ae8edb's message overstated two counts. The head refuses 25 of
  the round-9 reviewer's 34 record probes, up from 16, not 26 up from 17.
- N1: the round-10 report's claim that plain token lists found none of
  the four forms in 3,000 pages holds for its RULE_TOKENS list alone. Its
  24-token list of the forms' chunks found X1, X3 and X5 first after 306,
  101 and 526 pages; the comment above RULE_PARTS now says so.
- N1: the comment in _browser_text now says the pattern captures names in
  ASCII letters only, so <ſcript> opens no element, and lower and
  casefold agree. The TAG comment and the notes say "an ASCII letter".
- N2: no third pair of readings. Of the 91 pages that 16ea8c9 refused
  and d7442ef admits, the head refuses 4 and 085a33b refuses 0.
  html5lib shows a record on 3, all of them refused.
- N4: the out-of-scope lists name S5 (other characters, such as fullwidth
  letters) and the review's other contrived forms that stay admitted:
  plaintext contents, a character reference glued to the name in xmp,
  svg and math markup, and a value split by a tag. This commit's checks
  found and named three more: hidden text, a "<?" instruction beside an
  empty comment, and markup a browser ignores in a select. The view now
  refuses the review's "<!-->", "<!--->", "<![CDATA[-->", "<!- -->" and
  noembed forms.
- N3, N5: no change.
- The lists also name a new form the rules refuse that holds no case
  record. A "-->" in a quoted attribute value, as in an arrow
  alt="Next -->", ends the tag in the browser view.

Masking. The view refuses on its own the pages that pinned eight ported
mutants of the browser text alone: X7, N3, N5, N7 and N10 of round 10's
set and G74-G76 of round 9's. This round's C1a-txt and V13 have the same
trouble. Seven record pages pin them. Six put those forms inside a
comment or a noscript block, which only the browser text reads, and one
puts a textarea after a comment, which only the view shows beside the
identifier. Two round-8 twins (browser_text_lt_opens_no_tag,
browser_text_title_contents) moved inside a comment for the same reason.
Four new twins hold the view, its comment ends, its hidden contents and
the opening's name end.

Checks, in process (logs in d2-r11-fix/):
- Pages: all 269 page entries behave as expected at the head (148 record,
  37 definition, 42 twins), 52 of them new since d7442ef. d7442ef
  refuses none the head admits.
- Probes: probe8 69, round 7 34, round-6 forms 6, fix pages 8, round 9's
  own 16 and same-row 16 give 0 parent-only and 0 d7442ef-only pages,
  with no verdict changed from d7442ef. The reviewer's 34 record probes:
  25 refused.
- Rule: over 100,000 pages each of fuzz_slice_r10's prescreen, fuzz8 and
  unicode token sets (seed 11), parent-only 0, 0 and 0. Over the review's
  oracle grammar, 205,000 pages (seed 11, and seeds 2-4 as in the
  review), the head admits no page 085a33b refuses, and nothing raised.
- Record forms the head admits, by html5lib, over seeds 2-4: 446 hits in
  330 forms, against the review's 846 in 611. Raw ">" in a quoted value
  188, plaintext 91, glued xmp references 15, noscript comment marks 17,
  svg or math 12, select 4, "<?" beside an empty comment 3, a value split
  by a tag 1. All are named.
- Departures: over 400,000 pages (the three token sets and grammar seed
  11), the head admits 171 that d7442ef refuses, all from the grammar.
  085a33b admits all 171. html5lib shows a record on 9: plaintext 6,
  raw ">" 1, noscript comment marks 1, svg 1.
- Newly refused: the head refuses 518 pages that d7442ef admits. html5lib
  shows a record on 455. The other 63 hold a raw ">" in a quoted value
  (35), svg or math (9), plaintext (3), select (2), a tag left open at the
  end of the page (2) or a mix (12).
- Stored captures: the head refuses 0 of 101, and so do d7442ef and
  085a33b.
- Twins: 42 of 42 isolate their rule or limit.
- Mutants, prescreened: of 190, every one that is not timing-only changes
  at least one test page, apart from four:
  - X12r and N14, equivalent as in round 10;
  - G99, which the differential test's examples pin;
  - C1a-src, the browser source alone with \b, where the split at every
    <script also opens a script and no page found tells the two apart.
- Lint: black -l 79 and ruff pass on both Python files, and git diff
  --check is clean.

The host runs the rest: the whole test command, this round's mutants at
seeds 0 and 7, the older sets at seed 0, the new tests against 085a33b's
guard in seeds 0, 1 and 2, and the tier counts (d2-r11-fix/host_pass.sh).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host pass at 955db50 found that the slower form T12 passes the timing
test: it builds the quoted values by list concatenation, and no long page
held scripts with strings in them, so the claim that T10-T15 each fail on
their own page was wrong for T12.

Add a page of 200,000 closed scripts that each hold a string, 4 MB. The
guard reads it in 1.2 s of CPU time, and T12 in 15.7 s against the test's
5 s bound. At a tenth of T12's time the head stays well inside the bound.
The guard is unchanged; the notes name the new form and the new largest
size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits November 1, 2026 09:42
Round 11 approved 076e431 with six low findings and four notes. The rule
still stands: the guard admits no page that 085a33b's guard refuses,
since every change here is to the browser readings, which have no
counterpart in that guard, and the new reading joins the text after a
period, which neither identifier rule crosses.

Low 2, round 10's forms inside noscript contents. No reading both left out
comments and showed noscript contents. The text now has a fourth reading,
the view with scripting off: BROWSER_VIEW_SCRIPTING_OFF is the view's
pattern without noscript, and _browser_view takes the pattern as an
argument. It refuses all 11 of the reviewer's noscript probes, the named
<b--> form among them, so the out-of-scope clause on comment marks in
noscript contents goes. Four record pages hold the forms.

Round 11's matter 1 and Low 6, forms a browser shows:
- Plaintext. A plaintext element now runs to the end of the page in the
  browser text and both views, as in a browser, and the views show its
  contents undecoded. Five record pages pin it, in all three readings at
  once and in each alone. The browser scan of the source does not change:
  plaintext there (PT7) changed no verdict on any test page or on 520,000
  invented pages.
- "<?" instructions. The views read one as a comment up to the next ">",
  and to the end of the page after the last ">", as a browser does.
- NUL. The views drop a NUL from the text and show one in title,
  textarea, xmp and plaintext contents as U+FFFD, as html5lib does outside
  svg and math.
- xmp contents. The views no longer decode them, as a browser shows them.
- These close the reviewer's NUL, "<?", plaintext and xmp forms, so the
  clauses that named the "<?" form beside an empty comment, plaintext
  contents and a reference glued to a name in xmp go.
- The double-escaped script and a template that a browser ends after the
  first </template> stay admitted: closing them needs a reading that
  tracks the script escape states or template nesting. The docstring and
  the notes name them, and plaintext joins the svg, math and select
  clauses, where a browser opens no plaintext element.
- The split-value clause now names a plural name and other words before
  the value, as in PNUMs 10<b>1, which the fuzz found.
- A new classifier puts each admitted form in one bucket, the first named
  cause whose removal ends the hit, so the buckets sum to the total.

Low 1, rule-breaking mutants the pages missed. Record pages hold
<!-- &#49;2345678901234 -->, <img alt="PNUM\t7"> and
<script>PNUM\n7</script>, so RN4 and RN6 now fail them. A third rule
property builds records from an identifier, a separator, a value of 1, 3,
9 or 14 digits, plain or as references, and a wrapper, with RN4's and
RN6's forms as examples. _assert_round_8_rule now also compares the tables
and preformatted blocks the round-8 guard found and _identifier_column on
each table, and its comment names them.

Low 3, C1a-src. A definition page holds the reviewer's
<script-x>x</script\x0b> page: html5lib shows the link's text and no slug,
and the head admits it, while C1a-src refuses it.

Low 4, the view's own rules. Record pages hold W3, W9, W18 and W19's
forms after a comment. A new test, test_browser_views_read_invented_pages
_as_stated, checks both views against 25 invented pages and the texts
html5lib shows of them, written out, so it does not depend on
BROWSER_VIEW; one, a nested template, holds the view's stated departure.
Every mutant of the views' own rules changes one of them: W1, W3, W4, W8,
W9, W16, W18 and W19, this round's PT1, PT3 to PT6, PI1 to PI3, NU1, NU3
and DX1 to DX3, and round 11's V2 to V14 as ported.

Low 5 and N2, the timing test. Two pages join it: a script of 500,000
strings (2 MB) and 60,000 quoted values, comments and style strings
(2.2 MB). The page of closed scripts with strings falls from 4 MB to 3 MB.
In CPU-capped children on this host:
- SF1 took 10.7 s, SF2 7.0 s and SF3 28.4 s, and SF4 passed 30 s;
- T12 and SF6 passed 15 s on the 3 MB page, and SV1, the view with
  scripting off searched past the last ">", 15 s on unclosed tags;
- the guard took at most 2.08 s on any of the 18 pages under Python 3.13
  and 2.19 s under 3.14.7, both on the 3 MB page, against the 5 s bound.

Notes:
- N1: _browser_view's docstring now says that after a double-escaped
  script the view can open an element a browser never opens. The timing
  test's comment gives the measured 2.2 s and the closest slow form's
  7.0 s, in place of "under 2 s" and "20 s or more".
- N3: X12r and N14 are equivalent, and C1a-src is not; a search of 20,000
  pages found a page that tells C1a-src apart and none for the other two.

Found by this round's checks:
- The view with scripting off masks three older mutants' pages, so record
  pages put N3's and G76's forms in a comment and RB8's comment text in a
  noscript block.
- Three round-10 mutants, X6, N5 and N6, edited "</(?a:\1)" at run time,
  which the plaintext lookahead now splits; mutants_r12 ports them.
- Five twins pin the new readings, 47 in all.

Checks, all in process unless marked; logs in d2-r12-fix:
- Pages: all 305 page entries behave as expected (171 record, 40
  definition, 47 twins) and all 25 view examples. 076e431 refuses one
  new page the head admits, instruction_after_last_bracket, which
  html5lib shows no record on.
- Probes: rounds 10 and 11's sets on this round's list, 253 pages,
  parent-only 0. Round 7's and 8's probe files are not on the list.
- The rule: parent-only 0 over the three token sets (100,000 pages each),
  round 11's grammar (100,000) and the round-11 review's grammar
  (100,000, seeds 21 to 24), and 0 raised.
- Admitted record forms over the review's seeds 21 to 23: 353 in 3,189
  hits, against the review's 526 in 3,385: raw ">" in a quoted value
  249, a value split by a tag 59, double-escaped scripts 23, template
  ends 18 and svg or math 4, none unexplained.
- Departures: over the token sets and round 11's grammar (400,000 pages)
  the head admits 9 pages that 076e431 refuses, and 46 more over the
  review's grammar. 085a33b admits all 55. html5lib shows a record on
  one, a plaintext opening inside math, a named form.
- Stored captures: the guard refuses 0 of 101, and so do 076e431 and
  085a33b.
- Twins: 47 of 47 isolate their rule or limit.
- Mutants, prescreened: 235, none a no-op. Apart from the timing-only
  ones, each changes a test page or view example, but PT7, which changed
  no verdict on any page tried, RN3 and RN5, which the properties reach,
  and X12r, N14 and G99, as in earlier rounds.
- Lint: black -l 79 --check, ruff check and git diff --check pass.

Waiting for the host (host_pass.sh): the whole test command, this round's
45 mutants at seeds 0 and 7, the 190 older ones at seed 0, the 64 new or
changed tests against 085a33b's guard in seeds 0, 1 and 2, and the tier
counts. I started no pytest run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… bounds

The host pass of d576600 showed three mutants the tests did not hold:

- SF2 (attribute values added per tag by concatenation) passed the timing
  test on the host at both seeds, and SF1 (style strings added by
  concatenation) passed it at seed 0, because the shared page spent its
  budget on all three forms. Each now has a page of its own under 3 MB.
  On the host the guard reads each in under 1 s of CPU time, SF2 reads
  its page for about 35 s and SF1 reads its page for about 9.5 s.
- RN3 (a wider year range) failed only when a property drew the right
  value. Two twins pin 1899 and 2100 against 1900 and 2099.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text only. Both files' syntax trees match bc40837's, docstrings aside.

Round 12 found the out-of-scope clause too narrow: a template's first
end tag can also sit inside a comment, a <? instruction or raw-text
contents, where a browser does not read it as one. The docstring and
the notes now name that case in general terms.

The refused list gains markup a browser ignores inside a select element
or after a frameset opening, and references to control characters with
or without a NUL.

Three test comments overstated: two of the view's rules change no view
example, the token lists hold a few character references, and the 2.2 s
comes from one host run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits November 3, 2026 05:51
Master's #573 (SSI0) and #574 (D4) each added a block to the
birth-evidence exclusions, their expected tuple and the reachability
test, where D2 adds its own. Keep master's blocks and put D2's after
them in all three places. Both sides add the same empty
tests/income_resources/__init__.py. The merge takes master's tier
files, and the next commit recounts them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master at 97003ac holds unit 23,653, artifact 4,527, integration_psid
1,471, reproduction_legacy 520, oracle_policyengine 337, total 30,508.
D2 adds 183 unit and 415 artifact tests, so the files now read unit
23,836, artifact 4,942, total 31,106. CI's tier policy test checks the
count on the full collection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits November 6, 2026 05:34
Master's #583 (CE1) added a block to the birth-evidence exclusions,
their expected tuple and the reachability test, where D2 adds its own.
Keep master's blocks and put D2's after them in all three places. The
merge takes master's tier files, and the next commit recounts them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master at 02897ed holds unit 23,736, artifact 6,726, integration_psid
1,471, reproduction_legacy 520, oracle_policyengine 337, total 32,790.
D2 adds 183 unit and 415 artifact tests, so the files now read unit
23,919, artifact 7,141, total 33,388. CI's tier policy test checks the
count on the full collection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit 4f99edf into master Nov 6, 2026
12 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

I merged this PR at 25057f4d5570a65b522f3f9818b3d024a3bf2952 with --squash --match-head-commit, as 4f99edfb48ab7ee186c9f82713037224b260992b (11:44Z, 2026-11-06). The squash commit's tree matches the head's.

  • Review. The reviewer in Subfleet job 20261103-061252-d2-review-r13 approved 9e4849f9 with no high or medium finding, and this PR's description holds the review record.
  • After the review. The income-and-resources lane merged master 02897ede (2ca74c50) and recounted the test tiers (25057f4d), and no reviewer read those two commits. I compared them with the reviewed head by script instead. Of the 272 paths they change, 268 match master's blobs. The two birth-evidence files differ because master's text gains this PR's original insertions and loses nothing, and the two tier files differ only in their counts.
  • The squash. Earlier commits on this branch hold eight Census user notes that quote individual survey cases, and the head no longer carries them, so I squashed the PR to keep them out of master's history. git log origin/master prints nothing for those eight paths.
  • CI. gh pr checks 581 exited 0 at the head, where 11 checks passed and deploy skipped (Tests run 42204606713). In that run, tests/test_tier_policy.py compared the committed counts with the collected suite and passed. GitHub reported the PR mergeable.
  • Tier counts. Before the next PR that changes the counts lands, its owner merges master 4f99edfb, or later, into it and recounts the test tiers.

MaxGhenis added a commit that referenced this pull request Nov 6, 2026
…nch's tests

Master adds CE1 (#583) and D2 (#581), which change no Track B file.
Master and this branch both add lines to
scripts/first_estimates_birth_evidence.py and its test, and git keeps
both sets. The tier files take master's counts plus the 2,735 unit tests
this branch adds since 97003ac: 26,654 unit and 36,123 in all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis deleted the ir/d2-20261024 branch November 6, 2026 14:41

This branch was successfully deployed

1 active deployment
Preview — 25057f4d Deployed Nov 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant