R3a: breadth-to-ten live journey unlocks Compare@10 - #5
Merged
phibkro merged 9 commits intoJul 23, 2026
Conversation
Base of the stacked R3a branch (off F3 tip e64e427). Verbatim design-spec: one live run ingests all ten NTNU_PROGRAMMES (real NTNU + DBH 347/208, fail loud), reconciles + atomically publishes each; GET /v1/programmes then shows programmeCount=10 / compareEnabled=true, each live-DBH-provenanced, catalogue stays fixture-free, Compare unlocks at exactly 10 (9 locked). Per-programme publish-vs-reject report is required F2 evidence. Quality gate UNCHANGED. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The R3a live breadth run surfaced that replicateNtnuCatalogue expected a
top-level JSON array, but the real all-studies endpoint returns
`{ q, numFound, docs: [...] }`. processNtnu fetches the catalogue
unconditionally (full mode does not even use it), so the shape mismatch threw
before any programme was reconciled. Read `.docs`, keep a bare-array fallback,
still fail loud on any other shape. No test covered this path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finding A from the R3a live run: migration 0006 (F3 fixture cleanup) deletes the `no.ntnu` institution as collateral on a fresh DB — once the fixture courses are gone nothing pins it, and the guard drops it. But `institutions` carries NO source_provider (it is real reference data, not fixture provenance), and the live reconcile path inserts courses/programmes under a NOT NULL FK to it while never provisioning the institution itself. At the real deploy migration order (0001..0007 before any run) every programme reconcile FK-failed. 0007 re-asserts NTNU as permanent reference data (idempotent INSERT OR IGNORE), leaving reconcile and the atomic publication protocol UNCHANGED. The added test applies the full 0001..0007 set then reconciles — the exact deploy order — and fails if 0007 is dropped or 0006 ever deletes the institution again (verified: red without 0007, green with it). This also closes the gap in F3's falsifier #2, whose test happened to run 0006 after a reconcile (courses then pinned no.ntnu). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The suite proved the 10-programmes-unlocked side of the operator-locked "exactly ten" Compare gate but never the lock side. Add the 9-programme boundary: meta.compareEnabled === false and compareProgrammes fails CompareUnavailableError(available 9, required 10). Guards against a regression to `> 10` or `>= 9` that the unlock-only test would miss. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pure formatting (double-space→single after periods; markdown emphasis *x*→_x_; test whitespace). No contract or logic change. Keeps `bun run validate` green at the PR tip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold the conditional expect into a single toMatchObject on the whole Either (oxlint vitest/no-conditional-expect). Same assertion, lint-clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The real R3b deploy (first ever run) rejected the sync queue: Cloudflare Queues require message_retention_period between 60 and 86400 seconds, but alchemy.run.ts requested 345600 (4 days). tsc/local-compile never caught it because no deploy had run. Cap at 86400 (24h), the max. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Feature
Delivers the R3a breadth-to-ten journey on Cloudflare: live NTNU/DBH ingestion publishes 10 fixture-free programmes, preserves source/R2 provenance, and unlocks Compare at exactly 10 (with the 9-programme lock boundary covered). This stack also includes the deploy fixes and SOPS-backed credential wrapper used to put the API, ingest worker, and PWA live.
Experience it
Open the live PWA:
Drive the public API directly:
The live ingest worker is https://course-data-platform-ingest.philib-krogh-d23.workers.dev/; mutation endpoints remain bearer-token gated.
Real vs deferred
Real: Cloudflare Workers/D1/R2/Queue deployment, 10 live-published programmes, Compare@10, and encrypted deploy credentials; deferred: custom domain and F4 error-body namespace.