Lesson 8 of 8
Unit · Someone else runs it
The case study is the thing you are reading
Twenty datasets from seventeen seeded scripts that regenerate byte-identically, twenty test files and 4,212 assertions, seven checks that fail the build, and a schema that refuses non-synthetic data. Every claim in this course is checkable in this repository.
Why the platform is the example
Every other course here uses a dataset. This one uses the repository, for a reason worth stating: a reproducible workflow is a claim about an artefact, and the only convincing demonstration is an artefact you can check.
Everything below is verifiable from a clone.
Raw data that is generated, not collected
pnpm datasets:generate
git status --short apps/data-analysis/public/datasets/files
# (nothing)
Twenty CSV files, seventeen generator scripts, and regenerating all of them changes
nothing. Each generator is seeded — SEED = 20240622, in the file — so the output
is a function of the code alone.
That is a stronger property than “the pipeline reruns”. It means rerunning is a
verification step: if git status is not empty after a regeneration, either the
generator changed or the committed file was edited by hand, and both are things you
want to know.
And the defects are deliberate. Every entry in a dataset’s knownIssues list
corresponds to a block in its generator — the four spellings of Nord-Ouest, the eleven
unit errors, the two duplicated students. Change one and you change the other,
which is the same discipline as the figures being generated from the data.
Committed output, for a reason that names its condition
The repository commits things it could rebuild: dataset CSVs, course PDFs, slide decks, figures, notebooks.
Because the deploy runs on Cloudflare Pages with no TeX, no Python and no pandoc. The build copies static files, so LaTeX is never on the deploy critical path and a contributor without the toolchain can still ship a content change.
That is the general rule from lesson 1, applied. Commit a generated artefact when rebuilding it is not available to everyone who needs it. Not because rebuilding is slow — because rebuilding is unavailable.
And it works because the output is deterministic. SOURCE_DATE_EPOCH pins
pdfTeX’s timestamp and pandoc’s, so a .pptx — a zip whose entry times would
otherwise churn — rebuilds byte-identically. Without that, committing generated
output would produce a diff on every build and nobody would review any of it.
Checks in three places, and the constraint that decided where
| Where | What | Why there |
|---|---|---|
| Zod schema | Field types, enums, required fields | Runs on one entry, no filesystem |
astro build |
Seven graph-level checks | Needs the whole collection |
pnpm test |
Filesystem checks | The build has no node:fs |
The Astro build prerenders inside a Cloudflare worker with no node:fs, so a
check that has to look at a file on disk cannot live there. dataset-files.test.ts
asserts that declared rows and bytes match the files; course-pdfs.test.ts asserts
that every published course ships both PDF editions.
Put the check where it can run. That is a real engineering constraint, and it produced a better arrangement than tidiness would have: the fast checks are in the schema, the graph checks are in the build, and the filesystem checks are in a test suite that runs first in CI.
The check that a reference graph cannot express
REFERENCE_RULES verifies that a declared slug resolves.
It cannot verify that an entry is pointed at.
So a course could ship with no lab and no exercise, and every gate would stay
green. That is what course-practice.test.ts exists for — counting backwards from
each published course to the practice that must name it.
Any “every X has at least one Y” rule needs a test of that shape, and recognising which of your constraints are of that kind is most of designing the check suite.
What the repository refuses to hold
.refine((d) => d.dataQuality.synthetic === true, {
message: "Only synthetic or fully de-identified datasets may be published.",
})
A dataset that is not declared synthetic fails the build. This audience models beneficiary, protection, GBV and clinical data, and “no record traces to a real person” is made a property of the repository rather than a promise in a note.
It pairs with generating the data rather than collecting it. The guarantee is structural at both ends: the generator cannot produce a real person, and the schema will not accept a claim that it did not.
Reproducibility at the level of the language
{ "packageManager": "pnpm@11.9.0", "engines": { "node": ">=22" } }
The package manager is pinned, not just the packages. A different pnpm resolves the lockfile differently, and that is the layer below the one most projects pin.
TypeScript is held at 6.x with the reason written down: TypeScript 7 does not yet
expose the programmatic API astro check depends on, so pnpm typecheck fails
outright on 7. The pin names the condition for removing it, which is what
separates a considered constraint from an inherited one.
What this repository does not do
Being honest about the gaps is part of the case study.
No container. A lock file plus a pinned package manager reproduces the JavaScript
side; the Python and TeX sides depend on what the contributor has. pnpm figures:build needs only a stock Python, and pnpm course:pdf warns rather than
failing when TeX is absent — a deliberate trade so a contributor without a toolchain
can still ship content.
No end-to-end determinism test in CI. The dataset regeneration is deterministic and verified by hand; there is no CI job that regenerates everything and fails on a diff. That would be the next thing to add.
Handover documentation is thin. CLAUDE.md and docs/DEFINITION_OF_DONE.md
carry the decisions, which is more than most repositories have and less than lesson 7
asks for.
Naming the gaps is the point. A case study that claims everything is a case study nobody can learn from, and the three above are the honest state of a repository that is otherwise unusually strict with itself.
Report it whole
Reproducibility of this platform
Data 20 CSV files generated by 17 seeded scripts. Regenerating all
of them produces byte-identical output; `pnpm datasets:generate`
followed by `git status` is the verification.
Checks 7 fail the build (schema, references, locale, translation
parity, topic-sector, synthetic-only, programme spine).
20 test files and 4,212 assertions run first in CI.
Output Course PDFs, slide decks, figures and notebooks are committed
and deterministic. SOURCE_DATE_EPOCH pins pdfTeX and pandoc.
Environment pnpm 11.9.0 and Node 22 pinned. TypeScript held at 6.x with
the reason and the removal condition documented.
Refused A dataset not declared synthetic fails the build.
Not done No container; no CI job that regenerates everything and fails
on a diff; handover documentation thinner than this course
asks for.
The last row is the one to copy into your own version. A reproducibility statement without a “not done” section is a statement nobody has audited.
What comes next
That is the course, and it is the last one in the programme’s spine.
The thread through all twenty is the same. A number in a programme report is a claim, and everything this platform teaches is about what has to be true for the claim to hold — a denominator you can defend, an interval that says how sure you are, a comparison group that is one, a chart that does not say more than the data, and a pipeline that produces the same answer when someone else runs it.
The last of those is the one that makes the others checkable. An analysis nobody can rerun is an analysis whose other properties have to be taken on trust, and this audience is judged on defending a number in a review rather than on producing it.