Status: deferred 2026-10-04: the visit-page picker run waits for the owner. The monthly refresh and the older icon formats are built (sections 1 and 2). Part of the identity family; written after the four specs were built, from the three optional follow-ups the build left.
Why
The identity build (links, social accounts, short names, colors and marks) ships with every value in a committed file,
refreshed by hand (sync-wikidata, probe-sites, sync-brand). Three things were left open: nothing refreshes them on
a schedule; 42 colleges have an icon the decoder couldn't read; and 492 colleges have no visit page because their
admissions page doesn't name one plainly enough for the scorer.
1. The monthly refresh (built 2026-10-04)
Problem. Social handles, visit pages, colors, and icons change; links break. A link becomes null only after two probe runs a day or more apart both find it gone, so the probe has to run again for that rule to ever act.
Design. A new workflow, .github/workflows/identity-refresh.yml, separate from the college-reported workflow (which
auto-merges and expects only college-reported data changes):
- When: checks daily at 06:00 UTC and proceeds only when due, meaning the newest probe in
data/site-probe.jsonis at least 28 days old, and when no college-reported data PR is open (both writedata/schools.json, and the second PR would conflict). Also runs by hand (workflow_dispatch), with aforceinput that skips the due check. - What:
npm run sync-wikidata,npm run probe-sites,npm run sync-brand, each ending inmerge-identity. No secrets beyond the repository token; Wikipedia and Wikidata responses are cached between runs. - Then: when
data/orpublic/brand/changed, a branchdata/identity-refresh-<date>, a PR with a generated summary (visit pages, accounts, colors, and marks gained and lost; links that failed and that became null) and its own release note, and auto-merge when CI passes, like the college-reported data PRs. The same guards apply as for any data change: lineage, the alias table's invariants, and every mark having its file.
As built (2026-10-04). .github/workflows/identity-refresh.yml and scripts/identity-refresh.mts (due,
pr-body, release-note; tests in tests/identity-refresh.test.mts):
- The due rule is the newest
retrievedindata/site-probe.jsonat least 28 days old (or no file); an open college-reported data PR always skips the run, even withforce, since the two PRs would conflict. A skipped run is a green job with a notice. - It runs the three syncs in order, caching
.cache/wikipediaand.cache/wikidatabetween runs, and opens a PR whendata/orpublic/brand/changed. The body has before and after counts (visit pages, accounts, colors, marks), the colleges that gained or lost each, the links that failed this run and that became null, and the marks added and removed. The release note follows once the PR has a number. Auto-merge (--squash, as the college-reported PRs) is on when all three syncs succeeded; otherwise a comment says why it waits for a person. - Key order. The refresh ends in
merge-identity, which used to move identity's keys to the end of each record: a run with nothing new rewrote 1,606 lines and broke five other pipelines' re-merge checks, which compare bytes. SoapplyIdentitynow keeps fixed positions (lineage records in one block after the ones other pipelines pin to the front,directoriesandlgbtq.policies; new top-level keys beforedirectories,lineage,trends, andreported).sync-data's output andmerge-identity's are byte-identical, and a test fails if they drift. - Owner, once: the repository already allows auto-merge and
COLLEGE_REPORTED_TOKEN(Contents and Pull requests, read and write) covers this workflow too, since the token is per repository. The first run happens the first morning the probe is 28 days old (2026-11-01), or by hand withforce.
2. Older icon formats (built 2026-10-04)
Problem. The icon step read ICO entries that are PNG or 32-bit BMP and skipped the rest, so 42 colleges whose only icon was an older ICO got no mark.
Design. Decode the remaining BMP depths ICO files use: 24-bit (no alpha: transparency from the AND mask), and 8-, 4-, and 1-bit palette entries (colors from the palette, transparency from the mask). The size, ink, and shared-image rules are unchanged, so a 16 px favicon is still rejected. Then re-run the icon step for the colleges that had an unreadable ICO.
As built (2026-10-04). bmpToRgba in scripts/lib/brand-icons.mts decodes 24-, 8-, 4-, and 1-bit entries, rows
padded to 4 bytes for both the image and the AND mask; 16-bit and compressed (RLE, BITFIELDS) entries stay skipped,
and a mark's recorded format names its depth (ico/bmp24). Tests build tiny ICO files in memory (a 24-bit entry with
a masked corner, an 8-bit palette entry, 4- and 1-bit entries, and a 16 px 32-bit entry beside a 64 px 8-bit one,
which must win) and check exact pixels; each guard was broken once and failed. The real run re-tried every college
without a mark whose candidates include an ICO (690, 2,362 requests): one gained a mark (Sweet Briar College, from
a 238 x 256 24-bit entry), and "undecodable" no longer appears among the reasons. Old ICO favicons are mostly 16–48 px:
they decode now and fail the 64 px floor, the honest reason. A few .ico URLs served one hosting template's default
PNG, identical across unrelated colleges, which the shared-image rule already rejects. No mark was removed by hand.
3. The visit-page picker run (deferred)
Problem. 492 colleges have neither a visit page nor a virtual tour: their admissions page links to one with wording the scorer doesn't recognize, or not at all.
Design. Already built and tested behind a flag (links.md, "As built: the probe"): the admissions page's links go to Haiku, which answers with one URL or "none". What's left:
- Before the first run, make the probe keep a picker find while its page still answers: today a monthly re-probe
without
--pickerwould replace it with the scorer's miss. - One run over the colleges without a visit page:
npm run probe-sites -- --picker(only colleges without a visit page), capped at about $2. Estimate: $0.003 a college, about $1.50. - Look at a sample of its picks before they publish; a wrong pick is worse than none.
Why deferred. It's the one paid step. The owner runs it when ready.