Skip to content

Give contract assertions unique ids and declare full-run population coverage - #612

Merged
MaxGhenis merged 3 commits into
mainfrom
w4/runner-assertion-ids
Oct 15, 2026
Merged

MaxGhenis merged 3 commits into
mainfrom
w4/runner-assertion-ids

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Fixes #611

These files belong to workstream W1 (the UK Budget reform contract runner: budget_reform_contract.py and budget_reform_population.py). W1's session is idle, so W4 opens this change for W1's runner by the coordinator's ruling. The coordinator's rule for it: it may only label and declare. No check's status, expected value or actual value changes, and no check is added or removed.

What changes

  • Assertion ids. Each case row carries assertion_id, its check label plus its index in case.checks. Two assertions with one label (for example a mandatory and a pending one on the same value) stay distinct in a report. The qualification gate (Qualify UK model and data candidates automatically before promotion #574) needs this so a failing mandatory assertion cannot borrow a pending authorization.
  • Declared population coverage. budget_reform_population.population_check_routes(routes) declares the exact (check_id, route) rows a full population run emits, so the gate can require the complete multiset.

Evidence that no verdict changes

  • test_assertion_ids_change_no_verdict covers every case in CASES, every route and three fake routes (hashed values, unchanged values, a failing route). It compares the 1,322 report rows, without assertion_id, with tests/fixtures/uk_contract/evaluate_case_rows.json.
  • That fixture was recorded from the runner on main 9d0c7c5 (the runner files are byte-identical to main 503d499), before this change. It records the verdict fields: case, route, check, status, expected, actual, decision and holds.
  • The harness is tests/uk_contract_row_scenarios.py. Recording twice gives identical bytes.
  • A population test checks that population_check_routes equals the rows run_population_checks emits on a tiny dataset.
  • A test checks that two same-label assertions get distinct ids.
  • Runner tests on policyengine-uk 2.126.12: 36 passed.

Invariants

  • Row fields other than assertion_id are identical to main's for every case, route and fake route.
  • Assertion ids are unique within each case and route.
  • The declared population coverage equals what a full run emits.

Independent review: GPT-6.1 Sol, round 1 running (~/reviews/w4-budget-ci-2026-10-11/runner-ids-round1-sol.md).

axiom: n/a: test-harness labelling in the contract runner; no policy change

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 14, 2026 08:21
…verage

Two additions to the UK Budget reform contract runner (W1's files) that the
qualification gate (#574) needs. Neither changes any check's status,
expected value or actual value:
- Each case row carries assertion_id, its check label plus its index in the
  case's checks, so two assertions with the same label (one mandatory, one
  pending) stay distinct in a report.
- budget_reform_population.population_check_routes(routes) declares the
  exact (check, route) rows a full population run emits.

tests/test_assertion_ids_change_no_verdict compares every case, route and
three fake routes' rows, without assertion_id, with rows recorded from the
runner on main before this change (tests/fixtures/uk_contract/
evaluate_case_rows.json, 1,322 rows), so any change to a verdict fails it.
A population test checks the declared coverage equals what a run emits.

Co-Authored-By: GPT-6.1 Sol via Subfleet <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-1 review of #612:
- population_check_routes declares a completed run's coverage; the early or
  partial error row ("population", "-") is declared separately as
  POPULATION_ERROR_DIAGNOSTIC, and a report holding it does not hold.
  Shared constants name every population check, with byte-identical
  strings, so labels cannot drift from the runner.
- Duplicate country routes declare one baseline-isolation row, as the
  runner emits.
- The golden fixture gains 45 rows from five scenarios that reach the
  missed verdict branches (matching variable comparisons, both sides of a
  tolerance, a missing value, a matching pending check, an expected
  rejection). The original 1,322 rows are an unchanged prefix. The fixture
  was regenerated from main 503d499's runner twice, identically, and the
  four reported mutants now fail the golden test.

Co-Authored-By: GPT-6.1 Sol via Subfleet <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 14, 2026
#612's population stub raised universal_credit under the UC allowance
reform but not universal_credit_pre_benefit_cap, so on this branch the
payment no longer equalled the award less deductions and #614's
accounting assertion rightly failed three coverage tests. The stub now
raises the award with the payment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 15, 2026 02:31
@MaxGhenis
MaxGhenis merged commit c5ec0e7 into main Oct 15, 2026
12 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Merged at 0095d7e on the standard gates. These are W1's runner files, changed under the coordinator's ruling; the change only labels and declares.

  • Checks: all 12 passed.
  • Independent review (GPT-6.1 Sol), round 2: APPROVE. It regenerated the fixture from main 503d499 (1,367 rows, byte-identical, with the original 1,322-row prefix). It confirmed that the shared label strings are byte-identical to main's and that no row, status, value or order changes. All four round-1 mutants are now rejected.
  • Low finding, carried into Check the UC allowance response on the award before deductions #614: the assertion-id tests don't yet cover extraction-error rows.

MaxGhenis added a commit that referenced this pull request Oct 15, 2026
Round-2 review of #614:
- Medium: deductions may not reduce the amount payable below a penny an
  assessment period (SI 2013/380 Sch 6 para 3(1)(a)). The check now
  requires every payment, in both runs, to be at least the lesser of its
  award and 12p a year, so deductions that take a whole award fail.
- Medium: every value the check reads must be finite in both runs; a NaN
  deduction, cap reduction or award no longer passes the comparisons.
- Low: the award-decline case lowers the award and its payment together,
  so only the award condition catches it; new tests cover errors that
  cancel across units, the penny boundary on gains, the 10% scaling
  tolerance and a payment left at exactly the floor. Removing any one of
  the four conditions fails a test.
- Low: the population stub has no cap reduction or deductions, so its
  payments equal its awards at every level.
- #612 review Low: assertion ids are tested on extraction-error, pending
  and unsupported rows too.

On the full 1.58.1 file with policyengine-uk 2.130.1 the check holds: 0
award falls, 0 accounting breaks, 0 payments below the floor, 0
non-finite values. All 66 runner tests pass.

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.

Contract runner: give assertions unique ids and declare full-run population coverage

1 participant