Skip to content

Cite US and UK spec sources by symbol or at a commit, never by a drifting line - #1407

Merged
MaxGhenis merged 23 commits into
mainfrom
spec-source-citations-us-uk-v2
Oct 28, 2026
Merged

MaxGhenis merged 23 commits into
mainfrom
spec-source-citations-us-uk-v2

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Supersedes #1146. This is #1146 rebuilt fresh on main, without its New Zealand files: #1282 landed the NZ half on 2026-10-15 with its own stricter grammar, and the NZ hub owns it. Round 2 (5221cd6a, plus the test-only 01da9a37) answers the round-1 review's REQUEST_CHANGES, round 3 (f30da07c) answers round 2's, round 4 (1840bc92) answers round 3's, round 5 (4229cd8c, plus the test-only c67030f7) answers round 4's, round 6 (29983852) answers round 5's, round 7 (5f6647b1, plus the test-only ea912094) answers round 6's, round 8 (25b5bbe2) answers round 7's, round 9 (a0d20514) answers round 8's, round 10 (fa7601cb, plus c36671e6) answers round 9's, round 11 (2822aff1) answers round 10's, round 12 (8a62c938) answers round 11's, and round 13 (4dc0734e, plus the test-only 5578c82d) closes a gap of rule 7's own that round 12's review also found, and a738da7d (plus the test-only e5e14e96) closes one that round 13's review and a fuzz found. Round 14's review approved e5e14e96 with no findings; see "Review" below.

Invariants

The guard is test_spec_source_citations.py, with validators in test_support/microcosm_build/spec_source_citations.py. It scans every resource in every country spec package, meaning every build/<country>/ directory with a country_package.json (today am, be, nz, uk, us). JSON and YAML resources are parsed, and every string is scanned, mapping keys included. Each URL in a string is first read the way a browser and then GitHub read it. Its authority is parsed as the WHATWG URL Standard parses it. Before the query, \ is /, and any number of slashes may follow https: or start a network-path reference (//host/...). The user (up to the last @) and the port are dropped. The host is percent-decoded, mapped to ASCII as UTS46 (IDNA) maps it, and lower-cased, so %67ithub.com, github.com and user@GitHub.com:443 are all github.com. Then, as GitHub does, % escapes of unreserved characters and of / in the path are decoded, . and .. segments resolved, and the query dropped, keeping the # fragment. A URL the standard refuses is read as written. A URL runs to whitespace, so a quote or a bracket inside it is part of it, as it is to a browser. Markdown and prose end a URL at a quote, a parenthesis or an angle bracket, so a string that reads differently that way is read a second time, with each URL ending there. Markdown and HTML decode a backslash before ASCII punctuation and a character reference before a link is followed (x\.py, &#35;L12), so a string holding either is read again with them decoded: as Markdown decodes them and, apart, as HTML does, which leaves a backslash as written (\&#99; is \c, and the URL Standard reads that \ as /), each once as text and once as a URL holds what they decode to. A quoted HTML attribute value or a Markdown destination in angle brackets (after the spaces, tabs and one line break CommonMark allows before its <) that starts a URL is read as the browser follows it: decoded, stripped at its ends, and with the whitespace inside it held, so a space in a segment that a later .. removes does not end the URL. Where a string holds a Unicode space such as U+00A0, which ends no URL in an attribute or a Markdown destination, its URLs are also read through it. Where a string holds a ? and a #, its URLs are also read through the whitespace in their query, which GitHub ignores, so href=".../x.py?a b#L12" cites x.py at line 12. The URL Standard deletes ASCII tabs and line breaks from a URL wherever they are, so each of these is read again without them. Every rule below applies to every reading: a citation any reading finds is checked. So HTTPS://GitHub.com/o/r/blob/<sha>/docs/../%74ools/x%2Epy?plain=1#L12 cites https://github.com/o/r/blob/<sha>/tools/x.py at line 12. For those resources:

  1. No line citation of a file in this repository, pinned or not (in_repository_line_citations). First, any prefix that names this repository is removed: a github.com/PolicyEngine/microcosm/(blob|tree|raw|blame)/<ref>/ or raw.githubusercontent.com/PolicyEngine/microcosm/<ref>/ URL, where <ref> may span segments (main, refs/heads/main, feature/x) and the path after each segment is tried, up to the depth of the deepest path form here, or microcosm/ (after any ../ and an optional PolicyEngine/) before a top-level source directory. The rest counts as in-repository when either holds:
    • after such a prefix, it is a file in this checkout;
    • it is an anchored path form of a source file under packages/, tools/, experiments/, test_support/ or docs/. The anchors are the path from the root and, under packages/, the path from the distribution directory (microcosm-build/...). A file in a package's src/microcosm/ is also named from src/, from the microcosm namespace (microcosm/graph/executor.py), from its shard (graph/executor.py), or from a first-level subpackage of its shard (us_runtime/asec_pool.py). Deeper tails do not count, so a foreign utils/loss.py or tests/test_aggregates.py stays foreign however this tree grows. A path here can hold an = (18 tracked files do, such as docs/evidence/us-import-entry-margins/reconciliation/period=2025-01.json), where a reading starts a name, so the run of path characters (letters, digits, _, ., /, = and -) that ends in a cited name is tried too, and a reverse locator reads a name after an = (lines 3 of a/b=c.py).
  2. No line citation of any other source unless it is pinned at a commit (unpinned_line_citations). A citation is pinned when either holds:
    • it binds to an inline commit (citing_commit). The spellings are commit <sha>, at <sha>, @<sha>, a GitHub /blob/, /tree/, /raw/, /blame/, /commit/ or /commits/ segment, or a raw.githubusercontent.com/<owner>/<repo>/<sha>/ URL. <sha> is 7-40 lowercase hex digits with at least one digit and one letter, not running on into a file name, a longer token (-), or an ellipsis (…, ...). After commit, at or @ it must not run on into a path either: in at a1b2c3d/foo.py:12 the hex is a directory. A citation binds to the nearest such commit in its own clause, else the nearest one before it in its string. A clause ends at ; or at a sentence end before a capital, bracket or backtick, and a break inside the citation itself (x.py; lines 12-19) does not split it. The fallback to an earlier commit does not apply when the citation's own clause names a moving ref: on/at/@ before main, master or HEAD, quoted or not (on `main` ), a /blob/, /tree/, /raw/ or /blame/ segment at one, or raw.githubusercontent.com/<owner>/<repo>/<branch>/, each also as refs/heads/<branch>. A cited URL at a moving ref counts, so commit a1b2c3d; https://raw.githubusercontent.com/<o>/<r>/main/x.py#L12 is unpinned;
    • the resource holds a path/commit mapping, at any depth, whose commit has the same sha form and whose path ends with the cited path, by components. A pin of policyengine_us_data/datasets/cps/cps.py covers cps.py and datasets/cps/cps.py, not archived_data/datasets/cps/cps.py or other/cps.py.
  3. No misspelled pin beside a citation (misspelled_commit_pins). A commit-like token in no recognised spelling is refused when it sits in the citation's clause, or between the citation and an earlier commit it binds to. Examples are archived 42ed5d45, rev a1b2c3d and (a1b2c3d). The point is that a stray sha cannot sit beside a citation that some other commit pins.
  4. No line locator in an unrecognised spelling (unrecognised_line_locators). This is a backstop. A source name followed within 24 characters, with no ., ; or line break between, by L<n>, line(s) <n> or l./ll. <n> must start a recognised citation, or that locator must belong to one. Otherwise it is refused (x.py some_function, lines 12-19). A bare number after a colon is left out (x.json: 63 intersections is a count).
  5. Every dotted microcosm.<shard>.* name in a resource resolves through import plus getattr. Only names whose second component is a shard in this checkout count (build, calibrate, data, diagnostics, fit, frame, graph), so a format id such as microcosm.axiom_input_closure.v1 is not read as a symbol.
  6. No line citation in a string that holds a dot segment, pinned or not (dot_segment_line_citations). A dot segment is .. as a whole path segment beside a /, a \ or a %2f (GitHub reads %2f as / before it resolves a path), either dot perhaps written %2e, in the string as written or in any source its readings start from (decoded, enclosed or joined). It removes the segment before it, and whitespace, a backslash or markup in that segment can make a renderer remove another segment than a reading does. In [a]( <https://github.com/…/tools/a b/../x.py#L12>) the destination follows a space, and in [a](https://github.com/…/tools/a\b/../x.py#L12) Markdown writes the backslash as %5C: both link to line 12 of tools/x.py. So every line citation in such a string is refused, whichever file it names. Without a dot segment, whitespace or a backslash that a reading takes otherwise than a renderer stays in the segment the renderer follows (%20, %5C), and no source file under packages/, tools/, experiments/, test_support/ or docs/ has either in its path (a test checks), so that link names no source file here.
  7. No line link of this repository, whatever file it names (line_links_of_this_repository). A string is refused where any source its readings start from holds this repository's GitHub path, PolicyEngine/microcosm/, with a line fragment (L and a digit) at the first # after it. The source is read as a browser and then GitHub route a path: a backslash as /, as a browser reads it in an HTML attribute, and again as no separator, as a Markdown renderer escapes it (%5C); the dot segments before each ? or # resolved, either dot perhaps %2e and an empty segment counted, so a dot segment can form the path (PolicyEngine/x/../microcosm/blob/<sha>/LICENSE#L3); then escapes of unreserved characters and of / decoded and a run of slashes read as one; and the owner and name matched in any case, but not as the end or the start of a longer name (NotPolicyEngine/microcosm, PolicyEngine/microcosm-dynamics). Rule 1 follows a link to the file it names, so where a reading takes a link's ref or authority otherwise than a renderer, it can name no file here, and a commit in the string then pins what it does name: in [a](<https://github.com/PolicyEngine/microcosm/blob/feature+foo/tools/x.py#L12>) a reading starts the name at the + and cites foo/tools/x.py. Rule 7 needs no file. A renderer's link to a line here names that path, and between a source and the URL a renderer follows lie only the decodings the readings apply, a Markdown renderer's percent-encoding (of a backslash or a tab, among others, which a browser then reads as part of a segment), a browser's steps (a backslash as /, dot segments resolved, and percent-encoding of characters that are neither in the path nor a #) and GitHub's (unreserved escapes decoded, a run of slashes as one, the owner and name in any case). GitHub decodes no %2f in the owner or the name (each such path returns 404), so decoding one here can only add a refusal.

The recognised spellings are:

  • forward: x.py:12, x.py:L12, x.py#L12, x.py L12, x.py line 12, x.py: lines 12, x.py @ L12, and with the number quoted, x.py:"12-19";
  • forward after a separator (,, ;, —, – or a spaced -), a bracket, and at/on: x.py, lines 12-19, x.py (lines 12-19), x.py [L12], x.py at line 12, x.py — ll. 12-19;
  • reverse: lines 12-19 of x.py, line 12 in x.py, L12 of x.py, ll. 12-19 of x.py.

Numbers are a line or a range (12-19, L12-L19, en and em dashes, 12 to 19, 12 through 19), joined by , or and. The file and the locator may be quoted, backticked or curly-quoted. Only text-source suffixes count (py, yaml, json, toml, sh, md, csv, rs and the like), and a cited file may be an https: URL.

Scope of each claim:

  • Out of scope: a dotted name with lines (build_loss_matrix.create_target_matrix:93-103), because ledger fact keys such as ledger.aggregate_fact.v2:5973… read the same way; and a reverse-order locator separated from its file by other words (lines 12-19 of the upstream x.py). The backstop reads forward from a file name only.

  • A bare file name such as concepts.py cannot be resolved, so rule 2 covers it, not rule 1. A relative path's inner .. segments are resolved (tools/x/../y.py is tools/y.py), but a path that climbs above where it starts (../y.py, tools/../../y.py), or an absolute one, is not resolved unless it names microcosm/. A relative reference is read as a path, not as a URL: its backslashes are not read as /, and whitespace ends it, so [a](<tools/a b/../ci_test_plan.py#L12>) cites b/../ci_test_plan.py, which names nothing here; rule 2 refuses it, and since round 11 rule 6 refuses it pinned or not, for its dot segment. Of the 200,000 generated links under "How this was verified", 12,702 relative ones read so, and every one is still refused by another rule.

  • Moving refs are the three branch names main, master and HEAD in the spellings rule 2 lists. Another branch name (develop), a query ref (?ref=main) or HEAD~1 is not recognised, so a citation in such a clause still falls back to an earlier commit.

  • URL reading covers three kinds of URL:

    • http(s) URLs, with any number of slashes after https:, or none;
    • network-path references (//host/..., any two of / and \), read as on an https page;
    • GitHub URLs written with neither (github.com/...), before an optional port. A user written before such a host is left as prose, and the host is read from where it starts.

    A host written with neither is GitHub's only in its ASCII spelling. Another host written that way is read as written, so its query can still hide a locator; the backstop catches a query of up to 24 characters. Punctuation that ends a URL (., ,, ;, :, !, ?, ), ]) is read as prose, and so is a quote or an angle bracket that ends one. Otherwise a URL runs to whitespace, so a quote or a bracket inside it is part of it. The second reading also ends a URL at a quote, a parenthesis or an angle bracket, so a Markdown link destination ([label](url), [label](<url>)), an autolink (<url>), an HTML attribute (href="url") and a URL written straight after another with only a quote between (https://a/x'https://b/...) are each read as a URL of their own. Square brackets end a URL in neither reading, which keeps an IPv6 host ([::1]) whole. Since a citation any reading finds is checked, the second reading can only refuse more. It over-refuses where a quote, a parenthesis or an angle bracket inside a URL whose path names a source file is followed by text that reads as a line locator. In another repository's line URL with text before the #L fragment (.../x.py?q='v'#L12), the second reading leaves x.py'v'#L12, which the backstop refuses as an unrecognised locator. In a data-source query that quotes a locator-like value (https://www.ons.gov.uk/data.json?where=CLASS='L12'), it leaves data.json'L12', an unpinned citation of data.json. Escaping the character (%27) passes. No live string reads differently the second way. Whitespace is Python's \s, 29 code points: the ASCII ones, U+001C-U+001F, U+0085, U+00A0 and the other Unicode spaces. Bun's URL reads a URL with any of them inside as one URL at all four places the code-point differential below tries (it drops a tab or a line break and escapes the rest). Since round 10 a URL is also read through whitespace other than ASCII whitespace (tab, line feed, form feed, carriage return and space), and a quoted attribute value or a destination in angle brackets that starts a URL is read whole. Elsewhere a URL broken by ASCII whitespace is read only up to it, as an unquoted attribute value and a Markdown destination outside angle brackets end there too.

  • Hosts are mapped to ASCII with Python 3.14's Unicode database (16.0), plus Unicode 17's U+A7F1 (꟱ is s). A code point added later that a browser maps into ASCII is not mapped.

    • Where UTS46 refuses a code point (a joiner, a bidi control, an unassigned one), the reader reads it anyway, which can only refuse more.
    • A host's trailing dot is dropped. The standard keeps it, but GitHub redirects github.com. to github.com.
    • An IP address is read as written: GitHub serves no file by address.
    • A label longer than 63 characters, which no DNS name holds, is mapped but not written in Punycode, where the standard writes any label so. No such host is GitHub's, and Python's Punycode encoder takes time quadratic in a label's length.
  • The decoded readings decode a backslash before ASCII punctuation and character references (decimal, hexadecimal and named; a numeric one with or without its ;), in one pass, as CommonMark decodes a link destination; and, apart, character references alone, as HTML decodes an attribute, leaving each backslash as written. Each is read once as text and once as in a URL, where whitespace, a quote, an angle bracket or a parenthesis that a reference decodes to is percent-encoded, or deleted if it is a tab or a line break. References are read as the HTML standard reads them: one to 0, to a surrogate or past U+10FFFF is U+FFFD, one to a C1 control is the windows-1252 character there where it has one, and one to any other control is that control (Python's html.unescape drops it). In a URL, as in an attribute, a legacy name without its ; before a letter, a digit or = stays as written. The readings decode everywhere in a string, including where Markdown leaves an escape as written (a code span, an autolink) and outside any link. So they can refuse more: a regular expression's x\.py:12 is read as x.py:12. No live string decodes differently.

  • The joined readings delete every ASCII tab and line break (U+0009, U+000A, U+000D) from each source above, as the URL Standard deletes them from a URL in an HTML attribute. Outside a URL they join a line's end to the next line's start, so x.py ending one line and :12 starting the next is refused. 16 live strings hold a tab or a line break, and reading them joined changes no rule's output.

  • The enclosed readings read any quoted value after = (="…", ='…', with whitespace around the =) and any destination in angle brackets after ]( or ]: (](<…>), ]: <…>), not only in a tag or a link. Each is decoded first, then stripped at its ends (a value of C0 controls and spaces, as the URL Standard strips a URL; a destination of whitespace, as markdown-it strips one), and a value's tabs and line breaks are deleted. One that then starts a URL is read whole, with the whitespace, quotes, angle brackets and parentheses inside it held; any other is left as written. So they can refuse more: prose holding x="https://github.com/o/r/blob/main/a b/../y.py#L12" is read as a line link of y.py. No live string reads differently this way.

  • Where a string holds whitespace other than ASCII whitespace (U+000B, U+001C-U+001F, U+0085, U+00A0 and the other Unicode spaces), its URLs are also read through it, to the next ASCII whitespace. No live string holds any.

  • Where a string holds a ? and a #, its URLs are also read through the whitespace in their query, to the fragment or a line break, three ways: with the query ending at an angle bracket, at a ", or at a ', what can end each enclosure, so a query does not swallow a later link. So these readings can refuse more: in See https://example.org/data.csv?x and, much further on in this note, #L12. the query runs to the #, and the URL reads as a line link of data.csv, which rule 2 refuses. Two live strings hold a ? and a #; neither reads differently this way.

  • Rule 6 refuses a line citation in a string with a dot segment anywhere in it, not only in the cited URL, and another repository's pinned citation too: x.py:12 at a1b2c3d; see ../docs is refused, and so is https://github.com/o/r/blob/a1b2c3d4/a/../x.py#L12. A run of dots that is no whole segment (1 .. 9, x/.../y, sha...) and a lone . are no dot segment. Of the round-10 review's 396 over-refusal probes, the 108 that cite another repository's file through a .. are now refused this way. No live string holds a dot segment.

  • Rule 7 reads links that name this repository's owner and name. A relative link ([a](tools/x.py#L12)) names neither and is left to rules 1 and 2, and a link through a redirect (a renamed repository, a short link) is not followed. It reads prose between the path and a later line fragment as part of the link, so see github.com/PolicyEngine/microcosm/tree/main/docs and cps.py#L197 at a1b2c3d is refused, as a dot segment's string is. An issue, a comment or a heading (PolicyEngine/microcosm#38, /pull/939#issuecomment-1, #readme) is no line, and a # between the path and a later line fragment (/issues/1 (#2) and x.py#L12) starts the link's fragment first, so neither string is refused. 99 live strings name the repository, and rule 7 refuses none. Since round 13 rule 7 resolves dot segments before it matches the path, as a browser does, so it refuses a link whose path a .. forms and not one that a .. sends out of this repository (PolicyEngine/microcosm/../other/blob/<sha>/x.py#L1), which rule 6 refuses where a reading finds the citation. A string does not say whether it is Markdown or HTML, so a backslash is read both ways: an HTML href through x\y/../microcosm, which links PolicyEngine/x/microcosm, is refused, as its Markdown twin, which links this repository, must be.

  • A fragment's % escapes are read as written, so #%4C12 is no citation. A character reference in one is decoded (#&#76;12 is #L12).

  • Escapes other than unreserved characters and / stay as written: GitHub reads %23 and %3F as part of a file name (both return 404 as line anchors), so x.py%23L12 is no line citation. %2F is decoded anywhere in the path, which GitHub does only after the ref, so the non-working github.com/PolicyEngine%2Fmicrocosm/blob/... is refused as in-repository.

  • A query is not read: a sha in one neither pins a citation nor counts as a misspelled pin.

  • A URL's path is resolved only up to the depth of the deepest path form here (10 components today; no tracked file is deeper).

  • A prose pin followed by / is no pin, so repo@<sha>/path/x.py:12 is refused: write repo@<sha> path/x.py:12 or a GitHub URL.

  • The guard does not check which repository a commit belongs to. A commit earlier in the string still pins a later citation unless the citation's clause names a moving ref. The live packages use that pattern on purpose: the PUF note names 42ed5d45… once, and later sentences say "at the same commit".

  • The guard does not check that the cited lines say what the prose claims. For this PR that was checked by reading each changed citation at its commit (see "How this was verified"), and a script checks every cited line number against the file at the bound commit.

Each rule has Hypothesis properties, at least one per refusal branch:

  • an unpinned citation is refused in each of 38 spellings, at any depth, as a value or as a key, and the backstop stays silent on every recognised spelling. Generation draws the reverse spellings, last in the list, least, so since round 9 a worded reverse range (lines 12 to 19 of x.py) is an explicit example;

  • a covering path/commit pin passes a citation;

  • a path pin covers exactly the citations its path ends in (a differential against a string-suffix oracle);

  • an inline commit in any of 12 spellings pins a citation when it comes before it (across clause breaks) or later in its own clause;

  • a locator quoted in any of 8 spellings, with any of 8 quote marks (or none) before and after the file and the numbers, is one citation, as a value or a key, at any depth: unpinned for a foreign file, in-repository for any of the 19 in-repository forms;

  • a commit only in a later clause, after ;, ., ! or ?, does not pin;

  • two citations in separate clauses each bind their own commit;

  • an earlier commit does not pin a citation whose clause names any of 12 moving refs, or a citation whose cited file is a URL at one of 5 moving refs (blob, raw and refs/heads/ forms), across each clause break;

  • a commit named in another string does not pin;

  • none of these names a commit: a decimal, an all-letter word, a prefix of 6 hex digits or fewer, a 41-64-hex digest, a branch name, a truncated <sha>… or <sha>..., or <sha>-dirty;

  • a hex directory after commit, at or @ (at <sha>/x.py:12, at any depth under it) names no commit, leaves the citation unpinned and is no misspelled pin;

  • a pin of another file, or a pin at a branch, does not cover a citation;

  • a commit-like token in no pin spelling is refused in the citation's clause or between the citation and its binding commit, and is not refused in another clause;

  • symbol citations are neither line citations nor backstop hits;

  • a symbol between a file and its lines is refused by the backstop;

  • an in-repository path, in 19 forms including GitHub URLs (at main, a sha, refs/heads/main and feature/citations), raw.githubusercontent.com, microcosm/tools/... and ../microcosm/..., is refused even when pinned inline or by path/commit;

  • a URL of this repository at a ref of 1-4 random segments, under 5 URL prefixes, is refused even when pinned inline;

  • a URL of this repository is refused however a browser and GitHub let it be written. Reading the citation's text again changes nothing. The property varies:

    • the repository: 5 URL prefixes, at a sha or a ref of 1-3 segments;
    • the lead: any of 12 (https://, http://, https:, https:/, https:///, https:\\, http:\/, //, \\, /\, /// or none);
    • the authority: any of 7 (a port, an empty port, :0443, a user, a user and password with a port, or two @s);
    • the host: any case, a trailing dot, and after a lead, up to 6 characters full-width and up to 6 %-escaped as UTF-8;
    • the path: up to 12 characters of the route, ref and path %-escaped in upper or lower hex (/ only after the ref), up to 6 of its / written \, and any of 11 dot-segment detours, four with a quote or a bracket in the segment removed ('x'/../, "/../, <x>/../, `x`/../);
    • the rest: a random query of up to 120 characters, quotes, backticks, curly quotes, angle brackets and other punctuation included, and a #L fragment.

    Since round 7 every full-width, escape and backslash index drawn lands on the host or a / (it wraps around their length). Since ea912094 a dot at a full-width index is written as any of ., 。 and 。, which the standard reads as .. Over 2,000 examples drawn with its strategies at ea912094, 1,490 put a full-width character and 1,467 an escape inside the host, 213, 151 and 121 write a host's dot as ., 。 and 。, 969 add a trailing dot, 1,637 write a separator as \, 688 take a detour with a quote or a bracket, 184 put one in the query, and every lead and authority appears 184-727 times;

  • a random query of up to 120 characters, quotes and brackets included, hides no line citation in another repository's URL, pinned at a sha or unpinned at main;

  • a line link of this repository is refused however Markdown or HTML escapes it. The link is under 5 URL prefixes at a sha, to 3 files at #L<a> or #L<a>-L<b>. It is set in 7 ways: an inline link, a pointy destination, a reference definition, a link with a title, and a double-quoted, single-quoted or unquoted href. 1-24 of its characters are escaped, each as a backslash (Markdown only), a decimal reference with 0-3 leading zeros, a hexadecimal one or a named one, and in an href up to 3 tabs or line breaks go anywhere as references, and in a quoted one also as written. Since round 10 the link may go through a segment that a later .. removes, holding whitespace the setting holds (a no-break space anywhere; in a quoted href or a pointy destination also a space, and in a quoted href a form feed, a tab or a line break), and in HTML up to 2 of the slashes after the authority are written \, with a reference after each. An explicit example puts a decimal reference after a backslash in a double-quoted href, the round-9 review's shape (300 examples; in one run 47% escaped three or more characters, 25% put tabs or line breaks in an href, 81% took a detour and 18% wrote backslashes);

  • every line citation in a string with a dot segment is refused, pinned or not, and without it the same string's citations pass rule 6. The dot segment is one of 8 spellings (.., either dot %2e, &#46;&#46;, .&period;, \.\., or a tab between the dots) beside a /, a \ or a %2f in either case, at 7 places around a citation of another repository; in its place goes one of 7 near misses (., ..., x, %2e, .x, x.., &#46;). The string is a value or a key at depth 0-2 (300 examples);

  • a line link of this repository is refused whatever whitespace its query holds: spaces, tabs, U+00A0 and U+3000 among ab=&+%, up to 60 characters, in 8 enclosures (200 examples);

  • a line link of this repository is refused whatever its ref or authority holds, and its twin in another repository (PolicyEngine/microcosm-dynamics, PolicyEngine/microcosms, NotPolicyEngine/microcosm, PolicyEngine/micro) is not, nor is one through a .. that leaves it (PolicyEngine/x/../../microcosm, PolicyEngine/x%2f..%2fmicrocosm, PolicyEngine/x\..\..\microcosm). The ref is 1-3 segments of letters, digits and _+@!$,;=()-; the authority one of 5 (github.com, a user, and whitespace in a user before an escaped host, a full-width one, or one with an ideographic full stop and a port); the repository one of 9 spellings a browser and GitHub route (case, escapes, a doubled slash, a . segment, and four through a ..: x/../, //x/%2e%2E/../, a%2fb/../ and x\y/../, which a Markdown renderer escapes and a browser in an href reads as x/y/../, linking no line here, and which is refused either way); the route blob, tree or blame; the file one of 3 source files or one of 3 whose names no reading takes for a source (LICENSE, a Dockerfile, SHA256SUMS); the query none, ?plain=1, 30 letters, whitespace or ?a/../../../../..; in 8 enclosures (300 examples);

  • on a temporary tree, a 500-segment ref is searched only as deep as the deepest path there (4 is_file checks), and the deepest path still resolves;

  • on a temporary tree, exactly the anchored tails of three files resolve;

  • the scan for source names finds exactly the matches finditer finds. The forward locator and the backstop are tried only where a match can start (the start of a run of path characters, http(s):, or where the previous match ended), and since round 8 the reverse locator once per list of line numbers, from the list's head rather than from each number in it. Since round 9, when it fails there, it is tried once more from the list's last number, where a range can end and another start (L1-L2-3 of x.py). The text is random path pieces, whole locators, list pieces, https:, quotes and breaks (500 examples; in one run 37% held a forward match, 29% a reverse one and 45% a backstop near match), plus examples that need the previous match's end, https: inside a run, a reverse locator after a failed list, lists mixing L numbers, labels and and, and four chains of ranges;

  • the locators, now reading whitespace, quotes and numbers possessively and finding a spaced dash by a lookbehind, match exactly what round 7's patterns (kept in the test) match, the same spans and the same file names, over random text of list pieces, separators, dashes, quotes, labels and source names (1,000 examples; in one run 47% held a forward match and 30% a reverse one), plus eight built around dashes with and without spaces, quoted lists, AND and chains of ranges;

  • the bindings, misspelled pins and backstop hits found by bisection equal those of the whole-string scans they replace, which the test keeps as the reference: random text of citations, pins in each spelling, stray shas, moving refs and clause breaks (500 examples, plus five built to tie the nearest commits on both sides, end a clause break where a citation starts, put a commit inside a cited URL, fall back past a later clause's moving ref, and mix backstop hits with citations);

  • eight hostile strings are scanned in under 2 s of CPU time: 60,000-character runs of :, //, a and github.com/, 20,000 escapes after https://a/, and strings of 4,000-8,000 citations (unpinned, pinned in their clause, pinned by a commit clauses before, and mixed with backstop hits). Here they take 0.2 s; the round-6 helper takes 92 s on the first five alone. Since round 8 nine more strings must fit in the same 2 s, the exponential ones first, so a regression fails before the slower ones run. Each took 4.6 s or more at ea912094, two over 120 s, and together they take 0.03 s here (see the table under Review). Since round 9 nine more must fit: a failed range with 32,768 spaces after its dash or its to, a label with 65,536 spaces after it, 16,000 ranges each ending where the next starts, a list read again from its last number, and runs of &#0, &#x, \ and &a. Since round 10 seven more, in 2 s of their own: runs of ="https:, ](<https: and &amp followed by a 28-letter name, a quote followed by 100,000 letters, a destination of 50,000 escapes, and URLs through 100,000 no-break spaces (0.8 s here under load, each linear in its length). Since round 12 ten more, in 2 s of their own, each of 52,000-64,000 characters: runs of this repository's path, of that path with a line fragment, of #L1, /./, /, %2f..%2f and =..%2f, a= before a citation, =x.py:1, and a= after lines 1 of (0.23 s here; quadrupling each quadruples its time). Since round 13 five more in those 2 s: PolicyEngine/ then 10,000 x/ and 10,000 ../, and runs of /.., ?/.., %2e%2E/ and \.. (0.13 s here; quadrupling each quadruples its time);

Differential tests:

  • every commit a uk_data_target_parity.json citation binds to is its own incumbent_commit;
  • every commit a uk_population_targets.json citation binds to is a prefix of its registry_parity.pinned_ref;
  • the PUF note's disclosure-record and disaggregator citations bind to the archived us-data commit, in both source_stages.json and the generated sources.yaml;
  • the ESI origin's loss.py binds to 2e044571 and its cps.py to 42ed5d45;
  • every Census column the US gap prose says the restore leaves out is outside ASEC_CENSUS_PERSON_COLUMN_NAMES;
  • the URL reader against the WHATWG URL Standard: a parametrized test checks 20 readings recorded from bun 1.3's URL (14 hosts read, 6 the standard refuses).

Two local differentials, against bun itself, cannot run in CI, which has no bun (scripts and results in the session's review folder):

  • Every code point. Each of the 1,112,064 code points was escaped into a host (a<cp>b.org) and read by both:

    • 156,364 agree and 147 are refused by both;
    • 955,522 are read by the reader where the standard refuses them;
    • 31 differ only where the standard's reading is Punycode (ß, ς, and letters Python's Unicode 16 does not case-fold yet), so none could be GitHub's.

    Before U+A7F1 was mapped, it was the one code point the standard maps into plain ASCII and the reader did not.

  • Every code point at four places in a URL of this repository (round 7): in a user (https://u<cp>v@github.com/...), inside a dot segment (tools/a<cp>b/../ci_test_plan.py), as a dot segment (tools/<cp>/../) and in a query (?v=<cp>w), each followed by #L12. At each place 1,112,035 code points agree, 0 are read as this repository's only by the reader, and the other 29 are the whitespace code points, where the URL ends by Scope.

  • 26,350 generated URLs. They cross 18 leads, 8 users and 11 ports with host spellings (escapes, full-width and look-alike letters, ignorable code points, ideographic dots, trailing dots, bad escapes), 9 paths and 10 queries and fragments:

    • 13,785 agree and 8,234 are refused by both;
    • 1,107 are read only by the reader, none of which the standard reads as GitHub's;
    • 0 are read only by the standard, and 0 differ;
    • 3,224 are not one URL token, by design: a bare host with a spelled authority, a trailing ? or #, or no path.

Why

The US and UK packages cited sources by line numbers that drift. The in-repository ones drifted under ordinary edits, and the external ones had no commit, so they drifted with the other repository's main. On main (90ff43f9) this guard flags US 45 unpinned + 18 in-repository citations and UK 47 unpinned; am, be and nz are clean. With this branch all five packages scan clean under all seven rules.

Changes

US, ecps_parity_known_gaps.json (6 hermetic_build_contract strings and is_unmarried_partner_of_household_head.evidence.semantic_non_substitutes.derived_A_EXPRRP; main dropped the employer-premium gap that #1146 also edited):

Was Now
asec_pool.py lines 61-75 microcosm.build.us_runtime.asec_pool.load_asec_h5_tables (plus _prepare_year_input where the restore matters)
asec_pool.py lines 397-422 microcosm.build.us_runtime.asec_pool._with_relationship_recode
tools/build_us_puf_support_base.py lines 85-88 and 659-699 _parse_args (--asec-h5), _pooled_asec_sources_from_args, _asec_sources_from_args
buildj_base.sh lines 65-69 / 65-81 / 70-81 and 103-108 the script's --asec-h5 2024=, 2023=, and 2022= (and --puf-h5) arguments
base_j.summary.json lines 55-75 / 317-318 base_source.sources[].sha256, puf_sha256, and base_source.metadata.sources[].share for the equal shares

#720 made several of these sentences false as written, for example "reads the prebuilt person tables without refreshing omitted Census columns". The pool now restores the reviewed Census person columns, so each sentence says which columns the restore leaves out. Tests check:

  • those columns against ASEC_CENSUS_PERSON_COLUMN_NAMES;
  • that the restore runs before the relationship fallback;
  • that the fallback never emits code 13 (a Hypothesis property over synthetic households);
  • that the tool defines the functions the evidence names.

No exclusion decision or reason changes.

US, source_stages.json → regenerated spec/sources.yaml (8 notes): every archived us-data citation names commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe and a package-relative path (datasets/cps/cps.py, datasets/cps/extended_cps.py, parameters/take_up/wic_takeup.yaml, utils/randomness.py). That includes the employer-premium note added since #1146. Round 2 also moves the PUF note's commit ahead of its first two citations: it now reads "The pinned PUF lineage (archived commit 42ed5d45…) … (datasets/puf/puf.py lines 1378-1398) … (datasets/puf/disaggregate_puf.py lines 55-60 and 85-123)". Before, it named the commit only sentences later, which binding would not accept.

US, target_parity_manifest.json and tools/build_us_target_parity_manifest.py: utils/loss.py:67-70 at 42ed5d45…. The ESI origin's CPS citation now reads archived commit 42ed5d45 datasets/cps/cps.py lines 197-271 and 1575-1581. It had read archived 42ed5d45 cps.py L197-271/L1575-1581, which round 1 counted as pinned by the unrelated commit 2e044571 earlier in the string. Both ranges were re-read at 42ed5d45: 197-271 is the ESI imputation block (ESI_POLICYHOLDER_VARIABLE through impute_employer_sponsored_insurance_premiums's return), and 1575-1581 is the policyholder flag and premium assignment.

UK:

Wrong pointers corrected. Each cited range was read at its commit. These seven pointed at other code:

Resource Was Read at the commit Now
us ssi_take_up notes "cps.py line 584 maps … SSI_VAL …, lines 1497-1499 export it" 584 loads the SSI rate; 1497-1499 map SSI_VAL to the temporary ssi_reported anchor; the save is 759-766 labels fixed, 759-766 added
us scf_wealth notes "source_impute.py lines 1146-1164 fits that anchor with these eight predictors" 1146-1164 only add the anchor to the QRF outputs fit 1227-1240, SCF_PREDICTORS 168-177
uk parity hmrc_cgt hmrc_cgt.py:195 a blank line 196 (_band_targets)
uk parity hmrc_spi hmrc_spi.py:230 the docstring's first line, below the def 229 (get_targets)
uk parity DfE education datasets/imputations/frs_only.py no education code services/services.py:137-148 (the dfe_education_spending imputation)
uk population land (3 sectors) sources/_land.py:52-73 the file has 65 lines 48-52, 54-59, 60-65 per sector
uk population salary sacrifice (8 strings) "silently suppressed by the bare-except drop in build_loss_matrix.py:376-394" 376-394 is the dispatch block, with no except the except Exception in targets/sources/hmrc_salary_sacrifice.py lines 34-109, which logs the error and returns only the contributions target

Smaller fix: prior_year_income gains the predictor-definition range extended_cps.py 234-248, matching its sibling stage.

Tests whose literal assertions moved. Three exclusion tests asserted "buildj_base.sh lines 65-69". They now assert the content anchors and check that base_j.summary.json holds three 64-character base_source.sources[].sha256 values.

Re-pins

Every byte edit to source_stages.json moves these (round 2's PUF-note edit moved them again):

Pin Main (90ff43f9) This PR
source_stages.json raw sha256 (sources.yaml stage_asset, generator FROZEN_LEGACY_RESOURCE_SHA256, us_spec_bundle.py, test_us_bundle_core_contracts.py) dc3b7298… 5fc76f9b…
US spec_sha256 (us-f0-coverage.json ×2, test_us_multispine_pool_tool.py) c677d50b… b5c87a41…
US documentation_sha256 (us-f0-coverage.json) 801a99da… 811b4091…
EXPECTED_HASHES.source_manifest 87b1e124… 10c05f0f…
EXPECTED_HASHES.late_resource_semantics 531b48cd… 6634f751…
EXPECTED_HASHES.full_checkpoint 030c8422… c88b0d7f…
us-f0-coverage.json pool-code observed sha af44d502… bbd77902…

How they were produced:

  • sources.yaml was regenerated with tools/generate_us_bundle_from_constants.py; its diff is the rewrapped PUF note and the new raw sha, and --check exits 0.
  • The three inventory digests come from build_inventory_coverage(load_bundle("us")): before the re-pin exactly these three items failed (38/41), each with only its digest differing.
  • us-f0-coverage.json was regenerated with tools/spec_engine_coverage.py: 42235/42235 configuration fields and 41/41 inventory checks. Exactly ten leaves moved: the expected and observed copies of the three inventory digests, documentation_sha256, both spec_sha256 copies and the pool-code observed digest. --check passes.
  • A grep for every superseded hash and its 8-character prefix finds none in use. That covers main's (dc3b7298, c677d50b, 801a99da, 87b1e124, 531b48cd, 030c8422, af44d502) and round 1's (e2de2b41, ca232744, 3cb60930, ecf9ca2b, e8797144, 189d4e28, 20e3cd3b). The one hit is e2de2b41…, round 1's truncated-hash counterexample, quoted as such in the helper's docstring, a test and the changelog.
  • The UK resources are legacy_json, so only the unpinned UK package fingerprints move. uk_data_target_parity.json equals build_uk_data_target_parity() after a JSON round trip.

Operational cost: the stacked US pool checkpoint identity binds the late producer resource semantics, which hash source_stages.json's bytes. Existing stacked US pool stage checkpoints will therefore be treated as stale and rebuilt on the next build, as after any source-stage note edit. No behaviour or data changes.

How this was verified

  • Every citation this rebuild wrote beyond Cite US and UK spec sources by symbol or at a commit, never by a drifting line #1146 was read at its commit in local clones of policyengine-us-data and policyengine-uk-data. That covers:

    The strings carried over from Cite US and UK spec sources by symbol or at a commit, never by a drifting line #1146 verbatim rest on Cite US and UK spec sources by symbol or at a commit, never by a drifting line #1146's audit: as its body reports, proposers read each citation at its commit and separate verifiers re-derived every edit.

  • After merging main, a script checked all 237 US and UK line citations mechanically, taking for each one the commit it binds to under rule 2 (or its covering path pin). In every case the commit resolves in one of the two clones, the cited path resolves to exactly one file at that commit, and every cited line number is within the file. 237 pass, 0 out of range, 0 unresolved. NZ's 46 citations are all covered by path/commit pins to the ops repository.

  • The guard, run against main (90ff43f9) with this branch's helper: US 45 unpinned + 18 in-repository, UK 47 unpinned, am/be/nz 0. The 4 US citations beyond round 1's 41 are the PUF note's two, in both resources, which binding now refuses. Against this branch: 0 for every rule in every package, including main's latest UK resources.

  • Round 2's rules, measured on the tree before adopting each one:

    • the widened grammar found 0 citations the old one missed;
    • binding refused exactly 4: the PUF note's two citations, in both resources;
    • the misspelled-pin rule refused exactly 1, the ESI origin;
    • the backstop, the moving-ref rule, URL and prefix normalization, anchored tails and suffix pins changed 0 classifications.
  • Round 5's URL reading, measured on the tree: it rewrites 69 live strings, all queries on data-source URLs (ArcGIS and ONS geography services, NISRA, Nomis, data.census.gov, and state revenue and legislature downloads), and none of them carries a line citation. A differential scan, the helper at 1840bc92 against 4229cd8c over every resource, finds 0 classification changes across 200 resources and 283 citations.

  • Round 6's authority reading, measured on the tree: it reads every one of the 4,482,233 live strings exactly as round 5 did. Round 6 changes only how URLs are read (the helper's docstring, imports, URL constants and readers), so no classification changes.

  • Round 7, measured on the tree: it reads all 4,482,233 live strings exactly as round 6 did, and a differential scan, the helper at 29983852 against 5f6647b1, finds 0 changes in any rule's output (citations, bindings, misspelled pins, backstop hits, in-repository hits) across 200 resources and 283 citations.

  • Round 8, measured on the tree: the helper at ea912094 against 25b5bbe2 over every resource, 200 resources, 4,482,233 strings and 283 citations. 0 strings read differently the first way, 0 are read a second way, and every rule's output is unchanged. The scan took 89 CPU seconds, against 96 at ea912094.

  • Round 9, measured on the tree: the helper at 25b5bbe2 against a0d20514 over every resource, 200 resources, 4,482,233 strings and 283 citations. Every string's round-8 readings come first and unchanged, no string decodes differently, and every rule's output is unchanged. 16 strings, all UK (source_stages.json and spec/sources.yaml), hold a tab, and each gains a joined reading, which changes no rule's output. The scan took 121 CPU seconds, against 101 at 25b5bbe2.

  • Round 9's reverse scan against finditer: 200,000 random strings of L numbers, plain numbers, dashes, to, through, separators, labels, quotes and of/in endings, 0 differences.

  • Round 9's decoded readings against a renderer: 200,000 random escapings of four line links of this repository (a sha, main, a network-path reference and a relative path), each set as an inline link, a pointy destination, a reference definition or a double- or single-quoted href. markdown-it-py 4.2 rendered each Markdown setting and HTMLParser read each href. 169,582 rendered to a link this guard refuses, and every one of them is refused as written. The rest render to no line link: an escape without its ; followed by a digit (&#49 then 2) is another number.

  • Round 10, measured on the tree: the helper at a0d20514 against c36671e6 over every resource, 200 resources, 4,482,233 strings and 283 citations. Every string's first reading is unchanged, no string gains a reading, and every rule's output is unchanged, as is whether each cited path resolves here. 1,226 strings hold a & or a backslash and are decoded the new ways, each to a reading round 9 already had. No live string holds a Unicode space or reads differently through an enclosed value. The scan took 137 CPU seconds, against 136 at a0d20514.

  • Round 9's review evidence replayed at round 10: all 45 of its mixed-context variants (29 render to a line link of this repository) and all 8,281 targets of its 27,625-input matrix are refused, and no input it found refused passes now. 17 more inputs are refused, strings like \&num;L12 that render to no line link, which the backstop refuses.

  • Round 10's readings against renderers: 200,000 generated links. Five line links of this repository (at a sha, at main, a network-path reference, a deeper file and a relative path) were each set in 7 ways (an inline link, a pointy destination, a reference definition, a link with a title, and a double-quoted, single-quoted or unquoted href), escaped at a rate of 0-30% (a backslash in Markdown, a decimal reference with 0-3 leading zeros, a hexadecimal one or a named one). Half took a detour through a segment holding one of 10 whitespace code points, written or as a reference, that a .. removes. In HTML, half wrote 1-2 slashes as \, often with a reference after one, and 30% put in a tab or a line break. Values and pointy destinations were padded at their ends, and = sometimes had whitespace around it. markdown-it-py 4.2 rendered the Markdown, parse5 7.3 parsed the HTML, and bun 1.3 resolved each link. A link counted when bun's parsed URL was a GitHub path to a file in this checkout with an L<n> or L<a>-L<b> fragment. 113,469 rendered to such a link. Every one written as an absolute URL is refused as written. 12,702 written as relative references are not, the Scope limit, and each of those is still refused by another rule. A first run of 2,000 found five shapes no reading refused, fixed in c36671e6: an attribute value starting &#32; or &#12;, and a destination in angle brackets padded with spaces, U+3000 or &#32;.

  • Round 11, measured on the tree: the helper at c36671e6 against 2822aff1 over every resource, 200 resources, 4,482,233 strings and 283 citations. Every string's first reading is unchanged, no string gains a reading, and every rule's output is unchanged, as is whether each cited path resolves here. Two strings hold a ? and a #, and their query readings are readings they already had. No string holds a dot segment, so rule 6 refuses nothing. The scan took 127 CPU seconds, against 122 at c36671e6.

  • Round 10's review evidence replayed at round 11. Its 607 generated contexts, 51 more it left unclassified and 21 family probes each render to a line link of tools/ci_test_plan.py in markdown-it-py 4.2, parse5 7.3 and bun 1.3, and no rule refused any of them at c36671e6. All 679 are refused now: rule 6 refuses each, and rule 1 also refuses 128. All 8,281 targets of its 27,625-input matrix and all 45 of its mixed-context variants are refused, and no input's refusal changes. Of its 396 over-refusal probes, pinned citations of another repository in prose, attributes and destinations, the 288 without a dot segment are refused exactly as at c36671e6 (4 of them), and the 108 with one are now refused by rule 6, as Scope states.

  • Round 11's readings against renderers. Round 10's 200,000 generated links, rerun with rule 6, give the same counts: 113,469 render to a line link of this repository and each is refused as written, and each of the 12,702 relative ones is still refused by another rule. The round-11 counterexample test's 25 inputs (11 shapes of the four findings and 14 queries holding whitespace) each render to a line link of tools/ci_test_plan.py, and 2,000 inputs drawn as the query property draws them each render to a link to the file at their lines. The guard refuses every one.

  • Round 11's timing strings, runs of what a dot segment, a URL read through its query and a destination after whitespace start with, grow linearly: doubling each doubles its time. A query reading that requires a query, or a fragment, takes 14.5 s on "https:" * 20_000 + " ?#", against 0.02 s; the timing test runs that string.

  • Round 12, measured on the tree: the helper at 2822aff1 against 8a62c938 over every resource, 200 resources, 4,482,233 strings and 283 citations. Every reading and every earlier rule's output is unchanged. No citation's path run differs from its file, no string holds a dot segment with or without %2f, and rule 7 refuses none of the 99 strings that name this repository. The scan took 116 CPU seconds, against 115 at 2822aff1.

  • Round 11's review inputs at round 12. 22 inputs, its two shapes and their variants (7 refs in Markdown and HTML, 5 authorities after a code span's =", a long query, an escaped owner with a doubled slash, and %2f..%2f), each render to a link to line 12 of tools/ci_test_plan.py in markdown-it-py 4.2, parse5 7.3 and bun 1.3. No rule at 2822aff1 refused any of them; rule 7 refuses all 22, and rule 6 the %2f one too.

  • Rule 7 against renderers. The property's space, 8 enclosures by 5 authorities by 9 repository paths by 4 queries by 2 refs, is 2,880 inputs. Of the 1,600 that name this repository, 1,400 render to a line link of it and all 1,600 are refused; the other 200, a tab and a space in a user before a full-width host in a Markdown destination, render to no URL. None of the 1,280 that name another repository is refused. 2,000 inputs drawn as the property draws them render to 1,786 line links of this repository, and all 2,000 are refused, their twins in other repositories none.

  • Round 13, measured on the tree: the helper at 8a62c938 against a738da7d over every resource, 200 resources, 4,482,233 strings and 283 citations. Every reading and every earlier rule's output is unchanged (0 differences), no string holds a dot segment, and rule 7 refuses none of the 99 strings that name this repository. The scan took 117 CPU seconds, against 118 at 8a62c938.

  • A dot segment that forms this repository's path, against renderers: 3,960 inputs, 4 files (LICENSE, a Dockerfile, SHA256SUMS and tools/ci_test_plan.py) by 22 paths (15 that a browser and GitHub route here, such as PolicyEngine/x/../microcosm, x/%2E./, //x/../../, a%2fb/../, x\..\ and &#46;&#46;, and 7 that route elsewhere) by 5 enclosures by 3 authorities by 3 queries. 1,920 render to a line link of this repository in markdown-it-py 4.2, parse5 7.3 and bun 1.3. At 8a62c938 rule 7 refused 264 of them and the rules together 678, so 1,242 passed. All 1,920 are refused now, and none of the 7 paths that route elsewhere. Rule 7 also refuses 780 inputs that render to no line link here, each naming this repository's path once its dot segments are resolved: 540 bare URLs, which markdown-it does not link; 180 Markdown destinations that the space in a user ends; and 60 Markdown links through x\..\, where Markdown reads \. as an escaped dot and HTML keeps the backslash.

  • Rule 7's property space at e5e14e96, rendered the same way: 8 enclosures by 5 authorities by 16 repository paths by 5 queries by 2 refs by 2 files, 12,800 inputs. Of the 7,200 that name this repository, 6,000 render to a line link of it and all 7,200 are refused; of the other 1,200, 900 are the tab and space in a user before a full-width host in a Markdown destination, which render to no URL, and 300 are x\y/../ in an href, which a browser reads as PolicyEngine/x/microcosm (the over-refusal under Scope). None of the 5,600 that name another repository or leave this one is refused, and none routes here. 2,000 inputs drawn as the property draws them render to 1,695 line links of this repository; all 2,000 are refused, their twins none, and no twin routes here.

  • GitHub's routing, by curl --path-as-is on 2026-10-28: PolicyEngine/x/%2e%2e/microcosm/blob/<sha>/LICENSE and x/.%2e/ return 200 (a browser has resolved both before it asks); PolicyEngine/x%5Cy/../microcosm and ..%5Cx/../microcosm return 200 (GitHub reads x%5Cy as one segment, which the .. removes); PolicyEngine/x%2f..%2fmicrocosm, x/..%2fmicrocosm, x/%252e%252e/microcosm, x%5c..%5cmicrocosm and x/y/../microcosm return 404.

  • Rule 7 fuzzed against the same renderers, at 4dc0734e and at a738da7d. A first fuzz, 20,000 links with paths of random tokens, found 3 of its 385 line links here that rule 7 missed at 4dc0734e, each a Markdown destination holding a backslash. A second, 40,000 inputs, builds detours (junk segments, then as many parent segments) through many spellings of ., .., /, \, ? and # (escapes, references, Markdown escapes, tabs), mixed with token soup, in 9 enclosures (inline, pointy, reference definitions, autolinks, bare, and three href quotings) and 6 authorities. 5,590 render to a line link here; rule 7 at 4dc0734e missed 58, each a Markdown link with a backslash, and at a738da7d refuses all 5,590, and all 385 of the first fuzz. Two more seeds of the second at a738da7d bring it to 60,000 inputs, 8,407 line links here, all refused.

Tests

Run locally after merging main, one file at a time, -n 0, -p no:cacheprovider (no full suite). All pass:
After merging 90ff43f9:

  • tools/generate_us_bundle_from_constants.py --skip-validation --check and tools/spec_engine_coverage.py --check (at 4229cd8c; since then c67030f7, 29983852, 5f6647b1, ea912094, 25b5bbe2, a0d20514, fa7601cb, c36671e6, 2822aff1, 8a62c938, 4dc0734e, 5578c82d, a738da7d and e5e14e96 change only the guard's test file, its helper, which nothing else imports, and the changelog fragment): exit 0; coverage 42235/42235 configuration fields, 41/41 inventory checks.
  • test_spec_source_citations.py: 137 passed at e5e14e96 (218 s; 31 of 31 properties reached their limits, 8,600 passing examples)
  • test_us_plan.py: 36 passed
  • test_country_spec.py: 136 passed

On round 2, before merging 90ff43f9. Main's newer commits touch UK resources, uk_runtime, gates.py and us_runtime/worker_identity.py, and CI runs these files on the merged head:

  • test_us_bundle_core_contracts.py: 17 passed
  • test_us_spec_bundle.py: 4 passed
  • test_spec_engine_country_bundles.py: 21 passed
  • test_spec_engine_reemission.py: 8 passed
  • test_spec_engine_loader.py: 10 passed
  • test_spec_engine_inventory_coverage.py: 13 passed
  • test_us_scf_wealth.py: 31 passed
  • test_us_relationship_inputs.py: 17 passed
  • test_us_parity_reference.py: 17 passed, 1 skipped
  • test_us_other_health_insurance.py: 27 passed
  • test_us_financial_assistance_exclusion.py: 3 passed
  • test_us_survivor_benefits_exclusion.py: 3 passed
  • test_us_wic_nutritional_risk_exclusion.py: 4 passed, 1 skipped
  • test_us_dc_ptc_take_up_exclusion.py: 4 passed
  • test_us_early_head_start_exclusion.py: 6 passed, 5 skipped
  • test_us_medicare_take_up.py: 19 passed
  • test_us_asec_sources.py: 41 passed
  • test_release_input_coverage.py: 86 passed
  • test_release_target_parity.py: 37 passed
  • test_us_esi_premiums.py: 148 passed
  • test_us_chronicle_feed.py: 27 passed
  • test_us_soi_congressional_district_vintage.py: 51 passed
  • test_us_soi_state_agi_bands.py: 29 passed
  • test_us_multispine_pool_tool.py: 31 passed

Skips are the files' own data-gated cases. The engine-contract copies and the UK and NZ suites ran in round 1 and CI, and were not rerun locally for round 2. Round 2 changes no UK or NZ resource, and no UK or NZ test names a file it changed. The one shared module it changes, inventory_coverage.py, moves only three US digests. CI runs every suite.

Mutants

Each mutant weakens one rule, or one resource, and runs the guard tests named by its -k expression (-n 0 -x). Files were restored byte for byte after each run. Since round 5 they run in a separate clone of the branch, so a mutant can never reach the branch itself. Since round 6 every run starts with Hypothesis's example database cleared, so no mutant is killed by a counterexample saved from another. The harness first runs the unmutated tests over the union of those expressions and stops if they fail. It counts a mutant as killed only when a test fails, with no setup or collection error. Until the round-10 run finishes, the table shows it as far as it has reached, the round-7 run at ea912094 for the rest of M1-M77, and pending for the rest. The round-8 run at 25b5bbe2 was stopped at M89 when round 8's review arrived; of M1-M89 it killed all but M24, M80 and M81 (see Review, round 8). The round-9 run at a0d20514 killed each of the 37 mutants it reached before round 9's review arrived. The round-10 run at c36671e6 reached M93-M128, then M1-M38, and killed each but M12; it was stopped there. The round-11 run, 150 mutants at 2822aff1, ran with Hypothesis limited to each test's explicit examples and killed all 150. The round-12 run, 167 mutants at 8a62c938 (M131-M135 re-anchored to the new dot-segment pattern, M147-M163 new), ran the same way in three clones at once; the 8 properties with no explicit example were skipped. It killed all 167. Round 13 changes only rule 7's reading, so round 13 reran rule 7's mutants the same way. At 4dc0734e, 16 of them, M147-M157 (M149-M152 re-anchored to its single reading) and M164-M168 new, were all killed. At a738da7d, 17, with M149-M151, M164, M167 and M168 re-anchored to its two backslash readings and M169 new: all 17 were killed (e5e14e96 adds only test inputs). The table shows the a738da7d run for rule 7's mutants and round 12's for the rest:

Mutant Weakens Killed by
M1 an all-digit or all-letter hex run counts as a sha test_a_decimal_a_word_a_digest_a_truncation_or_a_branch_names_no_commit
M2 an inline commit never pins test_a_commit_before_a_citation_or_later_in_its_clause_pins_it
M3 a commit in any string of the resource pins every citation in it test_a_commit_named_in_another_string_does_not_pin_a_citation
M4 a path pin accepts any commit value test_a_pin_of_another_file_or_at_a_branch_does_not_cover_a_citation
M5 a path pin covers every file test_a_path_pin_covers_exactly_the_citations_its_path_ends_in
M6 the in-repository refusal is off test_a_line_citation_of_this_repository_is_refused_even_when_pinned
M7 only root-anchored paths count as in-repository test_in_repository_paths_resolve_from_an_anchor_or_a_repository_prefix
M8 the reverse locator (lines 12-19 of x.py) is dropped test_an_unpinned_line_citation_is_refused_at_any_depth
M9 mapping keys are not scanned test_an_unpinned_line_citation_is_refused_at_any_depth
M10 dotted names outside a shard count as symbols test_only_names_under_a_shard_are_symbol_citations
M11 the nearest commit anywhere in the string binds, not only in the citation's clause test_a_commit_only_in_a_later_clause_does_not_pin_a_citation
M12 no fallback to an earlier commit test_a_commit_before_a_citation_or_later_in_its_clause_pins_it
M13 no clause breaks at all test_a_commit_only_in_a_later_clause_does_not_pin_a_citation
M14 no sentence-end clause breaks test_a_commit_only_in_a_later_clause_does_not_pin_a_citation
M15 the misspelled-pin check is off test_a_commit_in_no_pin_spelling_beside_a_citation_is_refused
M16 the misspelled-pin check ignores the gap back to the binding commit test_a_commit_in_no_pin_spelling_beside_a_citation_is_refused
M17 the misspelled-pin check scans the whole string test_a_commit_like_token_in_another_clause_is_no_misspelled_pin
M18 this-repository prefixes are not removed test_in_repository_paths_resolve_from_an_anchor_or_a_repository_prefix
M19 every path tail counts as in-repository test_in_repository_paths_resolve_from_an_anchor_or_a_repository_prefix
M20 a path pin matches by base name test_a_path_pin_covers_exactly_the_citations_its_path_ends_in
M21 a truncated hash (…, ..., -x) pins test_a_decimal_a_word_a_digest_a_truncation_or_a_branch_names_no_commit
M22 the backstop is off test_a_line_locator_in_an_unrecognised_spelling_is_refused
M23 the backstop also flags recognised citations test_no_resource_cites_a_line_in_an_unrecognised_spelling[uk]
M24 no to/through ranges test_an_unpinned_line_citation_is_refused_at_any_depth
M25 the l./ll. labels and upper-case LINES are dropped test_an_unpinned_line_citation_is_refused_at_any_depth
M26 GitHub /raw/, /blame/ and /commit(s)/ segments no longer pin test_a_commit_before_a_citation_or_later_in_its_clause_pins_it
M27 a clause break inside the citation splits its clause test_a_break_inside_a_citation_does_not_end_its_clause
M28 the moving-ref exception is ignored test_an_earlier_commit_does_not_pin_a_citation_whose_clause_names_a_moving_ref
M29 a colon locator no longer accepts a quote before its number test_the_round_two_review_counterexamples_are_refused
M30 a # locator no longer accepts a quote before its L number test_a_quoted_line_locator_is_refused_as_a_value_or_a_key
M31 a prose pin (commit, at, @) may run on into a path test_the_round_two_review_counterexamples_are_refused
M32 a moving ref must be unquoted test_the_round_two_review_counterexamples_are_refused
M33 a raw.githubusercontent.com URL at a branch is no moving ref test_the_round_two_review_counterexamples_are_refused
M34 a refs/heads/<branch> ref is no moving ref test_an_earlier_commit_does_not_pin_a_citation_whose_clause_names_a_moving_ref
M35 a URL's ref is one segment test_the_round_three_review_counterexample_is_refused
M36 line citations scan URLs as written test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M37 the backstop scans URLs as written test_the_round_four_review_counterexamples_are_refused
M38 a URL's query is kept test_a_query_does_not_hide_a_line_citation
M39 path escapes are not decoded test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M40 an escaped / is not decoded test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M41 dot segments are kept test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M42 the host is kept as written: not decoded, mapped or lower-cased test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M43 GitHub URLs without a scheme are not read test_the_round_four_review_counterexamples_are_refused
M44 every ref split is tried, however long the ref test_a_long_ref_is_searched_only_as_deep_as_the_deepest_path_here
M45 the depth bound is one segment short test_a_long_ref_is_searched_only_as_deep_as_the_deepest_path_here
M46 every escape is decoded, %23 and %3F included test_the_round_four_review_counterexamples_are_refused
M47 a URL may end in punctuation test_the_round_four_review_counterexamples_are_refused
M48 a . segment is kept test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M49 the host is not percent-decoded test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M50 network-path references (//host/...) are not read test_the_round_five_review_counterexamples_are_refused
M51 \ is not read as / test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M52 the host is lower-cased but not mapped to ASCII test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M53 no compatibility mapping before case folding (ℌ stays H) test_a_url_is_read_as_the_url_standard_reads_it[https://git\u210cub.com/o/r/blob/a1b2c3d/x.py#L12-https://github.com]
M54 default-ignorable code points stay in the host test_a_url_is_read_as_the_url_standard_reads_it[https://gi%C2%ADthub.com/o/r/blob/a1b2c3d/x.py#L12-https://github.com]
M55 Unicode 17's U+A7F1 is not mapped test_the_round_five_review_counterexamples_are_refused
M56 the user is kept in the host test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M57 the port is kept in the host test_the_round_five_review_counterexamples_are_refused
M58 a port that is not a number is read test_the_round_five_review_counterexamples_are_refused
M59 a port above 65535 is read test_the_round_five_review_counterexamples_are_refused
M60 a host holding a character the standard forbids is read test_the_round_five_review_counterexamples_are_refused
M61 a host's trailing dot is kept test_a_trailing_dot_is_dropped_from_a_host
M62 https: must be followed by // test_the_round_five_review_counterexamples_are_refused
M63 a GitHub host without a scheme may not follow @ test_the_round_five_review_counterexamples_are_refused
M64 a GitHub URL without a scheme may not carry a port test_a_url_of_this_repository_is_refused_however_github_lets_it_be_written
M65 a network-path reference is read as a GitHub URL without a scheme test_the_round_five_review_counterexamples_are_refused
M66 。 is not read as . test_a_url_is_read_as_the_url_standard_reads_it[https://github\u3002com/o/r/blob/a1b2c3d/x.py#L12-https://github.com]
M67 a URL ends at a quote or an angle bracket test_the_round_six_review_counterexample_is_refused
M68 a user is looked for before a GitHub host without a scheme (quadratic) test_a_long_string_is_scanned_in_linear_time
M69 a source-name pattern is tried from every character test_a_long_string_is_scanned_in_linear_time
M70 a source-name match is not tried where the previous one ended test_source_names_are_found_where_finditer_finds_them
M71 a source-name match is not tried at http(s): inside a run test_source_names_are_found_where_finditer_finds_them
M72 each citation finds its string's breaks, commits and tokens again test_a_long_string_is_scanned_in_linear_time
M73 the moving-ref check runs once per citation, not once per clause test_a_long_string_is_scanned_in_linear_time
M74 the nearest commit is sought among all of the string's commits test_a_long_string_is_scanned_in_linear_time
M75 the commit just before a citation is no candidate test_bindings_are_found_as_a_scan_of_the_whole_string_finds_them
M76 the commit just after a citation is no candidate test_bindings_are_found_as_a_scan_of_the_whole_string_finds_them
M77 the misspelled-pin check scans from the string's start test_bindings_are_found_as_a_scan_of_the_whole_string_finds_them
M78 a clause break ending where a citation starts is ignored test_bindings_are_found_as_a_scan_of_the_whole_string_finds_them
M79 the list separator is round 7's ambiguous one (,, and or , and, whitespace given back) test_a_long_string_is_scanned_in_linear_time
M80 whitespace and digits in a line number or a range are given back test_a_long_string_is_scanned_in_linear_time
M81 whitespace after a line(s), l. or ll. label is given back test_a_long_string_is_scanned_in_linear_time
M82 whitespace in a forward locator is given back, and a spaced dash is found as \s- test_a_long_string_is_scanned_in_linear_time
M83 quotes around a locator are given back test_a_long_string_is_scanned_in_linear_time
M84 a reverse locator is tried from every number of a list (plain finditer) test_a_long_string_is_scanned_in_linear_time
M85 a label longer than 63 characters is written in Punycode test_a_long_string_is_scanned_in_linear_time
M86 a spaced dash needs no space before it (x.py- L12) test_the_locators_match_as_before_their_whitespace_was_read_possessively
M87 the second reading is dropped test_the_round_seven_review_counterexamples_are_refused
M88 the second reading does not end a URL at a quote test_a_string_is_read_as_a_browser_and_as_markdown_delimit_its_urls
M89 the second reading does not end a URL at an angle bracket test_the_round_seven_review_counterexamples_are_refused
M90 the second reading does not end a URL at a parenthesis test_the_round_seven_review_counterexamples_are_refused
M91 a failed reverse locator is not tried again from its list's last number test_the_round_eight_review_counterexamples_are_refused
M92 a failed reverse locator is tried again from the first L number after its list's start, not from its last test_a_long_string_is_scanned_in_linear_time
M93 the decoded readings are dropped test_the_round_eight_review_counterexamples_are_refused
M94 a backslash escape is not decoded test_the_round_eight_review_counterexamples_are_refused
M95 a numeric character reference is not decoded test_the_round_eight_review_counterexamples_are_refused
M96 a named character reference is not decoded test_the_round_eight_review_counterexamples_are_refused
M97 a numeric reference is converted whole (Python refuses more than 4,300 digits) test_a_string_is_read_again_with_its_markup_escapes_decoded
M98 a numeric reference's leading zeros count toward its length test_a_string_is_read_again_with_its_markup_escapes_decoded
M99 a numeric reference needs its ; test_the_round_eight_review_counterexamples_are_refused
M100 tabs and line breaks are not deleted test_the_round_eight_review_counterexamples_are_refused
M101 a decoded reading is not read again with its tabs and line breaks deleted test_a_string_is_read_again_with_its_markup_escapes_decoded
M102 a tab is not deleted test_the_round_eight_review_counterexamples_are_refused
M103 the HTML reading (references alone, backslashes as written) is dropped test_the_round_nine_review_counterexamples_are_refused
M104 the Markdown reading (backslash escapes and references) is dropped test_the_round_eight_review_counterexamples_are_refused
M105 the decoded readings are read only as text, never as in a URL test_the_round_nine_review_counterexamples_are_refused
M106 the decoded readings are read only as in a URL, never as text test_a_string_is_read_again_with_its_markup_escapes_decoded
M107 a reference to a control is dropped, as Python's html.unescape drops it test_the_round_nine_review_counterexamples_are_refused
M108 a reference to a C1 control is not read as windows-1252 reads it test_a_string_is_read_again_with_its_markup_escapes_decoded
M109 a reference to 0 is U+0000, not U+FFFD test_a_string_is_read_again_with_its_markup_escapes_decoded
M110 a reference to a surrogate is the surrogate, not U+FFFD test_a_string_is_read_again_with_its_markup_escapes_decoded
M111 in a URL, a legacy name before a letter, a digit or = is decoded test_a_string_is_read_again_with_its_markup_escapes_decoded
M112 in a URL, what a reference decodes to is not held test_the_round_nine_review_counterexamples_are_refused
M113 in a URL, a decoded tab or line break is percent-encoded, not deleted test_a_string_is_read_again_with_its_markup_escapes_decoded
M114 the enclosed reading is dropped test_the_round_nine_review_counterexamples_are_refused
M115 an attribute's value is not stripped at its ends test_the_round_nine_review_counterexamples_are_refused
M116 an attribute's tabs and line breaks are not deleted before it is checked for a URL test_the_round_nine_review_counterexamples_are_refused
M117 a destination in angle brackets is not decoded test_the_round_nine_review_counterexamples_are_refused
M118 URLs are never read through Unicode spaces test_the_round_nine_review_counterexamples_are_refused
M119 an enclosed URL's whitespace is not held test_the_round_nine_review_counterexamples_are_refused
M120 an enclosed value that starts no URL is read as one too test_a_string_is_read_again_with_its_markup_escapes_decoded
M121 no whitespace is allowed around an attribute's = test_the_round_nine_review_counterexamples_are_refused
M122 a destination in angle brackets is never read test_the_round_nine_review_counterexamples_are_refused
M123 the Unicode-space gate never opens test_the_round_nine_review_counterexamples_are_refused
M124 an in-repository path's inner .. is not resolved test_in_repository_paths_resolve_from_an_anchor_or_a_repository_prefix
M125 a leading .. is skipped, so a path that climbs out resolves test_in_repository_paths_resolve_from_an_anchor_or_a_repository_prefix
M126 a destination in angle brackets is not stripped at its ends test_the_round_nine_review_counterexamples_are_refused
M127 an attribute's references are held before its ends are stripped (as at fa7601cb) test_the_round_nine_review_counterexamples_are_refused
M128 a destination's references are held before its ends are stripped test_the_round_nine_review_counterexamples_are_refused
M129 rule 6 is off test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M130 a dot segment counts only as written, not in a decoded, enclosed or joined source test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M131 a dot segment is found only as literal dots, not with %2e test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M132 a dot segment is found only beside / or %2f, not \ test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M133 a lone . counts as a dot segment test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M134 a dot segment that starts a path (../x) is not found test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M135 a dot segment must have a separator after it (docs/.. at a string's end is missed) test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M136 the query readings are off test_a_line_link_of_this_repository_is_refused_whatever_whitespace_its_query_holds
M137 a query reading stops at whitespace test_a_line_link_of_this_repository_is_refused_whatever_whitespace_its_query_holds
M138 no query reading ends a query at an angle bracket test_the_round_ten_review_counterexamples_are_refused
M139 no query reading ends a query at " test_the_round_ten_review_counterexamples_are_refused
M140 no query reading ends a query at ' test_the_round_ten_review_counterexamples_are_refused
M141 a query reading requires a query (quadratic where none follows) test_a_long_string_is_scanned_in_linear_time
M142 a query reading requires a fragment (quadratic where none follows) test_a_long_string_is_scanned_in_linear_time
M143 no whitespace is allowed before an angle destination's < test_the_round_nine_review_counterexamples_are_refused
M144 rule 6 refuses only a string's first citation test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M145 no line break is allowed before an angle destination's < test_the_round_ten_review_counterexamples_are_refused
M146 only spaces, not tabs, are allowed before an angle destination's line break test_the_round_ten_review_counterexamples_are_refused
M147 rule 7 is off test_the_round_eleven_review_counterexamples_are_refused
M148 rule 7 reads only the string as written, not its decoded, enclosed or joined sources test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M149 rule 7 keeps a path's escapes (%50olicyEngine) test_the_round_eleven_review_counterexamples_are_refused
M150 rule 7 reads a backslash only as no separator (x\..\ in an HTML href) test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M151 rule 7 keeps a run of slashes (PolicyEngine//microcosm) test_the_round_eleven_review_counterexamples_are_refused
M152 rule 7 resolves no dot segment (PolicyEngine/x/../microcosm) test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M153 rule 7 matches the owner and name in one case only test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M154 the owner may end a longer name (NotPolicyEngine/microcosm) test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M155 the name may run on (PolicyEngine/microcosm-dynamics) test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M156 any fragment is a line (#readme, #38) test_a_line_link_of_this_repository_is_refused_by_its_path_alone
M157 the path may come anywhere in the string, after the fragment too test_the_round_eleven_review_counterexamples_are_refused
M158 no dot segment is found after a %2f test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M159 no dot segment is found before a %2f test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M160 a dot segment that starts a path is not found before a %2f (..%2fdocs) test_a_dot_segment_refuses_every_line_citation_in_its_string_pinned_or_not
M161 the path run before a cited name is not tried as a path here test_a_path_here_that_holds_an_equals_sign_is_read_whole
M162 the path run stops at an = test_a_path_here_that_holds_an_equals_sign_is_read_whole
M163 a reverse locator reads no name after an = test_a_path_here_that_holds_an_equals_sign_is_read_whole
M164 rule 7 reads %2e as no dot (x/%2E%2e/) test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path
M165 rule 7 reads only a lowercase %2e as a dot test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path
M166 rule 7 resolves dot segments across a ? (LICENSE?a/../../../../..) test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path
M167 rule 7 decodes %2f before it resolves dot segments (x%2f..%2fmicrocosm) test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path
M168 rule 7 reads a run of slashes as one before it resolves dot segments (//x/../../) test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path
M169 rule 7 reads a backslash only as / ([a](…/PolicyEngine/x\y/../microcosm/…)) test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path
D1 the ESI origin respelled archived 42ed5d45 (round 1's wording) test_no_resource_misspells_a_commit_pin[us]
D2 a UK parity citation at 8629dbb, not the register's a43e8c5 test_uk_data_parity_citations_bind_the_incumbent_commit
D3 the PUF note names its commit only after its first citations (round 1's wording) test_no_resource_cites_a_line_it_does_not_pin[us]
D4 a US citation in an unrecognised spelling test_no_resource_cites_a_line_in_an_unrecognised_spelling[us]

M16 survived the first run: the property's "between" case put the stray sha after the clause break, inside the citation's own clause. 01da9a37 adds a placement in the binding commit's clause, and M16 is now killed by commit 000000a archived 000000a; _.py:1.

M27 survived the first run in the clean clone, at c67030f7. Its earlier kills had come from a counterexample Hypothesis saved in the author's checkout and replayed on every later run. Round 6 adds a deterministic example, x.py; lines 12-19 at a1b2c3d, and clears the database before every run.

M78 survived the round-7 run at ea912094: no generated string put a clause break flush against the citation after it. 25b5bbe2 adds two examples to the binding differential, at a1b2c3d; on main;x.py:12 and 9f8e7d6c;x.py:12, and each kills it.

M12 survived the round-10 run at c36671e6, after six rounds that killed it. Its one killer was a 300-example property, and the kill needed a commit in an earlier clause with a ; in the generated prose; seeded in-process runs drew that combination in 0 of 100 runs. An explicit example now kills it. Before the round-11 run, each mutant was checked in-process at c36671e6's tests: the mutated helper loaded in place of the helper, no file edited, and each test run on its explicit examples alone (M44, M45, M53 and M54, whose tests are parametrized, only the harness runs). 33 mutants were killed only by random generation: M1, M2, M3, M4, M5, M6, M9, M11, M12, M13, M14, M15, M16, M17, M20, M21, M22, M23, M25, M26, M34, M36, M38, M39, M40, M41, M42, M48, M49, M51, M52, M56, M64. For each, Hypothesis searched for a falsifying input with the mutant loaded, and that input, in readable values, is now an explicit example. Each is shown to pass on the helper and to fail with its mutant applied.

Review

Round 1: a GPT-6.1 Sol review was queued with every Codex lane at its limit, and it ran on the Opus fallback (job 20261026-160321-microcosm-1407-review-r1, against d9672bd9), which had no shell. It returned REQUEST_CHANGES with six findings. Each is now addressed:

# Finding Response
1 One commit anywhere in a string pins every citation in it (obr.py:302 on main; compare us-data at 42ed5d4 passed); the ESI origin's cps.py was "pinned" by the unrelated 2e044571 Binding (rule 2), plus the moving-ref exception: that example is now refused, and so is its reverse, us-data at 42ed5d4; obr.py:302 on main. The misspelled-pin rule (3) caught the ESI origin, which now spells its own commit. The PUF note now names its commit first. Properties cover each case.
2 x.py:L12, at line, ; lines, —, [L12], to/through/and ranges, em dash and ll. were unrecognised All are recognised now (38 property spellings), and the backstop (rule 4) refuses any labelled locator after a source name that no spelling matches. The registry fixture's build_loss_matrix.create_target_matrix:93-103 stays out of scope, as documented: it is a dotted name, read the same way as ledger keys.
3 github.com/PolicyEngine/microcosm/blob/<sha>/tools/x.py#L12 and microcosm/tools/x.py escaped the in-repository rule Prefix normalization (rule 1); both forms are in the property's 16 in-repository forms. The bare-name limit is stated in scope.
4 A path pin covered any file with the same base name Component-suffix matching, with a differential property against a string oracle
5 Classification depended on any tail of this tree (utils/loss.py) Anchored tails only; a temporary-tree test pins exactly which tails resolve
6 at e2de2b41… counted as a pin …, ... and - end a token rather than a sha, for pins and for misspelled-pin tokens

Round 2: GPT-6.1 Sol (job 20261026-184526-microcosm-1407-review-r2, against 5221cd6a) ran the guard suite (69 passed; all 18 properties reached their limits with 4,100 passing examples, none vacuous), confirmed each round-1 counterexample is fixed, re-read the round-2 citation edits at 42ed5d45, and grepped the 14 superseded hashes. It returned REQUEST_CHANGES with three P2 findings, all fixed in f30da07c:

# Finding Response
1 A quoted number after a colon (tools/build_us_puf_support_base.py:"12-19", value or key) was no citation, so an in-repository line citation passed A colon or # locator now accepts quotes before its number. A new property quotes 8 locator spellings at 4 slots with 8 marks, for foreign and in-repository files, as values and keys.
2 A moving ref in a raw URL (commit a1b2c3d; https://raw.githubusercontent.com/<o>/<r>/main/.../cps.py#L12) or quoted (x.py:12 on `main` ) still inherited the earlier commit The moving-ref rule now covers raw.githubusercontent.com/<o>/<r>/<branch>/, refs/heads/<branch> and quoted branch names. The moving-ref property gains 7 spellings, and a new property cites URLs at 5 moving refs after an earlier commit.
3 look at a1b2c3d/foo.py:12 read the directory a1b2c3d as a pin After commit, at or @, a sha followed by / is no pin; URL pins (/blob/<sha>/x.py) are unchanged. A new property covers hex directories after each prose spelling.

All five of the review's inputs are now refused, in a dedicated example test. Re-running the reviewer's own 48 probes at f30da07c changes exactly six outcomes, each one of those inputs or a variant of it: the two hex-directory probes, x.py:"12-19" and x.py:"L12-L19" (the second was already refused by the backstop, and is now a citation), and the quoted in-repository value and key. The three fixes change no classification in the live packages: 283 citations, 0 hits under every rule, and all 237 US/UK citations still bind and are in range.

The review's "Not verified" generator and coverage checks timed out in its environment (clean-worker import over 300 s); both pass here at f30da07c, after the merge of 90ff43f9: the generator's --check exits 0, and spec_engine_coverage.py --check reports 42235/42235 configuration fields and 41/41 inventory checks (106 s, 1.9 GB peak).

Round 3: GPT-6.1 Sol (job 20261026-205923-microcosm-1407-review-r3, against f30da07c) replayed round 2's inputs and probes and confirmed all three are fixed. It ran the guard suite (73 passed; it reports 21 properties at their limits, 4,800 passing examples, none vacuous) and hand-applied M31 and M33, each killed by its intended assertion. It confirmed repo@<sha>/path is refused by design, that mainland keeps the earlier-commit fallback, and that a GitLab /-/raw/main/ URL is refused. It returned REQUEST_CHANGES with one P2, fixed in 1840bc92:

# Finding Response
1 https://raw.githubusercontent.com/PolicyEngine/microcosm/refs/heads/main/tools/build_us_puf_support_base.py#L12 at a1b2c3d (and the /blob/refs/heads/main/ form) escaped the in-repository rule: the normalizer took refs as the ref and left heads/main/tools/... unresolved The normalizer now stops at the ref, and the resolver tries the path after each segment, so a ref of any depth resolves, slash-named branches included (/blob/feature/x/tools/..., which the review did not name, had the same gap). Three ref-qualified forms join the in-repository list, a new property cites a file at a ref of 1-4 random segments under 5 URL prefixes, and an example test runs the review's input. No live classification changes: 283 citations, 0 hits, main's counts unchanged.

Round 4: GPT-6.1 Sol (job 20261026-215022-microcosm-1407-review-r4, against 1840bc92) replayed round 3's input and its /blob/refs/heads/main/ form, both now refused. It ran the guard suite (75 passed; 22 properties at their limits, 5,000 passing examples) and hand-applied M35, which both intended tests killed. It exercised dot segments, queries and fragments, ambiguous refs (/blob/tools/tools/...), the neighbouring PolicyEngine/microcosm-dynamics (still foreign) and a file deleted since an old sha (not refused: it resolves against this checkout only, as Scope states). It returned REQUEST_CHANGES with one P2, fixed in 4229cd8c:

# Finding Response
1 https://github.com/PolicyEngine/microcosm/blob/1840bc92…/%74ools/build_us_puf_support_base.py#L12 extracted the file 74ools/build_us_puf_support_base.py, foreign and pinned by the URL's sha, and …/tools/build_us_puf_support_base.py?plain=1&review=<32 x's>#L12 yielded no citation, because the query was longer than the backstop's 24-character window. Both URLs return HTTP 200 Each URL is now read the way GitHub reads it before any rule runs (see Invariants). Each form was checked against GitHub first. These return 200: %74ools, tools%2F…, …%2Epy, docs/../, docs/%2e%2e/, ./, an upper-case host, :443, a user, %62lob and the review's query. These return 404: %23L12, %3Fplain=1, /BLOB/ and PolicyEngine%2Fmicrocosm. A new property writes a URL of this repository in each way GitHub accepts, another hides line citations behind random queries, and an example test runs the review's two inputs and the forms above. The review's nonblocking finding, an 8,000-segment ref that took 115 s and 1.09 GB to resolve, is fixed as well: the resolver tries only paths no longer than the deepest path form here, and that input now resolves in under a millisecond; a temporary-tree test counts the is_file checks. URL reading rewrites 69 live strings and changes no classification: 283 citations, 0 hits, main's counts unchanged.

The mutant harness's unmutated baseline then caught a generator bug in the new URL property: it could escape a character inside an escape already there, writing %%32E for %2E, which names another path. c67030f7 fixes the generator and leaves the helper unchanged. Both URL properties then passed under eight Hypothesis seeds, 500 examples each.

Round 5: GPT-6.1 Sol (job 20261026-230951-microcosm-1407-review-r5, against c67030f7) checked round 5 broadly:

  • it replayed round 4's two inputs, both now refused, and reran its 28 URL probes plus 45 more;
  • it reproduced the 11 GitHub 200s and 6 404s above with curl;
  • it measured the 8,000-segment stress at 0.75 ms and 387 MB, and confirmed the depth bound misses no tracked file;
  • it compared readings over all 4,482,233 live strings: 69 change and no classification does;
  • it ran the guard (79 passed; 24 properties, 5,500 examples) and hand-applied M39 and M45, both killed.

It returned REQUEST_CHANGES with one P2, fixed in 29983852:

# Finding Response
1 https://%67ithub.com/PolicyEngine/microcosm/blob/c67030f7…/tools/ci_test_plan.py#L12 extracted 67ithub.com/…. //github.com:443/…/tools/ci_test_plan.py#L12 extracted 443/PolicyEngine/…. Both were foreign, pinned by the URL's sha, and served with HTTP 200. //user@github.com/…/%74ools/ci_test_plan.py#L12 combined a user with round 4's escape. urlsplit kept the host's escapes, and a GitHub URL without a scheme was read only when a slash followed its host The authority is now parsed as the WHATWG URL Standard parses it (see Invariants), not by urlsplit. The host is percent-decoded, mapped to ASCII as UTS46 maps it and lower-cased. A user and a port are dropped, \ is /, any number of slashes may lead, and a network-path reference may name any host. Python's IDNA codec was tried first and rejected: its Unicode 3.2 tables read github.cₒm as Punycode, where the standard reads github.com. The reader was checked against bun's URL over every code point and 26,350 generated URLs (see Differential tests). An example test runs the review's three inputs, nine more spellings, and four authorities the standard refuses. A table test checks 20 readings recorded from the standard. The URL property gains leads, authorities, full-width and escaped host characters, trailing dots and \ separators. Round 6 reads all 4,482,233 live strings exactly as round 5 did, so no classification changes.

Round 6: GPT-6.1 Sol (job 20261027-003202-microcosm-1407-review-r6, against 29983852) confirmed round 5's three inputs and its probes are refused. Its own differential against bun covered 155,184 URLs, it found 0 reading changes over 4,482,233 live strings, and it ran the guard (102 passed). It hand-applied M53 and M64, both killed. It returned REQUEST_CHANGES with one P2, fixed in 5f6647b1:

# Finding Response
1 https://github.com/PolicyEngine/microcosm/blob/29983852…/tools/ci_test_plan.py?value='<40 x's>'#L12 yielded no citation: the URL token ended at the quote, so #L12 was read as prose beyond the backstop's window. Bun reads the quote as part of the query, and GitHub serves the URL A URL now runs to whitespace and does not end in a quote, a bracket or sentence punctuation (see Scope). The same class covered a quote or a bracket in a dot segment the browser removes (tools/'x'/../ci_test_plan.py), which this round found and fixes too. GitHub served each form with HTTP 200, escaped as a browser sends it. An example test runs the review's input with each quote and bracket in a query or a dot segment, and quoted whole URLs. The URL property now writes quotes and brackets in queries and detours, and its indices always land. A code-point differential at four places in the URL agrees with bun everywhere but at whitespace. No live reading or classification changes.

The review's nonblocking note, that looking for a user before a GitHub host written without a scheme took time quadratic in a run of punctuation (32,000 : took 1.64 s), is fixed as well: the host is matched where it starts, and a user before it is prose. Hunting for other hostile strings found three older quadratic scans, all fixed here and none new in round 6. Each source name was rescanned from every character of a long run of path characters (60,000 a took 19.6 s), and each citation found its string's clause breaks, commits and commit-like tokens again (2,000 pinned citations in one string took 5.8 s). The backstop compared each near match with every citation. Now the source-name scan starts only where a match can start, and each string's marks are found once and searched by bisection. Two differential properties check the new paths against finditer and against the whole-string scans, and a timing test runs eight hostile strings in 0.2 s.

Round 7: GPT-6.1 Sol (job 20261027-035952-microcosm-1407-review-r7b, against ea912094) confirmed round 6's input is refused. It ran the guard (110 passed; 26 properties, 6,500 passing examples), checked the Unicode, scan and binding differentials and all 4,482,233 live strings, and hand-applied M66, M70 and M75, each killed. It returned REQUEST_CHANGES with two P2s, both fixed in 25b5bbe2:

# Finding Response
1 [https://example.org/?x](<https://github.com/PolicyEngine/microcosm/blob/29983852…/tools/ci_test_plan.py#L12>) yielded no citation. The URL token ran to whitespace, so the label's query swallowed the link's destination, which CommonMark renders as a link to this repository's file and round 6 had refused A string is now also read with each URL ending at a quote, a parenthesis or an angle bracket, as Markdown and prose delimit one, wherever that reads differently, and every rule checks both readings (see Invariants and Scope). An example test runs the review's input and seven more shapes from its report: an autolink pair, a raw destination, network-path and scheme-less destinations, a relative tools/ci_test_plan.py#L12, an HTML <a href> and a compact quoted list. Another pins the two readings: a URL glued to another by a quote is now read as a URL of its own, and a quote in another repository's query is refused in the second reading, the over-refusal Scope states. No live string reads differently the second way.
2 line_citations("L1 and " * 24) took 13.1 CPU seconds and found nothing: a failed list of line numbers was retried once for each way to share its whitespace out The list grammar is unambiguous now. Whitespace, quotes and digits are read possessively; a separator is , (then an optional and) or and; and a spaced dash is found by a lookbehind, so it no longer competes with the whitespace before it. A reverse locator is tried once per list, from its head, rather than from each number in it: started inside a list, it reads to the same end, and what ends a reverse locator (of, in) cannot continue a list. Hunting for more hostile shapes found three other superlinear scans, all fixed: a failed locator was retried once for each way to share a run of spaces or quotes out, a reverse locator was tried from each number of a failed list, and a long host label was written in Punycode, whose encoder is quadratic (a label over 63 characters is now left unencoded; see Scope). A differential property checks the new locator patterns against round 7's, and the timing test runs the nine strings below.

Each string below, in CPU seconds for line_citations and unrecognised_line_locators together, one process per string, stopped at 120 s:

String Shape ea912094 25b5bbe2
"L1 and " * 24 a failed list, shared out 15.05 0.00
"lines " + "1 and " * 8 + "in x.py" a failed list, shared out 4.60 0.00
"lines " + "1 and " * 10 + "in x.py" a failed list, shared out over 120 (stopped) 0.00
"lines " + "1, " * 24 + "in x.py" a failed list, shared out 4.82 0.00
"x.py" + " " * 1_024 + "x" spaces after a source name, shared out 27.74 0.00
"x.py" + "'" * 16_000 + "L1x" quotes after a source name, shared out 5.58 0.00
"line " + " " * 32_768 + "1x in x.py" spaces after a label, shared out 6.43 0.01
"L1 and " * 8_000 a failed list, and a reverse locator tried from each number over 120 (stopped) 0.01
an 8,000-code-point host label Punycode 7.81 0.00

Round 8: GPT-6.1 Sol (job 20261027-064542-microcosm-1407-review-r8b, against 25b5bbe2) confirmed round 7's two findings are fixed. It replayed 191 round-7 URL probes and 24 boundary additions, and reran round 7's timing input: 14.7 CPU seconds on the old helper, under a millisecond now. It timed 153 hostile workloads at n and 4n and found no superlinear family. Against the ea912094 locator patterns it compared 377,119 texts and found 0 differences, and against bun it replayed 155,184 URLs. Over all 4,482,233 live strings, every rule's output was unchanged and no string was read a second way. It ran the guard (113 passed; 27 properties, 7,500 examples) and hand-applied three mutants: M84 and M87 were killed, and M80 survived. It returned REQUEST_CHANGES with two P2s, both fixed in a0d20514:

# Finding Response
1 L1-L2-3 of tools/ci_test_plan.py yielded no citation. A reverse locator was tried only from a list's head: it read L1-L2, failed, and moved past the list. But a range can end where another starts, and from L2 the locator reads L2-3 and succeeds When a reverse locator fails from a list's head, it is now tried next from the list's last number, if that is an L number, and only then after the list. From any other number it reads the rest of the list as the head did, so only the last number can read differently (see _finditer). Each number is read in at most two lists, so the scan stays linear. The round-8 example test runs the review's input and three more chains. Both differentials gain chain pieces and four examples each. 200,000 random chained lists agree with finditer
2 [a](https://github.com/PolicyEngine/microcosm/blob/29983852…/tools/ci_test_plan\.py#L12) yielded no citation. CommonMark decodes \. in a link destination, and character references likewise (&#35;L12, #&#76;12, &#46;py) A string holding a backslash before ASCII punctuation, or a character reference, is now read again with them decoded. Decoding is one pass, as CommonMark decodes a destination and HTML an attribute; a numeric reference may lack its ;. The URL Standard also deletes ASCII tabs and line breaks from a URL, which reveals a link in an href that decoding alone would not (.../x.p&#10;y#L12, ht\ttps://...). So each string is also read with them deleted, as written and as decoded. Every rule checks every reading, and each new reading is added to the old ones, so it can only refuse more (see Scope). The example test runs 15 escaped Markdown and HTML shapes and 4 broken hrefs. Each was confirmed by markdown-it-py 4.2, or by HTMLParser and bun 1.3. A new property escapes 1-24 characters of a line link of this repository in 6 Markdown and HTML settings (300 examples). Of 200,000 random escapings rendered by markdown-it, 169,582 render to a line link this guard refuses, and all of those are refused as written

The review's note on M80 is answered too. The timing test now runs a failed range with 32,768 spaces after its dash ("lines L1-" + " " * 32_768 + "2x in x.py", and the same with to). M80 takes 30 s on each, against 0.01 s for the helper. The round-8 mutant run found two more survivors, M24 and M81. An explicit worded reverse range (lines 12 to 19 of x.py) now kills M24, and "x.py line" + " " * 65_536 + "1x" kills M81 (13.5 s, against 0.02 s). The review also noted that the second reading refuses a data-source URL whose query quotes a locator-like value (data.json?where=CLASS='L12'). Scope now states that shape.

Round 9: GPT-6.1 Sol (job 20261027-085429-microcosm-1407-review-r9, against a0d20514) confirmed round 8's two findings and the variants it had saved are fixed. 717,075 reverse scans agreed with finditer, every rule's output was unchanged over all 4,482,233 live strings, and it hand-applied M91, M93 and M100, each killed. Its run of the whole guard file stalled in dependency imports on this host, so it reported none. It returned REQUEST_CHANGES with one P2, fixed in fa7601cb and c36671e6:

# Finding Response
1 <a href="https://github.com/PolicyEngine/microcosm/blob/29983852…/tools\&#99;i_test_plan.py#L12"> yielded no citation of this repository. HTML decodes the reference and leaves the backslash, which the URL Standard reads as /, so the link is to tools/ci_test_plan.py. Markdown's one pass took \& as an escaped &, so no reading decoded the reference A string holding a & or a backslash is now decoded as Markdown decodes it and, apart, as HTML does, each once as text and once as a URL holds what it decodes to, and each is read joined too (see Invariants and Scope). References are now decoded as the HTML standard decodes them. A reference to a control is kept, where Python's html.unescape dropped it, so .&#1;. had read as ... The counterexample test runs the review's input and five variants (&#x63;, an unquoted href, leading zeros, a backslash before the path's first segment, and &Tab;), each confirmed by parse5 7.3 and bun 1.3. The markup property now writes slashes as \ in HTML with a reference after each, and an explicit example puts a decimal reference after a backslash in a double-quoted href

Looking for the same class, a link a browser reads whole that a reading split, found more, all fixed in round 10. Whitespace in a segment that a later .. removes ended the URL: a quoted attribute value and a destination in angle brackets hold any, and a plain destination and an unquoted value hold a Unicode space or one a reference decodes to. fa7601cb reads enclosed values whole, reads URLs through Unicode spaces, and holds what a reference decodes to inside a URL. A fuzz of that generation against markdown-it, parse5 and bun then found five more links: HTML decodes an attribute's references before the URL Standard strips the value's ends (href="&#32;https://…"), and markdown-it strips whitespace from a destination's ends ([a](< https://… >)). c36671e6 strips after decoding. A pinned relative path through an inner .. (tools/x/../ci_test_plan.py#L12 at <sha>) was not resolved, and now is. The counterexample test runs 30 inputs, each confirmed by the oracles, and three plain strings these readings must not refuse.

Round 10: GPT-6.1 Sol (job 20261027-111834-microcosm-1407-review-r10, against c36671e6, the delta from a0d20514) lost its lane before it finished (exit 125). Its draft report and evidence were rescued from its checkout. It confirmed round 9's input is refused. It replayed all 45 prior mixed-context variants and all 27,625 prior matrix inputs against fresh markdown-it, parse5 and bun runs: all 8,281 targets are refused, 17 inputs gained a refusal (the backstop's, on \&num;L12-style non-links) and none lost one. It timed 18 hostile families at 20,000 and 80,000 characters and found none superlinear, and its 396 pinned citations of other repositories gained no refusal against a0d20514. Its full guard run, live comparison and three mutants were still running when the lane was lost. It reported four P2s. Each is a line link of this repository that markdown-it-py 4.2 renders and parse5 7.3 and bun 1.3 resolve to tools/ci_test_plan.py, and in each a .. removes a segment that holds whitespace or a backslash. All four are fixed in 2822aff1:

# Finding Response
1 [a]( <https://github.com/…/tools/a b/../ci_test_plan.py#L12>) yielded no citation of this repository. CommonMark allows spaces, tabs and a line break between ( and <, and the enclosure needed ](< An angle destination is now found after spaces, tabs and one line break before its <, and rule 6 refuses the string as well. The counterexample test writes the gap as a space, a tab, a line break, and spaces and tabs around a line break, and puts a line break before a reference definition's <
2 Use `prefix="` before the value., a blank line, then <a href="https://github.com/…/tools/a b/../ci_test_plan.py#L12">: the code span's =" started an enclosure that ended at the href's opening quote, so no reading held the value Rule 6 (below)
3 [a](<https://github.com/…/tools/a\b/../ci_test_plan.py#L12>): Markdown keeps the backslash and writes it %5C, so the .. removes a%5Cb; a reading took the \ as / and removed b, citing tools/a/ci_test_plan.py. The plain destination is the same Rule 6
4 [a](&#32;//github.com/…/tools/a&#32;b/../ci_test_plan.py#L12): markdown-it decodes both references, strips the leading space and writes the inner one as %20 Rule 6

The review proposed a reading fix for each. Each would close one way a renderer and a reading remove different segments, and every finding needed a .. to remove a segment. So round 11 refuses the class (rule 6): every line citation in a string with a dot segment, in any source its readings start from. Without one, whitespace or a backslash that a reading takes otherwise stays in the segment the renderer follows, and a test checks that no source file here has either in its path. No live string holds a dot segment. The counterexample test runs the four inputs and seven variants, each confirmed by the three oracles. A property puts one of 8 spellings of a dot segment at 7 places beside a pinned or unpinned citation of another repository, and a plain-string test checks that a run of dots that is no whole segment refuses nothing. All 679 of the review's misses are refused now, and its matrix and its over-refusal probes without a dot segment are refused exactly as before (see "How this was verified").

Looking for the same class without a .. found one more. GitHub ignores a query, and whitespace in one ended the URL in each reading that no enclosure held (<a href="…/tools/ci_test_plan.py?a b#L12"> after round 10's code span), leaving #L12 beyond the backstop's window. Where a string holds a ? and a #, its URLs are now also read through the whitespace in their query (see Scope). The counterexample test runs 14 query shapes: at c36671e6 the 7 an enclosure held were refused, and the other 7 were not. A property writes a line link of this repository with spaces, tabs and Unicode spaces in its query in 8 enclosures.

Round 11: GPT-6.1 Sol (job 20261028-014437-microcosm-1407-review-r11, against 2822aff1) did not finish. Its first attempt found the model at capacity, and the provider stopped its second after about two hours with a content-filter refusal, so it returned no report. Its session log keeps its running draft: REQUEST_CHANGES with two P2s. Before it stopped, it had hand-applied M130, M138 and M144 (each killed), found no clear superlinear case in its first timing probes, and compared 116,755 live strings (am, be and nz) against c36671e6 with no differences. It also named a candidate it left unconfirmed: GitHub reads %2f as / before it resolves a path. All three are answered in 8a62c938:

# Finding Response
1 A ref holding punctuation at which a reading starts a name: in [a](<https://github.com/PolicyEngine/microcosm/blob/feature+foo/tools/ci_test_plan.py#L12>), pinned elsewhere in the string, a reading cites foo/tools/ci_test_plan.py, which names no file here, so no rule refused it. feature+foo is a valid ref name Rule 7 (below). The counterexample test writes 7 such refs (feature+foo, feature@foo, release!1, v1,2;3=4, it's, a$b, a=b) in Markdown and HTML
2 Whitespace in a URL's user where a code span's =" has taken the attribute's opening quote: Use `prefix="` before the value., a blank line, then <a href="https://a b@%67ithub.com/PolicyEngine/microcosm/blob/…/tools/ci_test_plan.py#L12">. Every reading's URL ends at the space, and the rest, read as prose, cites 67ithub.com/PolicyEngine/microcosm/blob/…/tools/ci_test_plan.py, which names no file here and is pinned by the sha in it Rule 7. The test writes 5 authorities this way (an escaped host, a full-width one, an ideographic full stop, a port, a tab), and the escaped host with a 30-letter query, and with an escaped owner, a doubled slash and a . segment
3 Unconfirmed: %2f beside .. Confirmed: tools/a b%2f..%2fci_test_plan.py#L12 renders to a link to line 12 of tools/ci_test_plan.py. Rule 6 now reads %2f as a separator, and rule 7 refuses the link too

In each finding a renderer follows a link to a line here and no reading does, as in round 10's. So round 12 refuses the class by what the link names rather than adding a reading fix for each: rule 7 needs only this repository's path and a line fragment after it. Its completeness argument and its limits (relative links, redirects, prose before a later fragment) are under rule 7 and Scope. The 22 inputs render as line links of this repository in all three oracles, no rule refused any at 2822aff1, and rule 7 refuses all 22. A test refuses 9 written forms by the path alone and passes 8 near misses, and a property draws refs, authorities, repository spellings, routes, queries and enclosures (see "How this was verified"). No live string is refused.

Looking further found one gap of rule 1's own. 18 tracked files hold an = in their path (docs/evidence/us-import-entry-margins/reconciliation/period=2025-01.json), and a reading starts a name after one. So docs/evidence/us-import-entry-margins/reconciliation/period=2025-01.json:3 at a1b2c3d cited 2025-01.json, which names no file here, and no rule refused it, and lines 3 of that path found no citation. The run of path characters before a cited name is now tried as a path here too, and a reverse locator reads a name after an =. A test covers the forward, microcosm/, reverse and URL forms, and a test that every source path here is read whole checks the premise against this tree.

Looking further after round 12 found a gap of rule 7's own. A dot segment can form this repository's path in a link to a file whose name no reading takes for a source: [a](https://github.com/PolicyEngine/x/../microcosm/blob/<sha>/LICENSE#L3) renders to a link to line 3 of LICENSE here, as do x/%2E%2e/, //x/../../, a%2fb/../, x\..\ and &#46;&#46; in its place, and the same links to the tree's Dockerfile and SHA256SUMS. No reading finds a citation, so rule 6, which refuses citations, never sees one, and rule 7 removed only . segments. Rule 7's completeness argument had counted on rule 6 for dot segments, which holds only where a reading finds a citation. Round 13 (4dc0734e) resolves dot segments in rule 7's own reading, as a browser does before GitHub reads the path. That reading covers every refusal of the one it replaces, which also refused links that a .. sends out of this repository. A test refuses 9 such links (round 12's among them) and passes 5 that link no line here (x/../../microcosm, x%2f..%2fmicrocosm, x/..%2fmicrocosm, microcosm-x/../microcosm-dynamics, x/.../microcosm), and the rule-7 property draws three repository paths through a .., two that leave it, the three files and a query of .. segments (see "How this was verified").

Round 12: GPT-6.1 Sol (job 20261028-061901-microcosm-1407-review-r12b, against 8a62c938) found the same gap independently and was cancelled about 25 minutes in, once the fix was pushed. Its draft verdict was REQUEST_CHANGES with one P2, which 4dc0734e answers:

# Finding Response
1 Rule 7 removed only . segments, so x/../ hid this repository's path, and its completeness argument left parent segments to rule 6, which needs a citation: Use `prefix="` before the value., a blank line, then <a href="https://a b@%67ithub.com/PolicyEngine/x/../microcosm/blob/2822aff1…/tools/ci_test_plan.py?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=1#L12"> rendered to a link to line 12 of tools/ci_test_plan.py, and no rule refused it Rule 7 resolves dot segments in its own reading (4dc0734e), and 5578c82d adds this input to the dot-segment test. No rule at 8a62c938 refuses it; rule 7 refuses it now

Before it stopped, it had also replayed the 22 round-11 inputs (none refused before, all now), found the new properties non-vacuous, and found that no foreign URL holding one of the 18 = paths is refused by rule 1. Its partial evidence is kept with the review records.

A fuzz of rule 7 against the renderers then found 3 links it missed at 4dc0734e, each a Markdown destination holding a backslash (see "How this was verified"). markdown-it percent-encodes a backslash (%5C), so in [a](https://github.com/PolicyEngine/x\y/../microcosm/blob/<sha>/LICENSE#L3) the .. removes the segment x%5Cy and the link is to line 3 of LICENSE here, while a browser reading the same URL in an href takes the backslash as /. Rule 7 read every backslash as /. a738da7d reads it both ways, and the dot-segment test adds four Markdown shapes (inline, pointy, reference definition, autolink), none refused by any rule at 4dc0734e. e5e14e96 adds to the rule-7 property a path through x\y/../ and one through x\..\..\ that leaves this repository.

Round 13: GPT-6.1 Sol (job 20261028-071421-microcosm-1407-review-r13, against 5578c82d) found the Markdown backslash independently and was cancelled about 45 minutes in, once the fix was pushed. Its draft verdict was REQUEST_CHANGES with one P2, which a738da7d answers:

# Finding Response
1 Rule 7 read every backslash as /, but markdown-it percent-encodes one in a destination (%5C), which neither a browser nor GitHub reads as a separator: [a](https://github.com/PolicyEngine/x\y/../microcosm/blob/5578c82d…/LICENSE#L12) rendered to a link to line 12 of LICENSE, as did its pointy and reference-definition forms, and no rule refused it a738da7d reads the backslash both ways, and the dot-segment test adds an inline link, a pointy destination, a reference definition and an autolink holding one, none refused by any rule at 4dc0734e. The review also noted that rule 7's property drew no path through a backslash; e5e14e96 adds one that links here and one that leaves

Before it stopped, it had also reviewed all 20 changed paths (each of the 92 changed JSON and YAML leaves is a string, and the four changed production modules' syntax trees are unchanged once string constants are normalized), replayed the 3,960-input matrix (its result equals the author's, and at 8a62c938 1,242 of the links passed every rule), rendered a 51,200-input grid of its own with no other miss, reproduced the property space's counts and GitHub's routing, and timed rule 7 on 18 long-input families at n and 4n, with nothing superlinear. Its partial evidence is kept with the review records.

Round 14: GPT-6.1 Sol (job 20261028-075749-microcosm-1407-review-r14, against e5e14e96, the whole branch) approved, with no findings. It replayed round 12's and round 13's inputs and the four Markdown shapes, each refused (round 13's family is missed at 4dc0734e). It rendered 30,440 inputs of its own, through literal, entity and escaped backslashes, whitespace and controls, Unicode, query and fragment boundaries, distant reference definitions and Markdown and HTML contexts: 653 render to a line link here, and rule 7 refuses all 653. It reproduced the property's 12,800-input space, including the 1,200 refusals that render no line link here. It confirmed that GitHub routes PolicyEngine/x%5Cy/../microcosm and PolicyEngine/..%5Cx/../microcosm to the blob (200) and the encoded-backslash alternatives nowhere (404). It ran the guard tests (137 passed, 31 of 31 properties at their limits), compared every rule over all 200 resources and 4,482,233 strings against 8a62c938 (no difference; none of the 99 strings naming this repository is refused), and hand-applied three mutants, the backslash read only as /, only as kept, and decoded before dot segments are resolved: each was killed. Its timing found no sustained superlinear trend: split-heavy _browsed samples took 5.38-5.43 times as long at four times the length, and _links_a_line_here 3.67-4.14 times. Its report and evidence are kept with the review records.

New Zealand

The NZ files stay as #1282 left them. This guard scans the NZ package too and finds nothing under any of the seven rules: every NZ line citation is covered by a full-path path/commit pin. The NZ hub agreed to keep NZ in this guard's scope and not to fold #1282's grammar into the shared helper: that grammar accepts any file extension and would over-match US and UK prose. The guard's docstring says to ask the NZ hub before exempting any NZ resource that starts failing it.

axiom: n/a: spec citation text, test-only validators and re-pins; no policy or rule change.

🤖 Generated with Claude Code

MaxGhenis and others added 23 commits October 26, 2026 14:30
…ting line

Rebuild #1146 on current main without its NZ files; #1282 landed the NZ
half with its own stricter grammar.

Add the shared validator (test_support/microcosm_build/spec_source_citations.py)
and an engine-free guard over every country package. It refuses a line
citation of a file in this repository, pinned or not, and a line citation of
any other source unless its string names the commit or the resource pins the
file in a path/commit mapping. Every dotted microcosm.<shard> name must
resolve.

US: port #1146's known-gap and source-stage rewrites that still apply (main
dropped the employer-premium gap), and name archived commit 42ed5d45 in the
ESI stage note added since. UK: the parity register's 13 citations name its
incumbent commit a43e8c5, each re-read there (hmrc_cgt 195 -> 196, hmrc_spi
230 -> 229, frs_only.py -> services.py 137-148); the population contract's
citations name registry ref 12a1e028 (30 ported strings, 9 rewritten since
#1146 re-read there; compute_esa is at income.py 98-102 at that ref).

Re-pin the source_stages.json raw hash and regenerate us/spec/sources.yaml.
The digests derived from it follow in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The source-stage citation edits move the US spec_sha256 (c677d50b ->
ca232744) and documentation_sha256 (801a99da -> 3cb60930), and three
inventory digests: source_manifest (87b1e124 -> ecf9ca2b),
late_resource_semantics (531b48cd -> e8797144) and full_checkpoint
(030c8422 -> 189d4e28). Regenerated us-f0-coverage.json with
tools/spec_engine_coverage.py: 41/41 inventory checks, and exactly ten
leaves move (the five above, their observed copies and the pool-code
observed digest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bind each line citation to the nearest inline commit in its own clause,
else the nearest one before it, and drop that fallback when the clause
names a moving ref. Refuse commit-like tokens in no pin spelling, widen
the locator grammar with a backstop for unrecognised spellings, strip
this-repository prefixes and count only anchored path tails, make a path
pin cover only citations whose components end its path, and stop reading
truncated hashes as pins.

Name the archived commit before the US PUF note's first citations and give
the ESI origin its own commit, then re-pin every digest built on
us/source_stages.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The misspelled-pin property placed its "between" token after the clause
break, inside the citation's own clause, so dropping the gap rule left it
passing. Add a placement in the binding commit's clause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Accept quotes before a colon or # locator's number, so x.py:"12-19" is a
citation. Count raw.githubusercontent.com URLs at a branch, refs/heads/
branches and quoted branch names as moving refs, so an earlier commit no
longer pins them. Stop reading a prose pin (commit, at, @) whose sha runs on
into a path: in "at a1b2c3d/foo.py:12" the hex is a directory. Add a
property for each, plus the review's exact inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The in-repository rule removed a GitHub URL's ref as one path segment, so
.../microcosm/blob/refs/heads/main/tools/x.py#L12 (and any slash-named
branch) left heads/main/tools/x.py unresolved and passed when pinned. Try
the path after each segment instead, and add a property over refs of one
to four segments plus the round-3 review's exact input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 4 of the PR 1407 review found two valid GitHub URLs that escaped
the guard: `%74ools/x.py` lost the repository prefix during extraction,
and a `?plain=1&review=...` query longer than the backstop's window hid
the `#L12` locator. Each URL in a scanned string is now read the way
GitHub reads it: scheme and host lower-cased, a user or port dropped,
`%` escapes of unreserved characters and `/` decoded, dot segments
resolved, and the query dropped. GitHub reads `%23` and `%3F` as part of
a file name, so those stay escaped.

The resolver also tried every split point of a URL's ref, so an
8,000-segment ref took 115 s and 1.09 GB. It now tries only the paths no
longer than the deepest path form in the checkout.

The new reading changes 69 live strings (queries on data-source URLs),
none of which carries a citation: classification is unchanged across all
200 resources and 283 citations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new URL property could escape the `2` of a `%2E` detour, writing
`%%32E`, which names a different path from the one it expects refused.
The mutant harness's unmutated baseline caught it (raw URL, ref 000000a,
detour `x/%2E%2e/`). Characters already inside an escape now stay as
written; the helper is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 5's review found that a URL of this repository with an escaped host
(https://%67ithub.com/...) or a network-path reference with a port or a user
(//github.com:443/..., //user@github.com/.../%74ools/...) escaped the
in-repository rule. urlsplit kept the host's escapes, and the URL pattern
read a scheme-less GitHub URL only when a slash followed its host.

The reader now parses the authority as the URL Standard does. Before the
query, \ is /. Any number of slashes may follow https: or start a
network-path reference. The user (up to the last @) and the port are
dropped. The host is percent-decoded, mapped to ASCII as UTS46 maps it (NFKC
case folding with Python's Unicode 16, plus Unicode 17's U+A7F1) and
lower-cased. A trailing dot is dropped, since GitHub redirects github.com. to
github.com. A URL the standard refuses is read as written. A scheme-less
GitHub URL may now carry a user and a port.

Checked against bun's WHATWG URL with every code point escaped into a host:
the two read the same wherever the standard maps it into plain ASCII. Of
26,350 generated URLs, none is read only by the standard and none is read
differently. The live packages' 4,482,233 strings read exactly as before.

Tests:
- the review's three inputs and nine more spellings, plus four authorities
  the standard refuses;
- a table of 20 readings recorded from the standard;
- the URL property now writes 12 leads, 7 authorities, full-width and
  escaped host characters, trailing dots and backslash separators;
- a deterministic example of a clause break inside a citation (M27).

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

Review round 6 found that a URL of this repository with a quote in its
query (`.../tools/ci_test_plan.py?value='xxxx'#L12`) escaped the
in-repository rule: the URL token ended at the quote, so its `#L12` was
read as prose. A browser reads a quote or a bracket inside a URL as part
of it (bun's URL), and GitHub serves each such URL. A URL now runs to
whitespace and does not end in a quote, a bracket or sentence punctuation.

The guard's scans are now linear in the string:
- a GitHub host written without a scheme is matched where it starts, with
  any user before it left as prose, instead of scanning for a user from
  every character of a run;
- a source-name pattern is tried only where a match can start (the start
  of a run of path characters, `http(s):`, or the previous match's end),
  which finds the same matches as `finditer`;
- each string's clause breaks, inline commits and commit-like tokens are
  found once, and each citation's clause, nearest commits and tokens are
  found by bisection; the moving-ref check is cached per clause;
- the backstop checks whether a near match ends inside a citation by
  bisection.

Tests: the reviewer's input with each quote and bracket in a query or a
dot segment; the URL property writes quotes and brackets in queries and
dot detours, and its full-width, escape and backslash indices always
land; a timing test of eight hostile strings (0.16 s; the round-6 helper
takes over 90 s on five of them); a differential of the run-start scan
against `finditer`, and of the bindings, misspelled pins and backstop
against the whole-string scans they replace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mutant that stops reading U+3002 as "." in a host survived round 7:
no test wrote a host with an ideographic full stop. The URL property now
writes a dot at a full-width index as any of the full-width, ideographic
and half-width ideographic full stops, the readings table has a raw and an
escaped ideographic stop, and the trailing-dot test drops each stop. bun
1.3's URL reads all three as "." in a host.

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

A URL's query no longer hides a Markdown link or an autolink written after
it: a string is also read with each URL ending at a quote, a parenthesis or
an angle bracket, as Markdown and prose delimit one, and every rule applies
to both readings. Line-number lists are read with possessive whitespace and
an unambiguous separator, a reverse locator is tried once per list rather
than once per number, and a host label longer than DNS allows is not
Punycode-encoded, so no locator scan backtracks exponentially or
quadratically. The patterns as they were are kept in the test file as the
reference a differential property checks the new ones against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ead Markdown and HTML escapes and URL line breaks

Round 8's review found two citations of this repository that no rule saw.

`L1-L2-3 of tools/ci_test_plan.py`: a reverse locator was tried only from
a list's head. It read `L1-L2`, failed, and the scan moved past the list,
but from `L2` the locator reads `L2-3` and succeeds. When a reverse
locator fails from a list's head, `_finditer` now tries it again from the
list's last number, if that is an `L` number, and only then moves past
the list. From any other number it reads the rest of the list as the head
did. Each number is read in at most two lists, so the scan stays linear.

`[a](https://github.com/PolicyEngine/microcosm/blob/<sha>/tools/ci_test_plan\.py#L12)`:
CommonMark decodes `\.` in a link destination, and character references
likewise (`&#35;L12`, `&#46;py`). A string holding a backslash before
ASCII punctuation or a character reference is now read again with them
decoded, in one pass. The URL Standard also deletes tabs and line breaks
from a URL, so each string is also read with them deleted, as written and
as decoded. Every rule checks every reading, so a new reading can only
refuse more.

Tests: the review's inputs, three more chains, 15 escaped Markdown and
HTML shapes and 4 broken hrefs; a property that escapes a line link of
this repository in six Markdown and HTML settings; chain pieces and
examples in both differentials; and timing strings for the round-8
mutant survivors M80 and M81 and for the new code. Three tests that
asserted the exact citation list now filter to the first reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ugh the whitespace a link holds

Round 9's review found an href this guard did not read as a browser does:
`<a href=".../tools\&#99;i_test_plan.py#L12">`. HTML decodes the
reference and leaves the backslash, which the URL Standard reads as `/`;
Markdown's one pass took `\&` as an escaped `&`, so no reading decoded
the reference. A string holding a `&` or a backslash is now decoded as
Markdown decodes it and, apart, as HTML does, each once as text and once
as a URL holds what it decodes to (whitespace percent-encoded, a tab or
line break deleted). References are decoded as the HTML standard decodes
them: a reference to a control is the control (Python's `html.unescape`
drops it, so `.&#1;.` read as `..`), one to a C1 control is read as
windows-1252 reads it, one to 0 or a surrogate is U+FFFD, and in a URL a
legacy name without `;` before a letter, a digit or `=` stays as written.

Beyond the review: whitespace in a segment that a later `..` removes.
A quoted attribute value and a Markdown destination in angle brackets
hold any whitespace, so each that starts a URL is now read as the
browser takes it, its ends stripped, its tabs and line breaks deleted and
its spaces percent-encoded. A Unicode space such as U+00A0 ends no URL in
the URL Standard, so where a string holds one its URLs are also read
through it. And an in-repository path's inner `..` segments are now
resolved (`tools/x/../ci_test_plan.py#L12 at <sha>` is refused); a path
that climbs out above where it starts names nothing here.

Every rule checks every reading, so a new reading can only refuse more.
Over all 4,482,233 live strings, every rule's output is unchanged and no
string is read a new way.

Tests: 25 counterexamples, each confirmed by parse5 7.3, markdown-it-py
4.2 and bun 1.3's URL; exact readings for the new decoders; inner and
leading `..` paths; three plain strings the new readings must not
refuse; the markup property now also inserts a whitespace detour before
a `..` and turns slashes into backslashes in HTML; and timing strings for
each new pattern.

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

A fuzz of round 10's generation against markdown-it-py 4.2, parse5 7.3
and bun 1.3 found five links no reading refused. HTML decodes an
attribute's references before the URL Standard strips its C0 controls
and spaces, so `href="&#32;https://..."` and `href="&#12;https://..."`
are links; the enclosed reading held the decoded space as `%20` first,
and the value no longer started a URL. markdown-it strips whitespace
from a destination's ends before following it (`str.strip`, in mdurl),
so `[a](< https://... >)`, a destination padded with U+3000 or U+00A0,
and `[a](<&#32;https://...>)` are links too; the angle reading did not
strip at all. Each enclosed value is now decoded without holding, then
stripped as its renderer strips it, then held.

Over 200,000 generated links, 113,469 render to a line link of this
repository and each is refused as written. The five shapes are in the
counterexample test. The round-10 timing strings get 2 s of their own:
all grow linearly, but under load they pushed the shared budget over.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s query's whitespace

Round 10's review found four links of this repository that no rule
refused, each confirmed with markdown-it-py 4.2, parse5 7.3 and bun 1.3:
an angle destination after the space CommonMark allows before its `<`,
an attribute whose opening quote a code span's `="` had consumed, a
backslash that Markdown writes as `%5C` and a reading took as `/`, and
an ordinary destination padded with `&#32;`. In each, a `..` removes a
segment that holds whitespace or a backslash, and the renderer removes
another segment than a reading does.

Every line citation in a string that holds a dot segment is now refused,
pinned or not (`dot_segment_line_citations`): `..` as a whole segment
beside `/` or `\`, either dot perhaps `%2e`, in any source a reading
starts from. Without one, whitespace or a backslash stays in the segment
a renderer follows, and a test checks that no source file here has
either in its path. No live string holds a dot segment.

GitHub ignores a query, so whitespace in one ended the URL wherever no
enclosure held it, and the line link was refused only where its `#L`
fell within the backstop's 24 characters. Where a string holds a `?` and
a `#`, its URLs are now also read through their query's whitespace,
ending the query at an angle bracket, a `"` or a `'`. An angle
destination is also found after spaces, tabs and one line break.

The counterexample test runs the four findings and their variants (11
inputs) and 14 query shapes, each rendered by the three oracles. Two
properties cover the rule and the query reading, and a test checks the
premise against this tree's file names. The review's 679 misses are now
refused, its 27,625-input matrix is unchanged, and the 288 over-refusal
probes without a dot segment gain no refusal. Each mutant that only
random generation killed at `c36671e6`'s tests (M12 survived round 10's
run that way) now has an explicit example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…egments and paths that hold =

Round 11's review found two shapes of link to a line of this repository
that no rule refused. markdown-it-py 4.2, parse5 7.3 and bun 1.3 render
each as a link to line 12 of `tools/ci_test_plan.py` on github.com. In
the first, the ref holds punctuation at which a reading starts a name
(`blob/feature+foo/...`, `release!1`, `v1,2;3=4`, `a=b`), so the
citation a reading finds (`foo/tools/ci_test_plan.py`) names no file
here. In the second, a code span's `="` has taken an attribute's opening
quote, and whitespace in the URL's user (`https://a b@%67ithub.com/...`)
ends every reading's URL before its host. The review also named `%2f`
beside `..`, which GitHub reads as a dot segment.

A string is now refused where any reading's source holds this
repository's GitHub path, `PolicyEngine/microcosm/`, with a line
fragment at the first `#` after it (`line_links_of_this_repository`),
whatever file the link names. The path is read as GitHub reads one:
escapes decoded, a backslash as `/`, a run of slashes as one, `.`
segments removed, and the owner and name in any case. A renderer's link
to a line here names that path. Between a source and its URL lie the
decodings the readings already apply, these normalizations,
percent-encoding of characters that are neither in the path nor a `#`,
and dot segments, which the dot-segment rule refuses. Relative links and
redirects are out of scope. Prose that holds such a path and fragment
without linking is refused too, by intent.

A `..` beside `%2f` is now a dot segment. A path here can hold an `=`
(`docs/evidence/margins/period=2025-01.json`), so the run of path
characters before a cited name is also tried as an in-repository path,
and a reverse locator reads a name after an `=` (`lines 3 of a/b=c.py`).

The counterexample test runs 22 inputs: all render as links to line 12
here, no rule at 2822aff refused any, and the new rule refuses all 22.
A property draws refs with punctuation, five spellings of github.com's
authority, five of this repository's path and eight enclosures, and
checks that a link here is refused and its twin in another repository
is not. Rendered by the oracles,
2,880 inputs from that space and 2,000 random draws agree: every input
here is refused, and none elsewhere. Across 4,482,233 live strings, the
earlier rules' results are unchanged, the new rule refuses none, and no
string holds a dot segment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A dot segment can form this repository's GitHub path in a link to a
file whose name no reading takes for a source:
`https://github.com/PolicyEngine/x/../microcosm/blob/<sha>/LICENSE#L3`.
markdown-it-py 4.2, parse5 7.3 and bun 1.3 render it as a link to line
3 of LICENSE here; so do `x/%2E%2e/`, `//x/../../`, `a%2fb/../`,
`x\..\` and `&#46;&#46;` in its place, and the same with this tree's
Dockerfile and SHA256SUMS. No reading finds a citation, so the
dot-segment rule never sees one, and the repository-link rule read the
path without resolving dot segments. Of 3,960 inputs (4 files x 22
detours x 5 enclosures x 3 authorities x 3 queries), 1,920 render as a
link to a line here, and at 8a62c93 1,242 of those passed every rule.

The repository-link rule now reads each source as a browser and then
GitHub read a URL's path: a backslash as `/` and the dot segments
before each `?` or `#` resolved, either dot perhaps `%2e` and an empty
segment counted; then escapes decoded and a run of slashes read as one.
This single reading replaces the earlier one, whose refusals it covers.
The earlier reading also refused links that a `..` sends out of this
repository (`PolicyEngine/microcosm/../other/...`); this one does not.
A browser reads `%2f` as no separator. GitHub decodes none in the owner
or the name (404 for `x%2f..%2fmicrocosm`, measured with curl
--path-as-is), so decoding one after resolving can only add a refusal.
GitHub also returns 404 for `%252e%252e` and `%5c` there, and 200 for
`x/%2e%2e/` and `x/.%2e/`, which a browser has already resolved.

All 1,920 here-links in the matrix are refused now, and no
other-repository detour is.
Rendered by the same oracles, the property's space (11,200 inputs,
expanded below) and 2,000 random draws agree: every input naming this
repository is refused, and none naming another.

Tests: `test_a_line_link_of_this_repository_is_refused_where_a_dot_segment_forms_its_path`
(8 inputs refused; 5 that link no line here, such as
`x/../../microcosm` and `x/.../microcosm`, not refused). The property draws three
more spellings of this repository's path through a dot segment, two
detours that leave it, the three files, and a query of `..` segments,
with two explicit examples. The timing test gains long runs of dot
segments, escaped dots, backslashes and segments after a `?`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The round-12 review (GPT-6.1 Sol, at 8a62c93) found the gap that
4dc0734 closes, with its own shape: a code span's `="` takes the
attribute's opening quote, so every reading's URL ends at the space in
`https://a b@%67ithub.com/PolicyEngine/x/../microcosm/blob/<sha>/tools/ci_test_plan.py?<30 letters>=1#L12`.
markdown-it-py 4.2, parse5 7.3 and bun 1.3 render it as a link to line
12 of `tools/ci_test_plan.py`. No rule at 8a62c93 refused it, and rule
7 refuses it at 4dc0734. The dot-segment test now holds it, as the
review asked.

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

A fuzz of rule 7 against the renderers found links it missed at
4dc0734, all Markdown destinations holding a backslash. markdown-it-py
4.2 percent-encodes a backslash (`%5C`), so in
`[a](https://github.com/PolicyEngine/x\y/../microcosm/blob/<sha>/LICENSE#L3)`
the `..` removes the segment `x%5Cy`, and the link is to line 3 of
LICENSE here; GitHub serves it. In an HTML `href` a browser reads the
same backslash as `/`, and the URL links `PolicyEngine/x/microcosm`.
Rule 7 read every backslash as `/`, so it missed the Markdown link. Of
40,000 generated inputs (detours through many spellings of `.`, `..`, `/`,
`\`, `?` and `#`, nine enclosures, six authorities), 5,590 render to a
line link here, and rule 7 at 4dc0734 missed 58, each a Markdown link
with a backslash.

Rule 7 now resolves each source's dot segments twice: with a backslash
read as `/`, as a browser reads it in HTML, and with it kept, as a
Markdown renderer escapes it. GitHub reads `%5C` as no separator
(`PolicyEngine/x%5c..%5cmicrocosm` returns 404). All 5,590 are refused
now, and the earlier fuzz's 385 too.

The round-13 review (GPT-6.1 Sol, at 5578c82) found the same class
independently, with the inline shape. The dot-segment test adds four
Markdown links: an inline link, a pointy destination, a reference
definition and an autolink, each refused by no rule at 4dc0734.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The round-13 review noted that rule 7's property drew no path through a
segment holding a backslash, the shape its finding used. The property
now draws `PolicyEngine/x\y/../microcosm`, which a Markdown renderer
escapes (`x%5Cy`, removed by the `..`) and a browser in an `href` reads
as `PolicyEngine/x/microcosm`; either way it is refused. Its twin,
`PolicyEngine/x\..\..\microcosm`, links no line here in Markdown or in
HTML and is not refused. An explicit example pairs the two in a
reference definition with a `Dockerfile`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 28, 2026 14:29
@MaxGhenis
MaxGhenis merged commit 7bf1ed8 into main Oct 28, 2026
10 checks passed
@MaxGhenis
MaxGhenis deleted the spec-source-citations-us-uk-v2 branch October 28, 2026 14:33
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