#!/usr/bin/env python3
"""verify-source-census.py — the OTHER generators' own censuses.

The ring reads itself (THE SOURCE'S CENSUS, closed 2026-08-15): every literal
the ring's generator carries is declared — derived, agreed, refused, the
faces, the naming — and a literal the census does not know fails the wake,
with the reverse gate (every declared number must be READ; a name for nothing
fails). The OTHER generators — the promise (build-the-promise.js), the ember
(build-ember-strip.js), the family line (build-family-line.py), the reverse
walk (build-reverse-walk.js) — never got the census: a wake could type a new
literal into their sources (a new sample x, a new offset, a new face) and
nothing would NAME it. The --check gates prove the drawing equals the
generator's output, but nothing proves the generator's own literals are
declared — the same ghost class the ring closed one surface further in.

This wall closes the class with the same instrument the ring uses: comments,
strings, and templates stripped (quote-aware, regex literals read whole — the
lexer's own rule, the shape the 00:27 wake closed in the ring) — and the
instrument's own ghost the audit found: a file whose first line is
#!/usr/bin/env node could not be read — the '!' after '#' sits in the
operator set, so the lexer read '/usr/bin/env node\n// …' as a regex and
swallowed the first comment block whole, and the digits INSIDE the comments
(19/19 in the promise's proof note, Q17/Q20 in the reverse walk's note)
walked into the reading as phantom literals: the census found numbers the
hand never typed, and the declared set could not name them. The shebang is
not JS — the lexer skips the first line when it starts with '#!'. The
reading is the same shape the ring's own census closed for the esc helper's
quote-marker: the instrument's rule, read.

The READERS' OWN RULE, one surface further in (closed 2026-08-25): a digit
inside a string, a comment, a template literal, or a regex literal is WORDS,
not a literal the hand typed into the code — the lexer strips them, and the
census reads only the code that remains. The class's own ghost, found by
building: a regex after a control-flow close paren — `if (x) /re/.test(y)` —
was read as DIVISION (the ')' is not in the operator set), the regex's digits
leaked into the reading as phantom literals (77/-9/0 from `/77[0-9]/`), and
an honest file failed the wake named by numbers the hand never typed. The
reader now walks back to the matching '(' and reads the keyword before it:
a control-flow head (if/while/for/switch/catch/with) means regex, an
expression head (foo(x) / 2) means division — the same rule the lexer
already reads for the keyword list, one surface further in. Proven both ways
by verify-source-census-both-ways.py (29/29): the boundary holds in every
generator, and the reverse gate holds per generator, not only for the ring's.

THE PROOF-OF-PROOF, one surface further in (closed 2026-08-25): the wall
now reads its own harnesses. The both-ways proofs — verify-source-census-
both-ways, verify-desc-readers-both-ways, verify-svg-xml-both-ways,
verify-img-reader-both-ways, verify-claim-patterns-both-ways, verify-
generated-both-ways — are hand-authored code like the sources were, and a
wake could type a new literal into a proof and nothing would NAME it: the
planted phantoms (777/999/888) live inside strings, and the same instrument
strips them — words, not literals — so each proof's own code-literals
(0/1 the exits, −1 the tail's last, the cases' own identifier digits) must
be declared like any source's. A digit typed into a proof the census does
not know fails the wake, named by its file. The proof of the proof is no
longer the hand's last place: the law reads the proofs the way it reads
the sources — and the naming (which proofs exist, what their numbers mean)
is still the hand's.

THE CENSUS'S OWN ROOM, one surface further in (closed 2026-08-25): the
wall now reads ITSELF. Every literal the census's own body carries — the
instrument's machinery (the walk's 0/1/2/3, the tail's −1) AND the very
numbers it declares for the other sources (the declared sets' keys live in
the census, so they are the census's own numbers too: 140, 48, 820, 10000,
the proofs' 401/−600/−800…) — must be named in its own room. A wake that
types a new number into the census (a new sample, a new guard, a new seat,
a new declared key for any source) fails the wake named by its own file.
The self-room's law is the forward gate only: a number unnamed is the lie
(found ⊄ declared). The reverse gate cannot hold here by construction —
writing a name writes the number, so a name for nothing is impossible in
the census's own room; the lie it can tell is the number it does not name.
The reading of the census is the census's own now; the naming is still
the hand's.

THE FINISH GATE'S OWN ROOM, one surface further in (closed 2026-08-25):
verify-descs.py — the wall that carries EVERY gate in-process (the
source-census included) — has its own code-literals read by no census.
The gate reads everything and is read by nothing: the same class, one
surface over from the census's own room. This wall closes it: the
finish gate is the TWELFTH target, its 8 literals (−1 the filename's last
segment, 0 the counters' start and the gates' fail, 1 the steps and the
failure exit, 2 the title's group, 3 the comment close's skip, 4 the
'<img' length, 5 the peek's end, 100 the broken-construct print window)
all declared, all read — a literal typed into the finish gate the census
does not know fails the wake, named by its own file. The gate that reads
everything is now read by the law; the naming is still the hand's.

THE READING OF THE READING, one surface further in (closed 2026-08-25):
the census's own READING is now read too. The both-ways proof once
asserted only the census's VERDICT (the exit code) — never its READING
(the counts it prints). A wake could silently drop a target from TARGETS
and the census would still exit 0 — one file fewer read, all still
honest — and no gate would know. verify-source-census-both-ways.py now
reads the reading: the honest estate must name TWELVE files read, a
dropped target narrows the reading and the law sees the lie, and a
digit-bearing word planted INSIDE the reader function (_strip_js's own
body) is words, not a literal — the reader's code path is read by the
same instrument it applies to the sources. The reading of the reading:
the reader is read; the naming is still the hand's.

THE READING OF THE READING OF THE READING, one surface further in
(closed 2026-08-25): the harness's own expectations are no longer
hand-typed strings. The law loads the census in-process, re-derives the
expected width from the census's OWN per-file listing, and asserts the
printed reading equals the computed reading line for line — a cosmetic
lie the exit code cannot see and the hand-typed strings still matched
(the loop narrowed, the summary not; the print narrowed, the compute
not) is now seen by the width re-derived from the census's own state.

THE NAMING'S OWN DERIVATION, one surface further in (closed 2026-08-25):
the estate's named width is no longer a hand-typed list in the harness.
The law derives the twelve names from the estate's own texts — the
census's own TARGETS (in-process), the anchors the estate's texts speak
(the finish gate's in-process import of the census, the finish script's
--check invocations and its verify-* wall), and the proofs' own glob — so
a wake that narrows an anchored name from TARGETS leaves the estate's own
text naming it, and the narrowing of the naming is SEEN (39/40 in the
both-ways proof). The residue — the files named by no estate text (41:
build-family-line.py dropped, the derivation follows the census,
invisible by construction) — is the voice: the claim's meaning cannot be
derived, only named.

For each generator: the source is read, stripped with the same instrument,
every literal enumerated; each found literal must be DECLARED (the census
names it: the placement's, the law's own, the voice's own); the reverse gate
holds — every declared number must be READ, a name for nothing fails the
wake. A literal the census does not know fails the build, named by its file.

Usage:
  python3 verify-source-census.py          # check the four other generators
  python3 verify-source-census.py --cards  # print full per-file declarations

Exit 1 on any undeclared literal or any unread declaration. Wired into
verify-descs.py so the finish-script gate carries it without a new file in
the wake's tail.

Deterministic: same files, same bytes, same verdict.
"""

import argparse
import re
import sys
from pathlib import Path

TOOLS = Path(__file__).resolve().parent

# ---------------------------------------------------------------- the lexer
# THE INSTRUMENT, SHARED: comments, strings, templates, and regex literals
# stripped — the same reading the ring's census uses, plus the shebang's own
# line skipped (the instrument's own ghost, closed this wake).

def _strip_js(src: str) -> str:
    if src.startswith("#!"):
        nl = src.find("\n")
        if nl >= 0:
            src = src[nl + 1 :]
    out: list[str] = []
    i, n = 0, len(src)
    in_block = False

    def regex_start(code: str) -> bool:
        t = code.rstrip()
        if not t:
            return True
        last = t[-1]
        if last in "([{:;,=!&|?+":
            return True
        if last == ")":
            # a control-flow close paren: `if (x) /re/.test(y)` — the '/'
            # after a control-flow condition is a regex, not division. The
            # heuristic's own blind spot, closed 2026-08-25: `if (EDGE > 1)
            # /77[0-9]/.test(String(EDGE));` once leaked 77/-9/0 into the
            # census as phantom literals — the condition's close paren read
            # as division, the regex's digits read as code, an honest file
            # failed the wake named by numbers the hand never typed. Walk
            # back to the matching '(' and read the keyword before it: a
            # control-flow head means regex, an expression head (foo(x) / 2)
            # means division — the same rule the lexer already reads for the
            # keyword list, one surface further in.
            depth = 0
            j = len(t) - 1
            while j >= 0:
                c = t[j]
                if c == ")":
                    depth += 1
                elif c == "(":
                    depth -= 1
                    if depth == 0:
                        break
                j -= 1
            head = t[:j].rstrip() if j >= 0 else ""
            return bool(re.search(
                r"(?:^|\s)(?:if|while|for|switch|catch|with)\s*$", head
            ))
        return bool(re.search(
            r"(?:^|\s)(?:return|typeof|instanceof|in|of|new|delete|void|case|do|else)\s*$",
            t,
        ))

    while i < n:
        c = src[i]
        if in_block:
            if c == "*" and i + 1 < n and src[i + 1] == "/":
                in_block = False
                i += 2
            else:
                i += 1
            continue
        if c == "/" and i + 1 < n and src[i + 1] == "/":
            while i < n and src[i] != "\n":
                i += 1
            continue
        if c == "/" and i + 1 < n and src[i + 1] == "*":
            in_block = True
            i += 2
            continue
        if c == "/" and regex_start("".join(out)):
            i += 1
            in_class = False
            while i < n:
                if src[i] == "\\":
                    i += 2
                    continue
                if src[i] == "[":
                    in_class = True
                elif src[i] == "]":
                    in_class = False
                elif src[i] == "/" and not in_class:
                    i += 1
                    break
                i += 1
            while i < n and re.match(r"[a-z]", src[i], re.I):
                i += 1
            continue
        if c in ('"', "'"):
            q = c
            i += 1
            while i < n and src[i] != q:
                if src[i] == "\\":
                    i += 1
                i += 1
            i += 1
            continue
        if c == "`":
            i += 1
            while i < n:
                if src[i] == "\\":
                    i += 2
                    continue
                if src[i] == "`":
                    i += 1
                    break
                i += 1
            continue
        out.append(c)
        i += 1
    return "".join(out)


def _strip_py(src: str) -> str:
    out: list[str] = []
    i, n = 0, len(src)
    while i < n:
        c = src[i]
        if c == "#":
            while i < n and src[i] != "\n":
                i += 1
            continue
        if c in ('"', "'"):
            triple = c * 3
            if src.startswith(triple, i):
                i += 3
                while i < n and not src.startswith(triple, i):
                    i += 1
                i += 3
                continue
            q = c
            i += 1
            while i < n and src[i] != q:
                if src[i] == "\\":
                    i += 1
                i += 1
            i += 1
            continue
        out.append(c)
        i += 1
    return "".join(out)


def _literals(code: str) -> set[float]:
    return {float(m) for m in re.findall(r"-?\d+(?:\.\d+)?", code)}


# ------------------------------------------------------- the four generators
# Each target: (filename, language, DECLARED set with the census's reasons).
# The DECLARED set is the hand's naming — every literal the generator carries
# is named here (the placement's, the law's own, the voice's own); a literal
# the census does not know, or a name nothing reads, fails the wake.

TARGETS = [
    (
        "build-the-promise.js",
        "js",
        {
            # the placement's — the hand's named projection
            140: "X0 — the curve's origin x",
            48: "LH_X — one line-height in the drawing's x",
            130: "LAST_Y — the last line, the gold rule's y",
            360: "SPAN — light EDGE -> 0 mapped linearly across this span",
            40: "BOX.x — the register box's left",
            78: "BOX.y — the register box's top",
            820: "BOX.w — the register box's width",
            52: "BOX.h — the register box's height",
            # the samples — the hand's x marks where the curve is read
            144: "SAMPLES — the hand's x marks (the curve is read here)",
            148: "SAMPLES — the hand's x marks",
            152: "SAMPLES — the hand's x marks",
            156: "SAMPLES — the hand's x marks",
            160: "SAMPLES — the hand's x marks",
            164: "SAMPLES — the hand's x marks",
            168: "SAMPLES — the hand's x marks",
            172: "SAMPLES — the hand's x marks",
            176: "SAMPLES — the hand's x marks",
            180: "SAMPLES — the hand's x marks",
            184: "SAMPLES — the void's x snapped to the grid (VOID_X = round(140 + 48·0.91629))",
            188: "SAMPLES — the hand's x marks",
            196: "SAMPLES — the hand's x marks",
            204: "SAMPLES — the hand's x marks",
            212: "SAMPLES — the hand's x marks",
            220: "SAMPLES — the hand's x marks",
            228: "SAMPLES — the hand's x marks",
            236: "SAMPLES — the hand's x marks",
            252: "SAMPLES — the hand's x marks",
            268: "SAMPLES — the hand's x marks",
            284: "SAMPLES — the hand's x marks",
            300: "SAMPLES — the hand's x marks",
            332: "SAMPLES — the hand's x marks",
            380: "SAMPLES — the hand's x marks",
            460: "SAMPLES — the hand's x marks",
            620: "SAMPLES — the hand's x marks",
            780: "SAMPLES — the hand's x marks",
            # the voice's own — typographic placement
            7.5: "VOID_LABEL_DY — the void label's rise (the hand's register)",
            7.1: "E1_LABEL_DY — the e^−1 label's rise (the hand's register)",
            # the law's own arithmetic
            10000: "round4's scale — the 4-decimal rounding (the law's own arithmetic)",
            4: "round4's own digit — the 4 in the identifier (the same identifier-digit the ring's census counts)",
            1: "the first e-fold (E1_X = X0 + 1·LH_X; curveYAtN(1)) — the law's own",
            0: "the success exit — process.exit(0) (the check's own pass)",
        },
    ),
    (
        "build-ember-strip.js",
        "js",
        {
            # the placement's — the promise twin's own room
            140: "X0 — the unit bar's origin x (the promise's own room)",
            48: "LH_X — one line-height in the drawing's x",
            130: "LAST_Y — the last line, the gold rule's y",
            40: "BOX.x — the register box's left",
            78: "BOX.y — the register box's top",
            820: "BOX.w — the register box's width",
            52: "BOX.h — the register box's height",
            # the law's own
            11: "N_STOPS — the 11 gradient stops (the law's own sample count)",
            100: "the percent rod — offsets 0..100 (the law's own)",
            1: "the e-fold's one (1·LH_X, (1 − EMBER_N), N_STOPS − 1) — the law's own",
            0: "the first stop's index and the success exit (stops[0]; process.exit(0))",
            3: "the three-decimal face — toFixed(3) (the law's own precision)",
        },
    ),
    (
        "build-family-line.py",
        "py",
        {
            # the model's own — the number-word table
            1: "_NUM — one",
            2: "_NUM — two",
            3: "_NUM — three",
            4: "_NUM — four",
            5: "_NUM — five",
            # the placement's — the drawing's geometry
            1280: "W — the canvas width",
            480: "H — the canvas height",
            160: "TOP_CY — the top marks' center y",
            330: "BOT_CY — the bottom marks' center y",
            190: "MW — the mark width",
            62: "MH — the mark height",
            240: "top_xs — Name It First's x (and the triad's word)",
            720: "top_xs — the checkbox trap's x",
            960: "top_xs — schema and practice's x (and the triad's enforcement)",
            600: "triad_x — schema and practice's seat at the triad's middle",
            # the voice's own — the wrap and the seats
            26: "the wall's title wrap threshold (len(name) > 26) and the single label's seat (BOT_CY + 26)",
            31: "the marks' rect offset (TOP_Y = TOP_CY − 31, BOT_Y = BOT_CY − 31)",
            0: "the first dual's index (duals[0]) and the search's start (range(mid, 0, …))",
            -1: "the wrap search's step (range(mid, 0, −1))",
        },
    ),
    (
        "build-reverse-walk.js",
        "js",
        {
            # the placement's — the drawing's geometry
            1280: "W — the canvas width",
            480: "H — the canvas height",
            200: "X_MARK — the marks' x",
            268: "X_KIND — the kinds' x",
            96: "Y_START — the line's top",
            43: "Y_STEP — the line's step",
            1120: "RAIL_X — the dark rail's x",
            # the law's own guards
            0: "the empty-desc guard (DESC.length === 0)",
            1: "the failure exit (process.exit(1))",
        },
    ),
    # ---- the proofs, one surface further in (closed 2026-08-25): the wall
    # now reads its own harnesses — the both-ways proofs themselves. A digit
    # typed into a proof the census does not know fails the wake; the planted
    # phantoms (777/999/888, the comment ghosts) live inside strings, and the
    # same instrument strips them — words, not literals. The proof of the
    # proof is no longer the hand's last place: the law reads the proofs the
    # way it reads the sources.
    (
        "verify-source-census-both-ways.py",
        "py",
        {
            0: "the honest exit — the gate's pass (expect 0), the counters' start (passed=0, failed=0), the harness's own success (return 0)",
            1: "the failing exit — the gate's fail (expect 1), the counters' step (passed += 1, failed += 1), the harness's own failure (return 1)",
            -1: "the tail's own last — the diagnostic's last line (splitlines()[-1])",
        },
    ),
    (
        "verify-desc-readers-both-ways.py",
        "py",
        {
            0: "the honest exit — the gate's pass (expect 0), the counters' start, the harness's own success (return 0)",
            1: "the failing exit — the gate's fail (expect 1), the counters' step, the harness's own failure (return 1)",
            -1: "the tail's own last — the diagnostic's last line (splitlines()[-1])",
            401: "the gauge's reading — the 401 text node the harness removes (the identifier real_401's own digit — the number the drawing carries; a claim-word that lives only in a comment is not a drawing word)",
        },
    ),
    (
        "verify-svg-xml-both-ways.py",
        "py",
        {
            0: "the honest estate's zero — failed == 0, the harness's own success (exit 0)",
            1: "the failing gate's one — f2 >= 1 (a corruption must fail, named by file), the harness's own failure (exit 1)",
            2: "the second case's seat — f2/l2/site2, the identifiers' own digit (the planted mismatched tag)",
            3: "the third case's seat — f3/l3/site3, the identifiers' own digit (the planted tag-like word in a desc)",
            4: "the fourth case's seat — f4/l4, the identifiers' own digit (the empty surface — the wall is never idle)",
            5: "the fifth case's seat — f5/l5, the identifiers' own digit (the planted tag-like word inside a comment — the parse reads the comments)",
            6: "the sixth case's seat — f6/l6, the identifiers' own digit (the planted unclosed comment — a comment that never ends is a ghost)",
        },
    ),
    (
        "verify-img-reader-both-ways.py",
        "py",
        {
            0: "the honest exit — the wall's pass (returncode == 0), the harness's own success (exit 0)",
            1: "the failing exit — the wall's fail (returncode == 1), the harness's own failure (exit 1)",
            2: "the second case's seat — site2, the identifier's own digit (the copied estate)",
            3: "the third case's seat — r3, the identifier's own digit (the planted '>' in alt, value != desc)",
            4: "the fourth case's seat — r4, the identifier's own digit (the planted '>' in a later attribute)",
            5: "the fifth case's seat — r5, the identifier's own digit (the planted dropped-alt tag)",
            6: "the sixth case's seat — sample6/got6, the identifiers' own digit (the img-shaped word inside a comment)",
            7: "the seventh case's seat — r7, the identifier's own digit (the planted img-shaped word in a comment on a page)",
            8: "the eighth case's seat — r8, the identifier's own digit (the planted real img inside a comment)",
            -600: "the short tail — the honest failure's output read from its last 600 chars (r1.stdout[-600:])",
            -800: "the long tail — the hostile failures' output read from their last 800 chars (r3/r4/r5/r7/r8.stdout[-800:])",
        },
    ),
    (
        "verify-claim-patterns-both-ways.py",
        "py",
        {
            0: "the honest exit — the gate's pass (expect 0), the counters' start, the harness's own success (return 0)",
            1: "the failing exit — the gate's fail (expect 1), the counters' step, the harness's own failure (return 1)",
            -1: "the tail's own last — the diagnostic's last line (splitlines()[-1])",
            4: "the four thin connectors — the count the harness asserts (planted == 4, the checkbox trap's thin lines)",
            7: "arc 7's own digit — the identifier arc7 (the arc whose opacity the harness strips, and the r=7 ring circle beside it)",
        },
    ),
    (
        "verify-generated-both-ways.py",
        "py",
        {
            0: "the honest exit — the gate's pass (expect 0), the counters' start, the harness's own success (return 0)",
            1: "the failing exit — the gate's fail (expect 1), the counters' step, the harness's own failure (return 1)",
            -1: "the tail's own last — the diagnostic's last line (splitlines()[-1])",
        },
    ),
    # ---- the census's own room, one surface further in (closed 2026-08-25):
    # the wall now reads ITSELF. The declared sets above ARE this file's own
    # numbers — every key the census types to name another source is a literal
    # in the census's own body — plus the instrument's machinery (the walk's
    # steps, the gates' exits). Every one of them must be named here, or the
    # census fails on its own file: a wake that types a new number into the
    # census (a new sample, a new guard, a new seat, a new declared key for
    # any source) is UNDECLARED in its own room. The self-room's law is the
    # forward gate only: a number unnamed is the lie; the reverse gate cannot
    # hold here by construction — writing a name writes the number, so a name
    # for nothing is impossible in the census's own room.
    (
        "verify-source-census.py",
        "py",
        {
            # the instrument's own machinery — the walk's steps, the gates' exits
            0: "the walk's zero and the gates' pass — i, n = 0 the scan's start; depth = 0 the paren-walk's start; nl >= 0 the shebang's read-back; failed = 0 the count's start; the success exit (return 0, process.exit(0))",
            1: "the walk's step and the gates' fail — i += 1 the scan's step; nl + 1 the shebang's skip; depth += 1 / depth -= 1 the paren-walk's depth; the failure exit (return 1, process.exit(1))",
            2: "the block-comment's skip — i += 2 past /* (and the escape's skip i += 2 in the string readers)",
            3: "the triple-quote's skip — triple = c * 3; i += 3 past the opening/closing triple quote",
            -1: "the tail's own last — t[-1] the previous char read; j = len(t) - 1 the paren-walk's start; j >= 0 the walk-back's guard",
            # the numbers the census types to name the other sources — the
            # declared sets' own keys, now named for the census itself
            4: "declared for the promise — round4's own digit (the 4 in the identifier); declared for the family line — _NUM four; declared for the claim-patterns proof — the four thin connectors (planted == 4)",
            5: "declared for the family line — _NUM five",
            6: "declared for the svg-xml proof — the sixth case's seat (f6/l6, the planted unclosed comment)",
            7: "declared for the claim-patterns proof — arc 7's own digit (arc7, the arc whose opacity the harness strips)",
            7.1: "declared for the promise — E1_LABEL_DY, the e^−1 label's rise (the hand's register)",
            7.5: "declared for the promise — VOID_LABEL_DY, the void label's rise (the hand's register)",
            8: "declared for the img-reader proof — the eighth case's seat (r8, the planted real img inside a comment)",
            11: "declared for the ember — N_STOPS, the 11 gradient stops (the law's own sample count)",
            26: "declared for the family line — the wall's title wrap threshold (len(name) > 26) and the single label's seat (BOT_CY + 26)",
            31: "declared for the family line — the marks' rect offset (TOP_Y = TOP_CY − 31, BOT_Y = BOT_CY − 31)",
            40: "declared for the promise and the ember — BOX.x, the register box's left",
            43: "declared for the reverse walk — Y_STEP, the line's step",
            48: "declared for the promise and the ember — LH_X, one line-height in the drawing's x",
            52: "declared for the promise and the ember — BOX.h, the register box's height",
            62: "declared for the family line — MH, the mark height",
            78: "declared for the promise and the ember — BOX.y, the register box's top",
            96: "declared for the reverse walk — Y_START, the line's top",
            100: "declared for the ember — the percent rod, offsets 0..100 (the law's own)",
            130: "declared for the promise and the ember — LAST_Y, the last line, the gold rule's y",
            140: "declared for the promise and the ember — X0, the curve's origin x",
            144: "declared for the promise — SAMPLES, the hand's x marks",
            148: "declared for the promise — SAMPLES, the hand's x marks",
            152: "declared for the promise — SAMPLES, the hand's x marks",
            156: "declared for the promise — SAMPLES, the hand's x marks",
            160: "declared for the promise — SAMPLES, the hand's x marks",
            164: "declared for the promise — SAMPLES, the hand's x marks",
            168: "declared for the promise — SAMPLES, the hand's x marks",
            172: "declared for the promise — SAMPLES, the hand's x marks",
            176: "declared for the promise — SAMPLES, the hand's x marks",
            180: "declared for the promise — SAMPLES, the hand's x marks",
            184: "declared for the promise — SAMPLES, the void's x snapped to the grid (VOID_X = round(140 + 48·0.91629))",
            188: "declared for the promise — SAMPLES, the hand's x marks",
            190: "declared for the family line — MW, the mark width",
            196: "declared for the promise — SAMPLES, the hand's x marks",
            200: "declared for the reverse walk — X_MARK, the marks' x",
            204: "declared for the promise — SAMPLES, the hand's x marks",
            212: "declared for the promise — SAMPLES, the hand's x marks",
            220: "declared for the promise — SAMPLES, the hand's x marks",
            228: "declared for the promise — SAMPLES, the hand's x marks",
            236: "declared for the promise — SAMPLES, the hand's x marks",
            240: "declared for the family line — top_xs, Name It First's x (and the triad's word)",
            252: "declared for the promise — SAMPLES, the hand's x marks",
            268: "declared for the promise — SAMPLES, the hand's x marks",
            284: "declared for the promise — SAMPLES, the hand's x marks",
            300: "declared for the promise — SAMPLES, the hand's x marks",
            330: "declared for the family line — BOT_CY, the bottom marks' center y",
            332: "declared for the promise — SAMPLES, the hand's x marks",
            360: "declared for the promise and the ember — SPAN, light EDGE -> 0 mapped linearly across this span",
            380: "declared for the promise — SAMPLES, the hand's x marks",
            401: "declared for the desc-readers proof — the gauge's reading (the identifier real_401's own digit)",
            460: "declared for the promise — SAMPLES, the hand's x marks",
            480: "declared for the family line and the reverse walk — H, the canvas height",
            600: "declared for the family line — triad_x, schema and practice's seat at the triad's middle",
            620: "declared for the promise — SAMPLES, the hand's x marks",
            720: "declared for the family line — top_xs, the checkbox trap's x",
            780: "declared for the promise — SAMPLES, the hand's x marks",
            820: "declared for the promise and the ember — BOX.w, the register box's width",
            960: "declared for the family line — top_xs, schema and practice's x (and the triad's enforcement)",
            1120: "declared for the reverse walk — RAIL_X, the dark rail's x",
            1280: "declared for the family line and the reverse walk — W, the canvas width",
            10000: "declared for the promise — round4's scale, the 4-decimal rounding (the law's own arithmetic)",
            -600: "declared for the img-reader proof — the short tail, the honest failure's output read from its last 600 chars (r1.stdout[-600:])",
            -800: "declared for the img-reader proof — the long tail, the hostile failures' output read from their last 800 chars (r3/r4/r5/r7/r8.stdout[-800:])",
        },
    ),
    # ---- the finish gate's own room, one surface further in (closed
    # 2026-08-25): the wall that carries EVERY gate in-process — the finish
    # gate, verify-descs.py — is itself read by the law. The gate reads
    # everything and is read by nothing; the census now closes the class:
    # a literal typed into the finish gate the census does not know fails
    # the wake, named by its own file.
    (
        "verify-descs.py",
        "py",
        {
            # the instrument's own room — the finish gate's own code-literals
            -1: "the filename's own last — rsplit('/', 1)[-1], the sitemap loc's final segment",
            0: "the counters' start and the gates' fail — i = 0 the scan's start; start < 0 / end < 0 the find's own guards; speaking = 0, essay_imgs = 0, essay_ok = 0, _img_tags = 0, _img_tags_read = 0, art_caps_ok = 0 the counters' start; returncode != 0 the subprocess gates' fail",
            1: "the steps and the failure exit — j + 1 the tag's close step; i = j + 1 the scan's advance; m.group(1) the loc's group; e[1] the title's seat; sum(1 …) the counter's step; essay_imgs += 1, essay_ok += 1, speaking += 1, _img_tags += 1, _img_tags_read += 1, art_caps_ok += 1 the counters' step; sys.exit(1) the wall's fail",
            2: "the title's group — m.group(2), the sitemap title and the img alt (the second group's own seat)",
            3: "the comment close's skip — i = end + 3, past the comment's own '-->' (the three-char close)",
            4: "the '<img' length — text[start + 4 : start + 5], the peek after the tag's own word (and i = start + 4, j = start + 4 the scan's advance past it)",
            5: "the peek's end — text[start + 4 : start + 5], one char after the tag word (the boundary's own width)",
            100: "the broken-construct print window — _b.strip()[:100], the diagnostic's own width",
        },
    ),
]


def check_all() -> tuple[int, list[str]]:
    failed = 0
    lines: list[str] = []
    for name, lang, declared in TARGETS:
        src = (TOOLS / name).read_text(encoding="utf-8")
        code = _strip_js(src) if lang == "js" else _strip_py(src)
        found = _literals(code)
        lines.append(f"== {name} ({len(found)} literals)")
        # the forward gate: every found literal must be DECLARED
        for lit in sorted(found):
            if lit not in declared:
                failed += 1
                lines.append(
                    f"  UNDECLARED {lit}: the source carries a literal the census does not know "
                    f"— the hand typed a number the law was not told about; declare it in "
                    f"verify-source-census.py or restore the hand's number"
                )
        # the reverse gate: every declared number must be READ (declared ⊆ found)
        for lit in sorted(declared):
            if lit not in found:
                failed += 1
                lines.append(
                    f"  UNREAD {lit}: a name for nothing — the census declares {declared[lit]}, "
                    f"but the machinery never reads it (declared ⊄ found)"
                )
        for lit in sorted(declared):
            if lit in found:
                lines.append(f"  {lit}: {declared[lit]}")
    lines.append(
        f"source-census: {len(TARGETS)} files read (the four generators + the six proofs + the census itself + the finish gate), {failed} failure(s)"
    )
    return failed, lines


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--cards", action="store_true", help="print full per-file declarations")
    a = ap.parse_args()
    failed, lines = check_all()
    print("verify-source-census: the OTHER generators read themselves")
    for l in lines:
        print(l)
    if failed:
        print(f"verify-source-census: {failed} failure(s)")
        return 1
    print(
        "OK: every literal the promise, the ember, the family line, the reverse walk, the six "
        "both-ways proofs, the census's own body, AND the finish gate carry is declared; every "
        "declared number is read — a literal the census does not know, or a name nothing reads, "
        "fails the wake. The wall reads its own proofs now, the census reads its own room, the "
        "gate that reads everything is read by the law, AND the reading of the reading is the "
        "harness's own now — the reader's code path is read by the same instrument it applies "
        "to the sources — AND the reading of the reading of the reading: the harness's "
        "expectations derive from the census's own computed state, the width is re-derived "
        "from its own listing, and the printed reading equals the computed reading, line for "
        "line: the proof of the proof is no longer the hand's last place."
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())
