Skip to content

C-30/C-46: the exclusion-manifest tripwire now runs in CI — the 2026-08-03 revert was the right observation with the wrong cause - #280

Merged
Polichinel merged 3 commits into
developmentfrom
fix/c30-exclusion-guard-runs-in-ci
Aug 17, 2026
Merged

Polichinel merged 3 commits into
developmentfrom
fix/c30-exclusion-guard-runs-in-ci

Conversation

@Polichinel

Copy link
Copy Markdown
Collaborator

Why now

On 2026-08-17 we told FAO, in writing, that the 76-cell exclusion list "is frozen in code and asserted against the producer in our test suite, so it cannot drift without failing loudly."

It could. test_manifest_matches_datafactory_land_minus_land_gaul — C-30's drift tripwire — skipped on every pull request, because views-datafactory was not fetched in CI. The guarantee we gave a partner was running on one laptop.

The blocker was misdiagnosed, and that is the interesting part

C-46 and conftest.SIBLINGS both recorded the same reason for not fetching it:

Checking it out converts an honest skip into a FileNotFoundError — measured 2026-08-03, tried and reverted.

The observation was correct. The cause was not. test_gaul_lookup_fidelity gated on data/raw/gaul_admin/ being a directory — and that directory is tracked in views-datafactory (it holds supplement_azores.geojson), while the seven GAUL parquets beside it are not. A checkout therefore satisfied the gate, the value comparison ran, and it died on a missing parquet.

The sibling was never the obstacle. The gate was asking whether a folder existed when it needed to ask whether the files it reads existed. The PRIO-GRID check at the bottom of the same file already had it right, gating on priogrid_cell.dbf itself.

Reproduced before changing anything

Against git worktree add --detach of views-datafactory@HEAD — which contains exactly what actions/checkout produces:

result
before 1 failed, 37 passed, 1 skipped — FileNotFoundError: .../gaul_admin/gaul0_code.parquet
after 35 passed, 4 skipped, 0 failed

What changed

  • tests/test_gaul_lookup_fidelity.py — _HAS_DATAFACTORY gates on the seven parquets it reads rather than on their directory; the skip reason now states that the parquets are absent and untracked upstream, instead of "checkout not found", which was false in exactly the case that broke CI.
  • .github/workflows/run_pytest.yml — fetches views-datafactory at ref: main, declares VIEWS_DATAFACTORY.
  • tests/conftest.py — ci_checkout=True with the corrected reason. test_ci_sibling_coverage requires the workflow and the declaration to agree; it passes (22 tests).
  • Register — C-30 and C-46 both record what changed. C-46's residual is discharged.

What this buys

Two checks move from skipped to running in the gate:

  1. C-30's exclusion-manifest tripwire — reads src/datafactory_query/{land,land_gaul}_pgids.json, both tracked upstream.
  2. C-46's own TestReleaseGate::test_land_gaul_commit_is_in_a_release_tag — the gate that entry says had "never run anywhere but one laptop."

The producer-comparison half still skips, honestly. Closing that needs the parquets published somewhere fetchable, which is not an access grant and not this PR.

What this does not do

C-30 is not re-tiered. What holds it at Tier 1 is the absence of a real global delivery exercising the guard end to end. CI wiring does not supply that, and saying otherwise would be the kind of claim this repo keeps a register to prevent.

Risk accepted

A merge to views-datafactory's main can now turn this repository red. That is ADR-016's deliberate trade, already accepted for views-appwrite and argued in C-46: it is the drift detector working, and the alternative is the state we were in — noticing drift only when someone happened to run the suite locally.

Verification

  • ruff check . — clean.
  • test_ci_sibling_coverage — 22 passed.
  • Full suite under a CI-shaped sibling: 428 passed, 5 skipped, 39 xfailed. The 25 failures are the pre-existing local venv drift registered as C-104 (pipeline-core 2.3.0 / pyarrow 23.0.1 against a lock pinning 3.0.1 / 16.1.0), identical before and after this change; CI installs from the lock.

Polichinel and others added 3 commits August 17, 2026 11:10
…C-46)

On 2026-08-17 this repository told FAO in writing that the 76-cell exclusion list
"is frozen in code and asserted against the producer in our test suite, so it
cannot drift without failing loudly". It could. The assertion —
`test_manifest_matches_datafactory_land_minus_land_gaul` — skipped on every pull
request, because views-datafactory was not fetched in CI. The guarantee was
running on one laptop.

The stated reason it was not fetched did not survive checking. C-46 and
`conftest.SIBLINGS` both recorded that a checkout "converts an honest skip into a
FileNotFoundError — measured 2026-08-03, tried and reverted". The observation was
right; the diagnosis was wrong. `test_gaul_lookup_fidelity` gated on
`data/raw/gaul_admin/` being a DIRECTORY, and that directory IS tracked in
views-datafactory — it holds `supplement_azores.geojson` — while the seven GAUL
parquets beside it are not. So a checkout satisfied the gate, the value
comparison ran, and it died. The sibling was never the obstacle; the gate asked
whether a folder existed when it needed to ask whether the files it reads existed.

Reproduced before changing anything, against a `git worktree --detach` of
views-datafactory@HEAD — exactly what `actions/checkout` produces:

    before   1 failed, 37 passed, 1 skipped
             FileNotFoundError: .../data/raw/gaul_admin/gaul0_code.parquet
    after   35 passed,  4 skipped, 0 failed

What changed:

- `test_gaul_lookup_fidelity._HAS_DATAFACTORY` gates on the seven parquets it
  actually reads, not on their directory. The PRIO-GRID check at the bottom of
  the same file already had this shape, gating on `priogrid_cell.dbf` itself.
- its skip reason now says the parquets are absent and that they are untracked
  upstream, instead of "checkout not found" — which was false in precisely the
  case that broke CI.
- `run_pytest.yml` fetches views-datafactory at `ref: main`, and declares
  `VIEWS_DATAFACTORY`; `conftest.SIBLINGS` flips to `ci_checkout=True` with the
  corrected reason. `test_ci_sibling_coverage` requires those two to agree, and
  does.

Two checks move from skipped to running in the gate: C-30's exclusion tripwire,
and C-46's own `TestReleaseGate::test_land_gaul_commit_is_in_a_release_tag` —
the gate that entry says had "never run anywhere but one laptop". The
producer-comparison half still skips, honestly, and still needs the parquets
published somewhere fetchable.

C-30 is NOT re-tiered: what holds it at Tier 1 is the absence of a real global
delivery exercising it end to end, and CI wiring does not supply that.

Suite: 428 passed, 5 skipped, 39 xfailed under a CI-shaped sibling. The 25
failures are the pre-existing local venv drift registered as C-104, unchanged by
this commit. ruff clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Findings from /review-diff on the previous commit. All four are the same defect
that commit fixed, one level up: a gate asking about something other than what
the test reads.

`test_gaul_lookup_fidelity` had ONE sibling gate, keyed to the GAUL parquets, and
applied it to four tests. Only one of them reads a parquet:

  test_lookup_values_match_the_producer_parquets  7 parquets (untracked)  correct
  test_lookup_gid_set_equals_the_declared_region  land_gaul_pgids.json — TRACKED
  test_coordinate_formula_matches_every_pg_cell   priogrid_cell.dbf; self-skips
  test_coord_dtypes_are_wire_stable               only the committed lookup

So two checks that need nothing untracked, and one that needs no sibling at all,
were dark in CI because they carried a mark for someone else's dependency.

The last is the one worth naming: it asserts `abs(v) < 2**53` for every code
column — that the §5.1 int64->float64 wire cast is lossless. That is precisely
the property ADR-013 §5.1a records and the 2026-08-17 mail to FAO relies on, and
it was skipped in the gate while resting on nothing but a committed fixture.

Changes:
- `_HAS_DATAFACTORY` -> `_HAS_GAUL_PARQUETS`, `_needs_datafactory` ->
  `_needs_gaul_parquets`. The old name asserted the conflation the bug was made
  of: a checkout can be present while these are absent, which is the normal case
  in CI.
- new `_needs_region_pgids`, gating on the tracked pgid list, so the region-set
  check runs in CI.
- the coordinate check loses the mark; its own `dbf.exists()` skip was already
  the right gate and is now the only one.
- the dtype check loses the mark entirely; it is always-on.
- the parquet comparison uses `_GAUL_ADMIN` instead of respelling the path, so
  the gate and the reader cannot drift apart — which is how this started.
- module docstring rewritten: three gates, not two, with the rule stated.

Measured. CI-shaped sibling (tracked files only): fidelity file goes 21 passed /
4 skipped -> 23 passed / 2 skipped, and both remaining skips name the artifact
they want. Full suite CI-shaped: 430 passed / 3 skipped, up from 426 / 6 before
this branch. With a full local sibling: 39 passed, nothing lost. ruff clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four findings, all mine, all the same defect the branch set out to fix.

1. HIGH — I broke the no-checkout case. Removing the mark from
   `test_coordinate_formula_matches_every_priogrid_cell` exposed that its
   `dbf.exists()` skip sits BELOW `dbf = _DATAFACTORY / ...`, and `_DATAFACTORY`
   is None when nothing resolves. Measured: `TypeError: unsupported operand
   type(s) for /: 'NoneType' and 'str'` — a red suite for any contributor
   without a views-datafactory checkout. CI never saw it, because CI now always
   has one. Fixed with a declared `_needs_priogrid_dbf` gate, like its
   neighbours.

2. MEDIUM — two `xfail(strict=True)` deploy gates began EXECUTING in CI once the
   sibling was fetched, and xfailed on missing files rather than on what they
   assert. `data/assembled/{grid.npy,provenance.json}` are untracked upstream —
   and measured today, `data/assembled/` is empty in the maintainer's own
   checkout too, so these gates have never evaluated their assertions ANYWHERE.
   A strict xfail that can only fail on FileNotFoundError can never XPASS, so
   the flip that is the whole mechanism could not fire. They now skip when the
   inputs are absent, which makes the state visible instead of disguising it as
   a passing xfail. Registered as C-108; skipping does not make the gate work.

3. LOW/MED — the newly-live C-30 tripwire gated on `land_pgids.json` while
   reading `land_gaul_pgids.json` too. Now that it runs in CI against
   views-datafactory@main, a rename upstream would have surfaced as a traceback
   instead of the drift assertion it exists to produce. Gate names both files.
   The stale `(CI has no sibling → skip)` comment is corrected — it was the last
   line in the repo still asserting the old state.

4. LOW — `test_the_builder_and_the_tests_resolve_the_same_datafactory` guards
   the fallback under `if "VIEWS_DATAFACTORY" not in os.environ`, with a comment
   saying that branch matters most in CI. Setting the variable in CI silenced
   exactly that branch and left the surviving assertion comparing
   $VIEWS_DATAFACTORY with itself. Now asserted unconditionally via
   `monkeypatch.delenv`.

Also corrected two overclaims of my own: the workflow comment said the sibling
was fetched "for ONE check" and the register said "two", when four began
executing.

Measured. CI-shaped sibling: 430 passed / 5 skipped / 37 xfailed (two former
xfails are now honest skips). No sibling resolvable: no TypeError, 8 clean
skips. Full local sibling: nothing lost. ruff clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Polichinel

Copy link
Copy Markdown
Collaborator Author

Review round — /code-review high + /review-diff

Two review passes ran against this branch. Both found real defects in my own changes, and every one was the same class the branch exists to fix: a gate asking about something other than what the test reads.

/review-diff — 5 findings, all fixed in 6fb2046

test_gaul_lookup_fidelity had one sibling gate keyed to the GAUL parquets, applied to four tests. Only one reads a parquet:

test reads was gated on
test_lookup_values_match_the_producer_parquets 7 parquets (untracked) parquets — correct
test_lookup_gid_set_equals_the_declared_region land_gaul_pgids.json — tracked parquets
test_coordinate_formula_matches_every_priogrid_cell priogrid_cell.dbf parquets
test_coord_dtypes_are_wire_stable only the committed lookup parquets

The last is worth naming: it asserts abs(v) < 2**53 for every code column — that the §5.1 int64→float64 wire cast is lossless, the property ADR-013 §5.1a and the 2026-08-17 mail to FAO both rest on — and it was dark in CI while depending on nothing but a committed fixture.

Also fixed: _HAS_DATAFACTORY → _HAS_GAUL_PARQUETS (the old name asserted the very conflation the bug was made of), and the parquet path is no longer respelled between the gate and the reader.

/code-review high — 4 findings, all fixed in 58ac58d

  1. HIGH, and mine. Removing a mark exposed that test_coordinate_formula_matches_every_priogrid_cell builds dbf = _DATAFACTORY / ... above its .exists() check. With no checkout, _DATAFACTORY is None → TypeError. CI never sees this, because CI now always has the sibling — it lands purely on contributors. Fixed with a declared _needs_priogrid_dbf gate.
  2. MEDIUM. Two xfail(strict=True) deploy gates started executing once the sibling was fetched, and xfailed on missing untracked files rather than on what they assert. Measured further: data/assembled/ is empty in the maintainer's own checkout too — these gates have never evaluated their assertions anywhere, and a strict xfail that can only fail on FileNotFoundError can never XPASS, so the flip that is the entire mechanism could not fire. Now skip when inputs are absent. Registered as C-108; skipping makes the state honest, it does not make the gate work.
  3. LOW/MED. The newly-live C-30 tripwire gated on land_pgids.json while reading land_gaul_pgids.json too — upstream rename would surface as a traceback rather than the drift assertion. Gate names both.
  4. LOW. test_the_builder_and_the_tests_resolve_the_same_datafactory guards the fallback under if "VIEWS_DATAFACTORY" not in os.environ, with a comment saying that branch matters most in CI — and this PR set that variable in CI, silencing it. Now unconditional via monkeypatch.delenv.

Two overclaims of my own, corrected

The workflow comment said the sibling was fetched "for ONE check"; the register said "two". Four began executing.

Verification

environment result
CI (real) 455 passed, 5 skipped, 37 xfailed, 0 failed
local, CI-shaped sibling 430 passed, 5 skipped, 37 xfailed
local, no sibling resolvable no TypeError, 8 clean skips
local, full sibling nothing lost

ruff check . clean. The 25 local failures throughout are the venv drift registered as C-104 — they pass in CI, which is the evidence the diagnosis was right.

@Polichinel
Polichinel merged commit 31d7482 into development Aug 17, 2026
4 checks passed
@Polichinel
Polichinel deleted the fix/c30-exclusion-guard-runs-in-ci branch August 17, 2026 09:24
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