Skip to content

Check and suggest the axiom line on country-model PRs; plan the pe-parity drain - #9

Open
MaxGhenis wants to merge 24 commits into
mainfrom
axiom-parity-check
Open

MaxGhenis wants to merge 24 commits into
mainfrom
axiom-parity-check

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

This makes the PolicyEngine→Axiom parity rule (CONTRIBUTING, "Mirror policy changes in Axiom") checked and assisted rather than manual, for every country model.

What's in it

1. A reusable PR check (.github/workflows/axiom-parity.yml, package axiom-parity/). Country repos call it on pull_request.

  • It applies only when a PR changes <package>/parameters/, variables/ or reforms/. Markdown under those paths doesn't count.
  • The check resolves each claim on the axiom: line against GitHub:
    • queued: the issue exists in a TheAxiomFoundation/rulespec-* repo, carries pe-parity, stands open (or closed as completed), and passes a dispatch-ready lint over its body and comments. The lint needs six things: module path, corpus citation (not a corpus release file), verbatim law, required outputs, a pasteable review_finding, and companion tests.
    • encoded: a rulespec PR, open or merged. It warns when the PR carries no encoder manifest, a JSON file under .axiom/encoding-manifests/.
    • encoded-correct: every module named exists on rulespec main, and so does any #rule cited. For each module, a cited companion case or file exists and outputs a rule of that module.
    • n/a: an allowed category (below), as the PR's only claim. For metadata and docs-tests, no changed policy file may change a parameter value or formula. The check reads every file, compares parameter YAML with descriptive keys removed, and compares Python by AST with docstrings and class-level labels removed.
    • needed: never passes.
  • A PR can make several claims (;, |, several lines, or a bullet list after a colon), and every claim must pass.
  • A line claims a status only in the canonical shape: right after the reference or citation it applies to, or opening its clause, with nothing before it that denies or conditions it. "rulespec-us#1416 is not [yet] queued" and "If rulespec-us#9 encoded, rulespec-us#1416 queued" claim nothing, whatever their punctuation.
  • It verifies the claims a PR makes. It can't tell that every changed provision has a claim; the finder comment lists every referenced citation so a reviewer can compare.
  • mode: warn reports in the job summary and annotations without failing. mode: enforce fails the job.

2. A parity finder (axiom-parity suggest). It runs in the same workflow and keeps one comment on the PR while the check fails.

  • It reads the reference URLs on the changed parameters and variables and maps them to corpus citation paths. The mapping covers:
    • legislation.gov.uk and gov.uk;
    • the US Code via Cornell, uscode.house.gov and govinfo;
    • the CFR via eCFR, Cornell and govinfo;
    • CA, MD and OH codes and 14 more state code sites, IRS revenue procedures, plus a section-number match for about 50 other state hosts.
  • It looks the citations up in a shallow, sparse clone of the rulespec repo and in the open pe-parity issues.
  • It suggests encoded-correct candidates together with the companion cases that output each module.
  • When a citation's source document already has an open issue, it suggests extending that issue. When nothing covers a document, it drafts one issue for it. A draft's TODOs fail the lint until a person fills them in.

3. A drain design (axiom-parity/docs/drain.md), with two tools:

  • corpus-pin lists the citations the encoder could read in the corpus release a rulespec clone pins: rows with an operative body. It lists rows marked repealed apart, because the encoder refuses them. It reads axiom-corpus with git, using the encoder owner's procedure.
  • drain-plan extracts each issue's target citations and review_finding (every finding block, body first, then comments), files each target under its source document, and prices a wave from measured run costs.
    • A batch is one document, so no run mixes jurisdictions, which the encoder refuses.
    • The plan treats a module a finding names as requested work unless the words around it mark an import, a deferral, a precedent or a description. When the issue gives no citation for requested work, the issue is held.
    • It applies the encoder owner's lists as data: blocked modules (declared, named in a finding, or owning the target citation on rulespec main), held citations and its dispatch-preparation verdicts, source patterns, and assembled pipelines, where the change an issue asks for is a hand edit that a signed run does not make.

CONTRIBUTING now lists the n/a categories and the issue headings, and names the check.

Allowed n/a categories (please check this list)

The rule names infra, data, ui, microsim-only and emulator-mapping. Three more cover policy-path PRs that change no provision of law:

  • docs-tests: documentation and tests;
  • metadata: labels, references and units;
  • not-law: contributed reforms, proposals, and announcements not yet legislated.

The replay below shows these three accounted for most n/a PRs. The check rejects a reason saying "Axiom doesn't encode this yet" unless the line gives an explicit category, because queued covers that case. Three US PRs used that reason: policyengine-us#9667, #9656 and #9588.

Evidence

Replay over every PR merged since 2026-09-26 (scripts/replay_history.py, diffing each merge commit locally):

Repo Merged Change policy files Would pass Main failures
policyengine-uk 98 81 49 no line 13, line with no claim 13, needed 3, issue not dispatch-ready 2, n/a category 1
policyengine-us 203 173 81 no line 44, issue not dispatch-ready 20, line with no claim 17, n/a category 4
  • I read each failure class and fixed every parser false positive the replay surfaced:
    • lines inside code fences;
    • a bullet list continuing a line;
    • dict(title=, href=) references;
    • 0000-01-01 parameter dates;
    • references moved under a dated value's metadata;
    • prose lines that start with "Axiom:".
  • The grammar reads no claim in prose it doesn't recognise, so 12 UK and 7 US pull requests that passed under the looser parser now fail with "no claim found". The message tells the author to start the declaration with its reference or status, and shows the accepted form, axiom: rulespec-uk#370 queued (SI 2014/1230 reg 6A(4)).
  • Rulespec lookups use main today, so a few old claims pass only because a module landed later.

Finder on the three open UK PRs the earlier replay session checked by hand (#2240–#2242). It reproduces those findings:

  • Rulespec encodes FA 2004 s.190: uk:statutes/ukpga/2004/12/190, with cases earnings_above_basic_amount and earnings_below_basic_amount.
  • ss.188, 189, 227, 227ZA, 228A and 233 have no module. The finder drafts one issue for FA 2004.
  • FA 2003 Sch 4ZA has no module, so it drafts an SDLT issue.
  • rulespec-uk#367 queues UC Act 2025 s.1 and passes the dispatch-ready lint.

The check passes on #2240's merge ref: it verifies s.190 against its module and both cases, and rulespec-uk#444 passes the dispatch-ready lint.

Backlog (axiom-parity backlog):

  • rulespec-uk: 79 of 83 open pe-parity issues are dispatch-ready.
  • rulespec-us: 84 of 175.
    • 85 US issues lack a pasteable review_finding; the 71 filed on 2026-10-09 include 60 of them.
    • 21 name no module.
  • By the plan's own checks (corpus pin, the encoder owner's blocked modules and citations, manual paths paused), 16 source documents pass: 17 citations, 16 signed runs, completing 11 issues. Without the citation holds and the manual-path pause it is 28 documents and 31 citations, completing 17 issues.
  • Passing the plan doesn't clear a document for dispatch. The encoder owner prepared all 16 documents for dispatch on 2026-10-10:
    • the owner dispatched 4 that day (Colorado 39-22-2003 and three New Mexico sections), and they wait on the signing approval;
    • 5 wait on an encoder fix (axiom-encode#1819);
    • 3 rely on a source that isn't in the pinned corpus;
    • the encoder can't run on the 4 UK documents: rulespec-uk has no working signed route. The signed workflow reads .axiom/workflow-toolchain.toml, which rulespec-uk lacks, and rulespec-uk's layout test allows no encoding manifest. The encoder owner has queued that route question for Max.
  • drain-plan reads those verdicts back as holds. With them and the UK hold applied, it lists no document that is ready and not already dispatched.
  • Some lint-ready issues are held for the work they ask for: 10 ask for a change to an assembled pipeline, and 13 ask for work on a module and give no citation for it (2 fall in both groups).
  • Two earlier versions of this description overcounted ready documents: 25 (31 issues, 45 provisions), then 16 completing 15 issues. Review rounds 3 and 4 caught both.

Encoder costs (axiom-parity/docs/drain.md), from the 453 targeted-signed-reencode runs between 2026-09-01 and 10-10:

  • The 121 runs since 2026-09-25 (GPT-6 Luna, escalating to Sol) cost $7.08 in model spend; 11 succeeded. Mean $0.058 a run, p90 $0.150, most $0.496.
  • Four citations from the 453 runs have a manifest on rulespec-us main. GPT-6 Sol encoded one of them on 2026-10-01.

Invariants (property-tested with Hypothesis)

  • The parser never raises, is deterministic, and finds no lines in text without an axiom: prefix.
  • Rendered claims, legal ids and rulespec refs round-trip through the parser.
  • Lint: for every subset of sections removed, the missing elements equal that subset; found and missing partition the six elements; a draft the finder writes is never dispatch-ready.
  • Behaviour: descriptive edits never count as a change; value edits and changed formula locals always do; a file compared with itself never changes.
  • Check: a policy PR passes if and only if it has at least one claim and every claim is good; adding a good claim never breaks a passing PR; the check is deterministic.
  • Differential: warn and enforce block on the same findings, and warn shows no errors.
  • Differential: anything the drain can paste as a review_finding, the lint counts.
  • Differential: the URL mapper never contradicts 427 URL-to-citation pairs that an independent pass checked against the corpus.
  • Drain: a batch is ready if and only if every issue is lint-ready and every target is in the pin.
  • Drain: every batch holds one source document and one jurisdiction; each target sits in exactly one batch; a batch needs its citations over 16, rounded up, runs.
  • Parser: an aside never cancels a negation ("is not (…) queued", "is not, …, queued" claim nothing).
  • Lint: an input is never an expected outcome, however many the issue lists, and a named input is never a required output, whatever its type, entity or period.
  • Parser: emphasis never turns a denial into a claim.
  • Parser: a condition or denial before a reference, in a continuation lead or in a bullet never makes a claim, whatever the order of reference and status (tests/test_round7_claims.py).
  • Drain: a module named in a root-first pairing, a brace group or a removal stays requested work in either rulespec layout, so the owner's holds reach it. The brace group can sit in the jurisdiction, and the removal can coordinate several objects or carry asides. An import or deferral clause after a removal keeps its own reading.
  • Parser: a line that puts a legal citation ahead of its reference and status makes no claim, and its problem shows the accepted form (tests/test_round8_diagnostics.py).
  • Lint: a name that its declaring sentence designates an input never becomes a required output, whatever whitespace or Markdown sits inside its name, type, entity and period (tests/test_round9_outputs.py).
  • Drain: a removal governs every module after it until a new predicate starts a clause, however the finding joins the modules, and a module a removal takes something from counts as requested work whatever the removed object (tests/test_round9_removals.py).
  • Drain: the bracket scan matches brackets in one pass, so 16,000 unmatched openers finish in under a second (tests/test_round9_scan.py).
  • Check: only a JSON file under .axiom/encoding-manifests/ counts as an encoder manifest (tests/test_round9_manifests.py).
  • Neighbourhood grids (tests/test_neighbourhoods.py): the tests try each free-text rule over every combination of the markers, wrappers and wordings near the ones it covers. Examples are 14 negations × 12 asides × 49 emphasis pairs for a denied status, and 8 list markers × 6 separators for a module-and-citation line.

.github/workflows/axiom-parity-tool.yml runs all 394 tests. actionlint is clean.

Review

Independent GPT-6.1 Sol reviews, each with reproductions:

  • Round 1 (505822f) requested changes: 4 P1 and 9 P2 findings.
  • Round 2 (b94b1bc) requested changes: 2 P1 and 10 P2 findings, plus 5 false positives in a 30-PR sample.
  • Round 3 (9f04a68) requested changes: 6 P1 findings, 8 P2 and 1 P3. It reproduced the replay, lint, pin and cost figures, and found the five earlier false positives fixed.
  • Round 4 (f858521) requested changes: 5 P1 findings and 6 P2. It reproduced every figure and found no misjudged PR in a fresh 20-PR sample.
  • Round 5 (d787a40) requested changes: 8 P1 findings and 1 P2. It reproduced every figure and found no misjudged PR in a fresh 20-PR sample.
  • Round 6 (6269fb0) requested changes: 8 P1 findings and 2 P2, most of them again in how the parser read status words.
  • Round 7 (d09c652) requested changes: 5 P1 code findings, one stale figure in this description, 1 P2 and 1 P3. It re-ran all ten round 6 reproductions and found them fixed.
  • The parser doesn't guess at negations. A status counts only in the canonical shape: right after its reference or citation (only separators, closing marks and a few words like "is" between), or opening its clause, with no denying or conditioning word before it. A property test checks that for any words between a reference and a status, the line makes a claim exactly when the grammar allows every one.
  • 2aaad0d7f fixes every code finding from the seven rounds, each with a regression test.
  • Round 8 (2aaad0d) requested changes: 3 P1 findings and 1 P2. It re-ran the round 7 reproductions and found them fixed, reproduced the replay, lint and drain figures, and judged the 19 newly failing declarations safe-side rejections whose message should show the accepted form.
  • 38a4381 fixes all four, each with a regression test.
  • Round 9 (38a4381) requested changes: 3 P1 findings, 1 P2 and 1 P3. It found the round 8 reproductions fixed, reproduced all 301 replay verdicts, and rebuilt the drain plan byte for byte.
  • 686c90b fixes all five. Each class now has its invariant in the code and a Hypothesis property over the inputs around it, and each fault's property fails on 38a4381.
  • Round 10 reviews 686c90b.

Rollout

  1. Merge this.
  2. Merge the caller PRs, which use mode: warn: Run the Axiom parity check on pull requests (warn-only) policyengine-uk#2249, Run the Axiom parity check on pull requests (warn-only) policyengine-us#10048, Run the Axiom parity check on pull requests (warn-only) policyengine-canada#558, Run the Axiom parity check on pull requests (warn-only) policyengine-il#12 and Run the Axiom parity check on pull requests (warn-only) policyengine-ng#28. They also update or add the PR templates' axiom line. The uk and us callers point at this branch, so their runs exercised the workflow end to end; both ran green.
  3. After about two weeks of warn-only runs, switch them to mode: enforce.
  4. Then make "Axiom parity" a required check in branch protection. That is a settings change for a repo admin.

The rule doesn't list policyengine-nz. Its hub can opt in with the same five-line caller.

axiom: n/a: infra: CI tooling for the parity rule; no country-model policy changes

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 10, 2026 07:14
A reusable workflow (axiom-parity.yml) checks the axiom: line on every PR
that changes parameters, variables or reforms: queued issues must exist,
carry pe-parity and be dispatch-ready; encoded PRs must exist and be open
or merged; encoded-correct must name a module on rulespec main and a
companion test that outputs it; n/a needs an allowed category, and
metadata/docs-tests claims are checked against the diff.

The finder maps changed files' reference URLs to corpus citations, looks
up RuleSpec modules and open pe-parity issues, and drafts a dispatch-ready
issue per source document when nothing covers a provision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
drain-plan groups dispatch-ready pe-parity issues by source document,
extracts each issue's target citations and pasteable review_finding, and
prices a weekly wave from measured encoder run costs. corpus-pin lists the
citations with an operative body in the release a rulespec clone pins,
read from axiom-corpus with git. docs/drain.md is the drain design.

Calibration against every policy PR merged since 2026-09-26 fixed: fenced
axiom lines, bullet continuations, n/a notes on queued lines, dict()
references, year-0000 parameter dates, and moved or misspelled references
inside dated values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 16 commits October 10, 2026 07:39
…t's open issue

A citation with no module and no issue of its own, in a source document that
already has an open pe-parity issue, now suggests extending that issue
instead of drafting a duplicate (seen on policyengine-us#10040, where
rulespec-us#1626 covers 20 CFR 416.1202 and the PR also cites 416.1160).

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

The PR and the tool are checked out into separate directories, so files in a
PR can't stand in for the tool. In warn mode a tooling failure is a warning,
not a failed job; in enforce mode it exits 2, distinct from a rule failure.
The finder writes nothing for PRs that change no policy files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A differential property test found the drain could extract a YAML-form
review_finding (review_finding: |-) that the lint missed. The lint now uses
the drain's extractor as a fallback, so anything the drain would paste counts
as present, and a batch is ready only if every issue has pasteable text.
The extractor also reads a cue after a bold step label.

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

Check
- A formula local named label/reference is code, not metadata; only class
  attributes are descriptive. Inert bare dict() expressions and date-keyed
  bracket values are normalized.
- Every changed policy file is checked for an n/a metadata claim (no cap).
- encoded-correct needs a companion test for every module it names, and an
  unresolved relative module (/22) blocks.
- n/a is a claim wherever it sits, so it can't share a line with others.
- Modal and future status words (might be queued, to be queued) are prose.

Lint and drain
- An element with a TODO is missing, companion tests need expected values,
  and a prose back-reference isn't the element: finder drafts never pass.
- One extractor decides the review_finding for both the lint and the drain.
- Every citation a finding names is a target; a batch needs pasteable text.
- corpus-pin skips repealed rows and reads rulespec-uk's real corpus pin.

Finder
- Issues are read with their comments (one GraphQL query per 50 issues).
- Dated-value references are read; dashes are normalized; the comment is
  only updated when a bot wrote it.
- URL mapping gains legislation.gov.uk /id/ and nested schedules, IRS rev
  procs, eCFR part URLs, govinfo and 14 state code sites, and is tested
  against 427 corpus-checked URL vectors.

Workflow: pull_request only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A shell guard on the maintainers' machines blocks any command line that
contains find run from a broad directory, so axiom-parity find could not be
run by the agents who need it. The workflow and docs use suggest.

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

drain-plan --blocked-modules takes modules that must not be a target or an
import. Its first use is the 17 rulespec-us modules that carry the obsolete
module.source_verification.values field and the modules that reference
them: a target importing one fails to compile.

The scarce resource is the production-signing approval, and one signed run
can carry up to 16 citations (MAX_SOURCE_BUNDLE_CITATIONS), so the plan
counts runs per source document and the doc budgets approvals per week.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Check and parser
- Any module reference that resolves to nothing fails an encoded-correct
  claim, however it is written (corpus path, legal id, path or sibling).
- A companion named later on the same axiom line counts; a bare test file
  name is searched beside every cited module.
- Negation stops at its clause ("is not encoded but is queued"); a status
  used as an adjective ("the queued rule") is not a claim; n/a after
  "and" or a full stop is still a claim; a reference-less status joins the
  next only when the statuses agree.

Lint and drain
- Placeholders are matched in any case; a companion case needs a stated
  outcome, not a digit; negative judgments count.
- A review_finding cue must introduce the finding in its own sentence; a
  fence tagged review_finding is one. Outputs named in the finding count.
- Targets are the labelled citation plus everything the finding names.

Finder and behaviour
- Sibling issues are found from every open issue's own citations.
- Comments past 100 are read; the REST fallback reads comments too.
- dict(...) is inert only when the module leaves the builtin alone.
- Dashes are normalized in URLs, claims and cited test paths.

docs/drain.md states the taxonomy's populations exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The encoder owner asked to pause manual paths until axiom-encode#1821
merges, so drain-plan takes --blocked-citation-pattern (repeatable).
docs/drain.md records that Max approved the standing approval (d1262).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
drain-plan --blocked-citations reads {blocked: [{citation, match, kind,
reason}]}, where match is exact or exact-or-descendant, and blocks an issue
whose target matches. It complements the module list and the pattern block.

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

Lint (issue_lint.py)
- Input facts are not expected outcomes: an `input:` block and an
  "input x: 0" clause are dropped before outcomes are looked for.
- A required output named in a finding must be declared as an output and
  tied to the identifier. "Add an input named a_b_c" declares an input.
- A finding cue must hand the finding over now. Deferred, unavailable and
  empty cues hand over nothing, and an inline cue takes only the block
  right below it.
- review_findings() returns every finding in a text once. Braced paths
  (a/b/{1,2}.yaml) are expanded before modules and citations are read.

Axiom line (axiom_line.py, check.py)
- A negation left hanging before an aside governs the status after it:
  "is not (yet) queued", "is not, in fact, queued", "(not yet) queued".
- Bold n/a after a claim, and "the already queued rule", parse correctly.
- A companion test named right after a negation is not evidence.

Drain (drain.py, corpus_pin.py, cli.py)
- A batch is one source document, so no run mixes jurisdictions (the
  encoder refuses such a bundle). An issue spanning documents is in each.
- The blocked-module filter checks the declared module, modules a finding
  uses, the target's path and the modules above it, and, with
  --rulespec-dir, the modules on main that own the target citation.
- Findings in comments add to the body's. Modules a finding asks for work
  on become targets; imports, deferrals and precedents do not.
- A pin file marks repealed rows; a repealed target is not in the pin.

Finder (finder/cli.py, finder/propose.py)
- The finder updates only a comment its own account wrote that opens with
  the marker. Issue text is dash-normalized before citations are matched.

Behaviour (behaviour.py)
- A star import may rebind dict, except the PolicyEngine model_api preamble.

docs/drain.md
- Four citations from the measured runs have a manifest on rulespec-us
  main, not two; one is a GPT-6 Sol encode. The "no fresh model encode has
  landed" claim is withdrawn.
- With every hold applied, 16 documents (20 citations, 16 runs) are ready,
  not 25.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 4 found new phrasings that the rules misread. Rather than add a rule
per phrasing, the rules now guess towards the safe side where they have to
guess, and the README says what they cannot tell.

Drain (drain.py)
- A module a finding names is requested work unless the words around it
  mark an import, a deferral, a precedent or a description. Each marker
  covers its own clause. A numbered list of modules under one "Encode
  these:" line is now read (rulespec-uk#429).
- A line that only pairs a module with a citation declares both
  (rulespec-us#1618).
- A requested module resolves to the citation the issue gives for it or
  for the provision above it, else to the one its path spells (federal US
  and UK). With neither, the issue is held and the plan names the module.
- A precedent or a described module is not at stake for the block list.
- Backticked sibling names, several brace groups and incomplete paths are
  handled. A pin row may hold spaces.

Lint (issue_lint.py)
- A list of input facts is not an outcome: only an announced result ends
  an input clause, and a bare "key: 0" or "x is 0" never counts. A result
  after a colon counts after a described case.
- A YAML case counts whatever the order of its keys; a comment inside a
  code block is not a heading.
- "Add an input rule a_b_c" declares no output.
- Headings and bold labels obey the same negation and deferral rules as
  inline cues. Back-to-back finding labels keep both findings.

Axiom line (axiom_line.py, check.py)
- Emphasis is stripped before parsing. A hanging negation skips its aside,
  and its reach ends at a hard break, another status word, or a reference
  after the aside.
- A test ruled out inside brackets is not evidence.

docs
- With every hold applied, 16 documents (17 citations, 16 runs) are ready,
  completing 11 issues, not 15. 13 lint-ready issues are held because a
  requested module has no citation.
- README: a Limits section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The encoder owner prepared the 12 US documents that passed the plan for
dispatch and found 2 dispatchable that day, 2 runnable after an issue
update, 5 waiting on axiom-encode#1819, 1 needing a ruling and 2 whose
finding relies on a form outside the pin.

- --blocked-citations is repeatable and reads the owner's verdict file
  ({documents: [{citation, verdict, reason, until}]}); every row holds
  its citation.
- docs/drain.md says plainly that passing the plan is necessary, not
  sufficient, with the verdict table. With the verdicts applied the plan
  lists 4 UK documents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The encoder owner keeps a list of assembled pipelines: modules composed
from other modules, where the change an issue asks for is a reviewed hand
edit, not an encode (axiom-encode#1723 for rulespec-us; an ordinary PR in
rulespec-uk).

- --work-holds reads {rule, modules: [{module, evidence}]}. An issue is
  held when a finding, or a line pairing a module with a citation, asks
  for work on a listed module. Importing from one is not held.
- The hold names the module and the owner's evidence, and the plan prints
  the owner's rule once. It replaces the missing-citation reason for those
  modules: naming a citation would not make them dispatchable.

On the backlog of 2026-10-10 it holds 9 lint-ready issues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 5 found phrasings one step from fixed ones. Each is fixed, and a
new test module builds phrasings from their parts so a rule is tried
against every combination of markers, wrappers and wordings near the ones
it was written for. Those tests found three more gaps before review did.

Axiom line
- Emphasis is removed completely, however nested: "is not ***queued***".
- A bullet that says anything about a status never inherits one.
- A negation can hang before an aside after an auxiliary ("cannot be
  (yet) queued") and before a dash aside ("is not — for now — queued").

Check
- A negation rules out every companion test in its reach: its own bracket
  group, nested groups included, up to a colon, semicolon, sentence end
  or status word.

Lint
- A name made an input by the words after it is not an output: "Add a
  rule a_b_c as a new input", "Add `x` to the inputs".
- A label's remainder is checked for deferral whatever its length, and a
  finding that starts on the label's line does not take a quote below it.
- A result table needs a filled-in result cell.

Drain
- A request in a module's own clause makes it work whatever describes it:
  "encode the deferred X", "add it to the guarantee that X composes",
  "encode X as a module". Description verbs are listed, not guessed.
- "1. module — citation" pairs are read like bulleted ones.
- programs/ modules are recognised; work on one with no citation is held.

docs
- Missing or repealed corpus text explains 8 failures, not 10.
- The backlog table and held-issue counts follow the new rules and the
  encoder owner's assembled-pipelines list.
- README Limits lists the evidence and finding-label rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s no signed route

The encoder owner prepared all 16 documents that pass the plan. Four were
dispatched (Colorado and three New Mexico sections). Five wait on
axiom-encode#1819, three rely on a source outside the pin, and the four
UK documents cannot be encoded: the signed workflow reads
.axiom/workflow-toolchain.toml, which rulespec-uk lacks, and rulespec-uk's
layout test allows no encoding manifest.

The document now carries that table and the UK finding, and the plan
command holds every uk citation until the route is decided.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Six rounds found phrasings one step from fixed ones, most of them in how a
status word is read. The parser no longer guesses at negations.

Axiom line
- A status is claimed only when it follows its reference or legal
  citation directly (separators, closing marks and is/are/now/already and
  a few such words may stand between), or opens its clause, and nothing
  before it in its piece denies or conditions it. "is not [yet] queued",
  "isn’t queued" and "Not in Axiom yet, rulespec-us#1416 queued" claim
  nothing, whatever their punctuation. Each status is judged in its own
  piece, so a denied claim keeps its references to itself.
- A bullet list inherits "The rest is queued:" only when nothing in the
  lead denies it.
- A status in prose with a reference after it is noted, not claimed.
- Curly apostrophes, Markdown links and comma citations ("43-1041(A),(H)")
  are read before a status.

Check
- Test references are read whole, so "file.test.yaml::case" doesn't end a
  negation's reach.

Lint
- An output named in a finding counts only in a closed set of declared
  shapes; a name followed by any input designation ("as a scoped input")
  is not one.
- A finding on its label's line is none when its first sentence defers
  it, however long.
- Cells under an input column are inputs. "(1)" and "(a)" are list
  markers.

Drain
- Root-first program paths, emphasis around paths, "repair the existing
  X" and "add the import to X" read as work on X.

Tests: grids over the words between a reference and its status, denials
before it, input designations, deferred label lengths, input columns,
module layouts, wrappers and modifiers, list numbering, repeated
references and Markdown links. 268 tests.

Replay: UK 60 of 81, US 87 of 173 policy PRs pass (was 58 and 87).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 6 commits November 6, 2026 06:39
…outs

A status now claims a reference only when the clause begins with the
reference or an explicit connector, and an unrecognised head keeps its
scope through later statuses on the line. "If rulespec-us#9 encoded,
rulespec-us#1416 queued" no longer claims #1416, and neither does a denied
bullet, "When ready" or "almost queued".

A bullet inherits a lead's status only through the same grammar as an
explicit claim. Reference-free continuation statuses now produce the
notice a job summary shows.

The issue lint reads a designation after a whole typed signature, so "Add
a_b_c Money (TaxUnit) as an input" supplies no output. The drain plan
pairs citations with root-first programs/<jurisdiction>/ modules, expands
their brace groups, and counts a removal from a module as work on it.

README documents the test-only handoff form that repeats existing outputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ls, grouped jurisdictions

The issue lint reads a finding's declaration through its Markdown, so
"Add `a_b_c` Money (`TaxUnit`) as an input" and "as a leaf **input**"
supply no output.

A removal request now governs every object it coordinates and the
asides between them. "Remove a rule and its imports from X" and "Remove
a rule, now obsolete, from X" count as work on X, so X keeps its owner
hold, and removing a deferred dependency from X does too. An import,
read, keep or defer predicate after the request still starts its own
clause.

The drain plan expands a brace group in the jurisdiction segment, so
"programs/{us-ar,us-ca}/tanf/fy-2026.yaml" names both modules, and it
keeps only expansions that start with a jurisdiction.

The check now tells an author who puts a legal citation before a status
to start the declaration with its reference or status, and shows the
form "axiom: rulespec-uk#370 queued (SI 2014/1230 reg 6A(4))".

Five more property tests drop Hypothesis's per-example deadline, as most
of the suite already does, because a loaded host pushed their examples
past 200 ms.

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

Lint normalizes whitespace and Markdown across the whole declaration before any pattern reads it, and a designation anywhere in the declaring sentence ("as an input", "(an input)") skips the name, whatever whitespace sits inside its type, entity and period annotations. Round 9 P1.1: 'Add `a_b_c` Money (TaxUnit ) as an input.' no longer counts as an output, so queued #1416 reports its missing outputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
drain reads every module after a remove or delete predicate as WORK until a genuine new predicate starts a clause, however the modules are joined (commas, and/or, a repeated from, brackets). A module a removal takes something from is WORK whatever the removed object names, including a blocked_by declaration; only an affirmative deferral predicate makes a module DEFERRED. Brackets are matched with one stack pass, so 16,000 unmatched openers take about 0.1 s instead of minutes. Round 9 P1.2, P1.3 and P2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An encoded rulespec PR that changes only .axiom/toolchain.toml and a module now gets the no-encoding-manifest warning. The signed encoder writes manifests to .axiom/encoding-manifests/<module path>.json at the repository root, as the four landed-manifest PRs (#1364, #1366, #1412, #1470) show, so those stay warning-free. The README names the manifest path and records the normalized declaration reading. Round 9 P3.

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

Loaded machines pushed some Hypothesis examples past the 200 ms default deadline, which reported scheduler delay as a functional failure. Example counts and assertions are unchanged; the bracket-scan regression keeps its explicit one-second limit. .diag/ and .uv-cache/ stay out of the tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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