Skip to content

feat(ci): declare sibling visibility, and un-dark seven cross-repo checks - #229

Merged
Polichinel merged 3 commits into
developmentfrom
feat/sibling-visibility-declaration
Aug 9, 2026
Merged

feat(ci): declare sibling visibility, and un-dark seven cross-repo checks#229
Polichinel merged 3 commits into
developmentfrom
feat/sibling-visibility-declaration

Conversation

@Polichinel

Copy link
Copy Markdown
Collaborator

Adds ADR-016. Partially mitigates C-81; resolves the CI residual on C-46 and C-57.

The failure this fixes had already happened

A few tests here verify claims this repo makes about other repos — chiefly that the coordinate-registry edition we pinned is still the one that exists. They need the sibling on disk, so they skipped in CI and ran only on a laptop.

Which siblings CI could fetch was decided in a comment, and the comment said views-appwrite was private. It went public on 2026-08-08 (views-appwrite@9d80b75, a deliberate recorded act) and the comment did not — so seven checks stayed dark for two days for no reason, including both registry-drift detectors, which had fired twice in one day the week before.

The first draft of ADR-016 proposed issuing a credential to reach those seven. It was written one day after the fact justifying it stopped being true. That is not a footnote to this change; it is the argument for it.

The fix: a declaration that carries its own date

Sibling(env=..., public=..., public_checked=..., ci_checkout=..., note=...)

public is a fact about the world. ci_checkout is our decision. Conflating them into one sentence of prose is exactly what went wrong.

frozen=True, kw_only=True deliberately: two adjacent booleans are a one-token slip between "public, not checked out" and the combination rule G3 forbids, and a plain dict would let a missing ci_checkout read as None — silently exempting that sibling from every rule, which is the failure mode this repo has registered more than any other.

ref: main is not a detail

Without it actions/checkout takes the sibling's own default branch — which for views-appwrite is development. Two checks read the working tree while a third demands reachability from main. Latent today (both branches carry the same edition); contradictory the first time a bump lands on development first. Retrofitted to the existing crafdapi step too, and made a rule rather than a habit.

The guards

tests/test_ci_sibling_coverage.py — eight rules, each a pure function of (workflow, siblings), so each runs against a synthetic broken world as well as the real file. A rule demonstrable only by editing CI is one nobody ever watches fail.

rule why
G1 declared checkouts exist, and the env var on the pytest step matches the checkout path env on the Lint step passes a naive check and the tests see nothing
G2 every checkout maps to a declaration, across all workflow files a sibling smuggled into codeql.yml is not invisible
G3 private ⇒ must have a token; public ⇒ must not see below
G4 every non-checked-out sibling explains why, naming a record a deferral needs an owner (ADR-014 §4)
G5 no sibling checkout carries continue-on-error see below
G6 paths under _siblings/ it is what ruff excludes; a sibling elsewhere once produced 745 lint findings
G7 ref: main above
G8 public_checked parses, and is not in the future

G3 and G5 look like fussiness and are not. No test here touches the network — that is a hard convention. public is verified by CI doing the tokenless checkout: the default GITHUB_TOKEN is scoped to this repo, so fetching a repo declared public fails if it is actually private. One token: added to dodge a rate limit, or one continue-on-error: added while debugging, and that verification silently stops existing.

A meta-test refuses a rule added to _RULES with no mutant proving it bites — and it is itself mutation-proven.

Found en route: an empty variable was a silent skip

broken_sibling_overrides filtered on if value, so VIEWS_APPWRITE="" read as unset — which is exactly what a YAML interpolation resolving to nothing produces. Seven checks would skip on a green build, in the one place it matters. Path("").exists() is True, so dropping the truthiness test alone makes it worse; the .strip() is load-bearing.

One test changes character rather than merely un-skipping

test_no_coordinate_value_is_copied_into_this_repo — the scan refusing registry values in this public repo's markdown — now runs on every PR rather than only on a maintainer's machine. README.md carried four such values once already. Its own comment said the opposite and has been corrected.

What is deferred, and what is overridden

Deferred: the credential for views-faoapi, the one genuinely private sibling. It buys one test — admittedly the one whose failure produces no error anywhere — and a better answer is pending: asking FAO to consent to making that repo public. An audit of its full history found no credentials and no partner staff emails; it is already MIT. ADR-016 §8 carries the trigger for falling back to the credential.

Overridden, and disposed of in the same change (ADR-014 §5): C-46 recommended "do not add sibling checkouts to the per-PR workflow". Its stated objection was coupling to another repo's default branch — which ref: main addresses. The residual cost is real and stated rather than glossed: a merge to a sibling's main can turn this repo red and block merges until someone re-pins. That is the drift detector working, and the trade is deliberate.

Also

pyyaml declared and re-locked in the same commit — it already arrived transitively, which is precisely how this repo lost pytest, and a stale lock makes poetry install refuse and takes the whole suite with it. The lock diff is two lines.

Deciders swept across ADRs 000–015: the PRIO MD&D Team no longer exists, and a decider field naming a body nobody can ask is worse than useless.

Verification

ruff check .                                 # clean
poetry check --lock                          # fresh
pytest -q                                    # 404 passed, 40 xfailed
VIEWS_APPWRITE=/nonexistent pytest -q        # FAILS, does not skip
VIEWS_APPWRITE="" pytest -q                  # FAILS — the hole above

Expected CI skips after this lands: 9 (8 views-datafactory, which no credential can fix — its tests need raw data absent from its git repo — plus 1 views-faoapi). I will read the real number off the CI run and correct the record if it differs, rather than deriving it.

🤖 Generated with Claude Code

Polichinel and others added 3 commits August 10, 2026 01:22
…ecks

A few tests here verify claims this repo makes about OTHER repos — chiefly that the
coordinate-registry edition we pinned is still the one that exists. They need the sibling
on disk, so they skipped in CI and ran only on a laptop.

Which siblings CI could fetch was decided in a COMMENT, and the comment said
views-appwrite was private. It went public on 2026-08-08 (views-appwrite@9d80b75, a
deliberate recorded act) and the comment did not, so seven checks stayed dark for two days
for no reason — including both registry-drift detectors, which had fired twice in one day
the week before. The first draft of ADR-016 proposed issuing a credential to reach them,
written one day after the fact justifying it stopped being true.

So the fact now lives in a declaration that carries the date it was checked:

    Sibling(env=..., public=..., public_checked=..., ci_checkout=..., note=...)

`public` is a fact about the world; `ci_checkout` is our decision. Conflating them is what
went wrong. frozen + kw_only deliberately: two adjacent booleans are a one-token slip
between "public, not checked out" and the combination G3 forbids, and a plain dict would
let a missing key read as None and silently exempt a sibling from every rule.

CI now checks out views-appwrite and views-crafdapi, both with `ref: main` — without it
actions/checkout takes the sibling's own default branch, which for views-appwrite is
`development`, while two checks read the working tree and a third demands reachability
from main. Latent today; contradictory the first time a bump lands on development first.

tests/test_ci_sibling_coverage.py: eight rules, each a pure function of
(workflow, siblings) so each runs against a synthetic broken world as well as the real
file. G3 (public => no token) and G5 (no continue-on-error) look like fussiness and are
not: `public` is verified by CI DOING the tokenless checkout, and either key silently
turns it into an unchecked claim. A meta-test refuses a rule added to _RULES with no
mutant proving it bites.

Also fixed, found en route: broken_sibling_overrides treated a variable set to the EMPTY
STRING as unset — which is exactly what a YAML interpolation resolving to nothing
produces. Seven checks would skip on a green build. Path("") .exists() is True, so the
obvious fix makes it worse; the strip is load-bearing.

pyyaml declared and re-locked in the same commit. It already arrived transitively, which
is precisely how this repo lost pytest; a stale lock makes poetry install refuse and takes
the whole suite with it.

test_no_coordinate_value_is_copied_into_this_repo changes character rather than merely
un-skipping: the scan refusing registry VALUES in this public repo's markdown now runs on
every PR rather than only on a maintainer's machine. Its comment said the opposite.

ADR-016 rewritten around the public/private rule; the token guide deleted, since we are
not issuing a token. The credential for views-faoapi — the one private sibling, worth one
test — is deferred with a named trigger, pending a request to FAO to make it public.

C-46's explicit recommendation ("do NOT add sibling checkouts to the per-PR workflow")
is overridden and disposed of in the same change: its stated objection was coupling to a
default branch, which `ref: main` addresses; the residual cost, that an upstream merge to
main can block merges here, is accepted and stated rather than glossed.

Deciders swept across ADRs 000-015: the PRIO MD&D Team no longer exists.

Suite 404 passed / 40 xfailed; ruff clean; lock fresh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI caught this on the first run, which is the point of the change it is part of: the
guard could not fail locally, because locally there is only one clock.

The dates were stamped from a maintainer's machine in CEST at 01:25 on 2026-08-10. The
runner is UTC, where it was 23:25 on 2026-08-09. So a date recorded truthfully today read
as tomorrow two hours away, and G8 called it a lie.

"Today" is not something a bare ISO date determines — it depends on where the reader is —
so comparing one against `date.today()` asks two different questions. A full day of
tolerance covers every real offset, and a date more than a day ahead is still exactly what
the rule is for: a fact nobody checked.

Written down rather than fixed quietly, because the repair a hurried reader reaches for
here is deleting the rule (ADR-014 §3). Added test_g8_tolerates_a_timezone_but_not_a_
fiction, pinning both sides of the boundary at 0/+1/+2/+400 days — a tolerance with no
test becomes whatever made the last failure go away. Suite green under TZ=UTC too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Read off the merge run rather than derived. 398 passed / 9 skipped / 38 xfailed; the
9 are 8 views-datafactory (needs published data, not a credential) and 1 views-faoapi.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Polichinel
Polichinel merged commit 7ecbfe1 into development Aug 9, 2026
4 checks passed
@Polichinel
Polichinel deleted the feat/sibling-visibility-declaration branch August 9, 2026 23:29
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