Skip to content

Notice when a Mech card drifts from the site it cites - #109

Merged
realmarcin merged 2 commits into
mainfrom
feat/card-drift-check
Sep 22, 2026
Merged

realmarcin merged 2 commits into
mainfrom
feat/card-drift-check

Conversation

@realmarcin

@realmarcin realmarcin commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Closes #104.

The card headline figures are hand-curated from each Mech's published browser. Nothing regenerates them, and nothing noticed when they went stale.

It found twice what I found by hand

  ok       AntibioticMech           2,939
  ok       CellStructureMech          421
  ok       HabitatMech              3,206
  ok       NaturalProductMech       3,115
  ok       ProteinTraitsMech      429,291
  ok       TaxonMech              625,960
  DRIFTED  CommunityMech        card 392, site 405
  DRIFTED  CultureMech          card 6,286, site 6,288
  DRIFTED  MediaIngredientMech  card 2,951, site 2,952
  DRIFTED  TraitMech            card 723, site 751

The triage sweep caught CommunityMech and TraitMech. The check caught four — including CultureMech, whose canonical count moved, and which I would not have thought to re-check.

Scheduled only, not on pull_request

The nightly cron already exists. A blocking check would make an unrelated docs fix unmergeable whenever a Mech shipped records overnight — TraitMech went 750 → 751 while this PR was being written. Nightly red is actionable; a blocked PR is not.

The ten sites do not agree on how they publish it

SOURCES pins it per Mech rather than guessing:

kind Mechs why
html 6 the figure is in a stat tile
text CultureMech, NaturalProductMech stated in prose, not a tile
json ProteinTraitsMech, MediaIngredientMech computed at runtime; the markup carries a placeholder and only the data file has the number (#86)

CultureMech points at pages/, not the app/ landing tile — that one is the legacy figure matching no data layer. All are pages/ or app/ URLs, never the repo root, since several roots are meta-refresh shells returning 200.

A fetch that fails is not a number that is wrong

Warning for the first, failure for the second. Conflating them teaches people to ignore a nightly red for someone else's outage.

The distinction earned itself immediately: the first run reported "html shape changed" for CultureMech and NaturalProductMech rather than inventing drift — which is how the text kind came to exist. A checker that had guessed would have reported two false drifts and hidden two real ones.

Coverage is pinned

A test asserts every card has a SOURCES entry and vice versa, so an eleventh Mech cannot get a card and be silently exempt — which is this issue's failure mode one level up. Verified it fails by removing TaxonMech from the table.

29 tests pass, assemble_page.py --check clean, workflow YAML parses with the new step correctly gated.

Not fixed here: the four drifted figures. Refreshing them belongs with #84/#87's full pipeline run, where the card and graph sources are updated together.

Review fixes

Rebased over #108. From the adversarial review, fixed here: a bare-list JSON body crashed published() with an uncaught AttributeError instead of being reported unreadable, and the list fallback was unreachable (closes #110); _fleet/README.md now documents the check (closes #111); the review skill's gate section separates the three merge-blocking steps from the scheduled card check (closes #112). Left filed: #113 (five cards now drift, nightly will be red until the #84/#87 refresh), #114 (three card regexes), #115 (all-unreadable run exits 0).

🤖 Generated with Claude Code

realmarcin and others added 2 commits September 22, 2026 00:27
The card headline figures are hand-curated from each Mech's published browser.
Nothing regenerates them and nothing noticed when they went stale, so the only
thing standing between the page and a wrong number was somebody looking (#104).

Four of ten have drifted since the last refresh two days ago:

    CommunityMech        card 392    site 405
    CultureMech          card 6,286  site 6,288
    MediaIngredientMech  card 2,951  site 2,952
    TraitMech            card 723    site 751

I had found two of those by hand; the check found four, which is the argument
for having it.

Scheduled only, and deliberately not on pull_request. The ten sites publish on
their own cadence — TraitMech moved again while this was being written — so a
blocking check would make an unrelated docs fix unmergeable whenever a Mech
shipped records overnight. The nightly cron already exists; a red there is
actionable, a blocked PR is not.

The ten do not agree on how they publish the figure, so SOURCES pins it per
Mech rather than guessing: a stat tile for six, prose for CultureMech and
NaturalProductMech, and a JSON file for ProteinTraitsMech and
MediaIngredientMech, whose pages compute it at runtime and whose markup carries
only a placeholder. CultureMech points at pages/ rather than the app/ landing
tile, which is the legacy figure matching no data layer. All of these are the
pages/ or app/ URL, never the repo root, since several roots are client-side
meta-refresh shells that return 200.

A fetch that does not arrive is a warning; a fetch that arrives and does not
match is a failure. Conflating them would teach people to ignore a nightly red
for somebody else's outage — and the distinction earned itself immediately,
reporting "shape changed" for the two prose sites rather than inventing drift,
which is how the prose kind came to exist.

A test pins that every card has an entry in SOURCES and vice versa, so an
eleventh Mech cannot get a card and be silently exempt. Verified it fails by
removing TaxonMech from the table.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From the adversarial review of this PR.

published() reached for document.get() before checking the shape, so a body
that is a bare JSON list raised AttributeError, which the except clause did
not name; the nightly run died with a traceback instead of an "unread" line,
and the list fallback below it was unreachable (closes #110). The shape is
tested first now, and a body of an unexpected type is reported, not raised.

_fleet/README.md enumerates the pipeline scripts and the hand-curated card
layer but said nothing about the check; it now says what it compares, when it
runs and why a nightly red is not a PR red (closes #111). The review skill's
gate section said the workflow runs exactly three things; it now separates the
three merge-blocking steps from the scheduled card check (closes #112).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@realmarcin
realmarcin force-pushed the feat/card-drift-check branch from e188035 to d6eb8ca Compare September 22, 2026 07:28
@realmarcin
realmarcin merged commit c5d8bfb into main Sep 22, 2026
1 check passed
@realmarcin
realmarcin deleted the feat/card-drift-check branch September 22, 2026 07: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

1 participant