Receipts

This site says you should be able to show your harness. This is ours, and anything failing shows here as failing.

Who wrote it
Agents wrote all of the code and all of the prose on this site.
Who directed it
One person, Quang, told them what to build.
Who reviewed it
Machine checks and other agents. No person has read every line.
1

Every check, from the last real run

Read from src/data/receipts.json, which bun run check writes when it finishes. Nobody types these in. A failure stays on this page until a later run passes.

All 18 checks passed

Ran 5 November 2026, 22:25 UTC. The whole run took 17 min 2 s.

bun
1.4.2
node
26.7.0
typescript
5.9.3
biome
2.6.5
vite
8.3.6
playwright
1.64.1
wrangler
4.157.0
react
19.3.1
  1. Export for agents and crawlersPass
  2. The buildPass
  3. TypesPass
  4. Types in scripts and testsPass
  5. Vocabulary lintPass
  6. LintPass
  7. Unit testsPass
  8. Mutation test of the instrumentsPass
  9. LinksPass
  10. Interview roomPass
  11. Review gym tells the truthPass
  12. The kit, installed and attackedPass
  13. Quotes have sourcesPass
  14. Figures on the experiment pagePass
  15. Who said whatPass
  16. Production buildPass
  17. Browser sweep of every routePass
  18. The shared UI, used end to end at 1440 and at 390 with touchPass
  1. Pass

    Export for agents and crawlers

    bun scripts/export.ts

    Writes the glossary, llms.txt, sitemap, robots.txt and skill. A pass means it ran. Nothing here reads what it wrote.

    48 ms

  2. Pass

    The build

    bun scripts/check/project.ts

    stages
    18
    ran
    18

    24 ms

  3. Pass

    Types

    bun run typecheck

    The app’s own TypeScript, with strict mode and unchecked index access on.

    errors
    0

    7.5 s

  4. Pass

    Types in scripts and tests

    bunx tsc --noEmit -p tsconfig.scripts.json

    The scripts and tests, which the app’s config leaves out.

    errors
    0

    5.4 s

  5. Pass

    Vocabulary lint

    bun scripts/check/words.ts

    Shape and style: length limits, banned words, every promised bench present. It can’t tell whether a definition is right.

    problems
    0
    terms
    526
    benches
    373

    39 ms

  6. Pass

    Lint

    bunx biome ci . --max-diagnostics=60 --colors=off

    Biome’s recommended rules, with four turned off and two single-file exceptions. scripts/check/all.ts gives the reason for each.

    errors
    0
    warnings
    19
    files
    268

    460 ms

  7. Pass

    Unit tests

    bun test scripts tests src

    Logic only. Nothing in this step opens a browser.

    passed
    676
    failed
    0
    files
    20

    4.6 s

  8. Pass

    Mutation test of the instruments

    bun scripts/check/mutation.ts --min 90

    Covers src/bench/metrics.ts and src/bench/mutate.ts and nothing else. The chapters’ own logic isn’t mutation tested here.

    score
    94.3%
    mutants
    174
    killed
    164
    killed by a test
    147
    killed by the time limit
    17
    survived
    10
    didn’t parse
    6
    targets
    src/bench/metrics.ts, src/bench/mutate.ts
    The 10 mutants that survived
    • src/bench/metrics.ts:53, < became <=
      while (j < line.length && line[j] !== c) j += line[j] === '\\' ? 2 : 1
    • src/bench/metrics.ts:60, < became <=
      while (j < line.length && /[\w.]/.test(line[j]!)) j += 1
    • src/bench/metrics.ts:67, < became <=
      while (j < line.length && /[\w$]/.test(line[j]!)) j += 1
    • src/bench/mutate.ts:33, < became <=
      while (i < source.length) {
    • src/bench/mutate.ts:41, < became <=
      while (i < source.length && source[i] !== '\n') i += 1
    • src/bench/mutate.ts:51, < became <=
      while (i < source.length && source[i] !== c) i += source[i] === '\\' ? 2 : 1
    • src/bench/mutate.ts:57, 1 became 2
      if (c === '/' && !/^[\w$)\]]$|^string$/.test(previous.slice(-1) === '' ? '' : previous === 'string' ? 'string' : previous.slice(-1))) {
    • src/bench/mutate.ts:60, < became <=
      while (i < source.length && (source[i] !== '/' || inClass)) {
    • src/bench/mutate.ts:67, < became <=
      while (i < source.length && /[a-z]/.test(source[i]!)) i += 1
    • src/bench/mutate.ts:124, <= became <
      if (all.length <= max) return all

    31.0 s

  9. Pass

    Interview room

    bun scripts/check/interview.ts

    questions
    60
    verbatim
    6
    criterion
    19
    reasoning
    35
    problems
    0

    35 ms

  10. Pass

    Review gym tells the truth

    bun scripts/check/gym.ts

    problems
    0
    exercises
    21
    real
    6

    2.9 s

  11. Pass

    The kit, installed and attacked

    bun scripts/check/kit.ts

    passed
    116
    failed
    0
    gaps
    6
    not run
    10

    4 min 40 s

  12. Pass

    Quotes have sources

    bun scripts/check/facts.ts

    A pass means every quote has a URL. This step doesn’t fetch the page or look for the words. The facts check section does.

    fetched in this step
    no
    quotes
    1078
    with no source
    0
    last fetched
    5 November 2026, 21:04 UTC
    verified
    1078
    not found
    0
    fetch failed
    0

    43 ms

  13. Pass

    Figures on the experiment page

    bun scripts/check/experiment-page.ts

    problems
    0
    files
    7
    terms
    29
    typed figures
    13

    331 ms

  14. Pass

    Who said what

    bun scripts/check/lineage.ts

    problems
    0
    nodes
    41
    wires
    18
    misquotes
    30
    quotes
    115

    45 ms

  15. Pass

    Production build

    node_modules/.bin/vite build

    Prerenders every page. A page that throws while prerendering fails the build.

    prerendered
    579

    7.2 s

  16. Pass

    Browser sweep of every route

    node scripts/check/site.mjs

    Chromium at 1440 and 390, against the production build under wrangler. Each bench gets one press of its first control.

    routes
    553
    widths
    1440, 390
    page loads
    1106
    benches
    746
    benches kept their height
    746
    benches with no control
    4
    largest jump on mount
    1 px
    internal links
    582
    share images
    19
    failures
    0

    7 min 22 s

  17. Pass

    The shared UI, used end to end at 1440 and at 390 with touch

    node_modules/.bin/playwright test -c tests/e2e/playwright.config.ts --reporter=line

    passed
    280
    failed
    0
    skipped
    76
    flaky
    0

    4 min 1 s

2

What the checks caught

A ratchet only turns if each mistake leaves something behind. This is the log of real things that went wrong while building this site, what noticed, and what changed. Of these 17, 4 were caught by a check we had already written. The rest needed something else, and that is worth knowing before you trust a green panel.

A check we had written
4
Comparing with the real thing
2
An agent looking at the screen
1
Opening the source
1
Results that looked wrong
2
We ran into it
6
A person reading the field
1
  1. The live site broke a rule no local check could see

    What went wrong

    Cloudflare Web Analytics is on for the dqnotes.com zone, so Cloudflare adds its beacon script to every page at the edge. Our Content Security Policy blocked it, which put a console error on every page, but only in production.

    What caught it

    We ran into it. Opening the live site in a browser straight after the first deploy. Every local check, including the browser sweep of every route, had passed, because the script is never there locally.

    What changed

    The policy now allows exactly the beacon's two origins, with the reason in public/_headers. scripts/check/live.mjs opens the live site after every deploy, and the deploy script now runs the full check first: the first deploy script skipped it.

    Still open. Anyone can still run wrangler deploy by hand and skip both, and CI has no deploy job.

    public/_headers, scripts/check/live.mjs, package.json

  2. Our own answer key failed our own test

    What went wrong

    The experiment’s reference solution for the comment renderer failed the hidden scenario “a quote cannot break out of the link”. The solution was right: it escaped the quote. The scenario’s regex matched the encoded quote anyway, so any program that escaped correctly would have been scored as broken.

    What caught it

    A check we had written. The instrument test, which requires every reference solution to pass every scenario of its own task before any agent is measured.

    What changed

    The scenario’s regex, which now only matches a real break out of the href. The test stays, because the experiment’s scores are only as good as its scenarios.

    tests/experiment.test.ts, experiments/tasks.ts

  3. The experiment kept restarting the dev server

    What went wrong

    The experiment’s temporary workspaces lived in the repo, each with its own tsconfig.json. Vite logged “changed tsconfig file detected”, cleared its cache and reloaded every time one appeared, and server-rendered pages answered 500 with “Cannot read properties of null (reading 'useContext')”.

    What caught it

    We ran into it. Pages failing on the dev server, and the server’s own log, which named the path experiments/.work.

    What changed

    The workspaces moved to the system’s temp folder, and the Vite config now tells its watcher to ignore experiments, docs, shots and scripts.

    experiments/run.ts, vite.config.ts

  4. The first task set was too easy

    What went wrong

    Claude Haiku 5.5 passed nearly everything from a vague brief, because the tasks were famous problems whose function signatures gave the answer away. An experiment about wording would have found nothing, for a boring reason.

    What caught it

    Results that looked wrong. The first results, which were too good to tell the conditions apart.

    What changed

    We replaced them with tasks that have traps in them, and kept two of the originals as controls. Choosing harder tasks after seeing results is a real weakness in the experiment, so it has to be disclosed alongside the numbers.

    experiments/tasks.ts

  5. Our reviewer prompt was biased, by us

    What went wrong

    The first reviewer prompt asked it to find a real defect. In a separate test, one correct function reviewed 40 times was approved 33 of 40 by a neutrally worded prompt and 1 of 40 by ours. A buggy function was rejected 40 of 40 under both, so on that function the harsher prompt caught nothing extra.

    What caught it

    Results that looked wrong. The reviewer gate’s results looked strange, and that is the only reason anyone tested the prompt. No check would have flagged it.

    What changed

    A neutral re-review of the recorded runs, published next to the original: of 18 broken and 222 good one-shot programs, the neutral prompt stopped 13 and 39, and ours stopped 16 and 126. The two prompts differ by more than one sentence, and this is one small model on one-function tasks, so the size of the gap is not a general result.

    public/data/review-flake.json, experiments/review-flake.ts, experiments/re-review.ts

  6. The kit’s tests were weaker than they looked

    What went wrong

    The bench kit includes a real mutation tester, so we pointed it at itself and at the metrics. The first score was 83.9%: 146 of 174 mutants killed and 28 alive.

    What caught it

    A check we had written. scripts/check/mutation.ts, running each mutant against the tests in its own throwaway copy.

    What changed

    More tests. It is now 94.3%, 164 of 174, with a floor of 90% in the check, taking 31.0 s on the last run. 10 survive. All 10 look equivalent, judged by reading them and not by proof: eight turn < into <= on a loop bound, where the extra pass changes nothing.

    Still open. 6 mutants don’t parse, so they aren’t counted. On the last reading, 4 of the 6 come from a real gap: the tester reads ** as two separate * tokens and turns one into /, so it can’t mutate the exponents in the crap function. Those exponents have tests, and no mutant tests the tests. The other 2 are the angle brackets of a generic type, read as comparisons.

    scripts/check/mutation.ts, src/bench/mutate.ts, src/bench/metrics.ts

  7. Two gaps in the security policy that a console wouldn’t show

    What went wrong

    The first policy left out blob: in script-src, which the reading chapter’s benches need to load real modules inside their workers. Separately, without 'unsafe-eval' a sandboxed bench fails with nothing in the console: each run quietly returns an error.

    What caught it

    A check we had written. The browser sweep, through one failing reading-chapter bench. That one bench is the only reason the blob: gap was found.

    What changed

    blob: went in, each allowance was tested by removing it from a production build, and the reasons went into public/_headers. The sweep now asks directly, by starting a blob: worker and seeing whether new Function runs in it.

    public/_headers, scripts/check/site.mjs

  8. Old pages kept coming back

    What went wrong

    Vite only empties an output folder that is inside the repo. Building into a scratch folder left the old pages there, the prerenderer was served one of those old pages, and it saved that page straight back into the new build.

    What caught it

    We ran into it. Building into a scratch folder so that the sweep couldn’t disturb anyone. Vite’s own warning about this was in the build output.

    What changed

    scripts/check/all.ts passes --emptyOutDir whenever it builds outside the repo.

    scripts/check/all.ts

  9. What every automated check passed, and a look did not

    What went wrong

    Simulation labels nobody could see. A note saying “on the left”, which was wrong on a phone, where the panel it meant sits above it. In the backpressure chapter’s review of its own benches, a note saying a neutral reviewer approved 40 of 40, when the recorded figure is 33.

    What caught it

    An agent looking at the screen. Agents opening their screenshots at 1440 and 390 and reading them. The sweep measures overflow and console errors. It has no opinion about whether a sentence is true.

    What changed

    Each was fixed where it was found. Looking is in the definition of done for a chapter, and that is a rule in a file: it depends on an agent doing it, and nothing checks that one did.

    AGENTS.md, scripts/shoot.mjs

  10. Chapter engines, held against the real tools

    What went wrong

    The git chapter’s worktree bench said agent B had nothing left to commit. Real git showed B does commit, and agent A’s commit lands on the branch called cart, carrying B’s half-done file.

    What caught it

    Comparing with the real thing. Running the same commands in real git 2.54 beside the bench. The bench shell got the same treatment against real bash and now agrees on 158 of 158 lines, after differences in ls argument order, ls -d and PATH= ls. The type bench’s small checker is compared with real tsc on every program it shows.

    What changed

    The note and its panel were corrected, and the shell’s differences were fixed. Only the tsc comparison is a test that runs every time. The git and bash ones were scripts run while writing those chapters.

    src/parts/backpressure/checker.test.ts, src/parts/git/benches.tsx, src/parts/machine/benches.tsx

  11. Quotes that were close enough to look right

    What went wrong

    A Hashimoto quote cut off mid-sentence. “Only exit 2 blocks”, out of date for Claude Code hooks since the onFailure setting in v2.1.295. A pstack line credited to encode-lessons-in-structure when it comes from the skill called correct. Our own research file misquoted Elicit and Assembled, and a chapter trusting it would have repeated both.

    What caught it

    Opening the source. Agents opening the primary source for each claim before writing it, as AGENTS.md asks.

    What changed

    The chapters carry the words on the source’s page. Corrections that override the research files now sit in AGENTS.md.

    AGENTS.md, src/parts/backpressure/words.ts, src/parts/proof/words.ts

  12. A plausible explanation that was wrong

    What went wrong

    The security chapter explains why 7 of 29 recorded comment renderers from vague briefs produce a javascript: link. (Its recorded alert(1) gets cut at the first closing bracket and doesn’t run. A payload with no brackets ran, from the same 7.) The first explanation written down was a guess, and it was wrong. Those programs do allowlist schemes. They fail because they treat a link with no visible scheme as relative, and a control character hides the scheme from their regex.

    What caught it

    Comparing with the real thing. Checking the guess against the recorded programs before it shipped.

    What changed

    The chapter states the cause the recordings show.

    src/parts/security/recorded.json, src/parts/security/logic.test.ts

  13. One agent’s clean-up reached across the machine

    What went wrong

    To clear a hung command of its own, an agent ran pkill -f "bun test". That pattern matches every process on the machine, so it may have killed other agents’ test runs. The same agent ran rm -rf on a shots folder in the shared scratch space, where another agent’s script was also writing.

    What caught it

    We ran into it. The agent’s own end-of-task report, which said so. We then checked whether the experiment was contaminated. It wasn’t: its last run was recorded at 09:54 UTC and the pkill was at 10:11 UTC. Three attempts with hanging tests had looked suspicious, and they reproduced on their own, so those tests really hang.

    What changed

    A rule in AGENTS.md: no pkill, killall or rm -rf on anything you didn’t create, and scratch files go in your own subfolder.

    AGENTS.md, experiments/results/main.jsonl

  14. One bad sed took down every page

    What went wrong

    A bad sed put a syntax error in one chapter’s words.ts. Every page imports every chapter’s words, so every route on the shared dev server failed for a minute or two, for every agent working at the time.

    What caught it

    We ran into it. The agent that did it reported it. Other agents reported the same kind of site-wide 500 from syntax errors in other chapters.

    What changed

    A rule in AGENTS.md: typecheck right after a bulk edit, and never run a blind sed over a words.ts.

    Still open. That’s a line in a file, and the same file says a line is the last resort. No check stops this happening again, and a per-chapter error boundary would.

    AGENTS.md, src/parts/index.ts

  15. A test result landed on the wrong bug

    What went wrong

    The git chapter’s bisect bench runs each test in a Web Worker and applies the answer when it comes back. Its check that the answer still applied compared only the midpoint and the size, and every new bug of the same size starts at the same midpoint. Press New bug while a test was running, and the old bug’s verdict was marked on the new search: one test counted that nobody ran on it, and a search that could now blame the wrong commit.

    What caught it

    A check we had written. Biome’s useExhaustiveDependencies flagged the effect for leaving out runTest and reset. Those two omissions were harmless. Reading the effect to fix them is what found this, and slowing the Worker in a production build showed it happening: Tests so far read 1 on a search that had run nothing.

    What changed

    An answer now counts only if the search that asked for it is still the current one, and a test whose bug has gone says nothing at all.

    Still open. No test holds this in place. The chapter has no component tests, so the fix was checked by hand in a browser, at 1440 and at 390.

    src/parts/git/benches.tsx, biome.json

  16. A whole topic was missing, and nothing flagged it

    What went wrong

    The course taught tests from examples to properties and stopped there. Model checking, formal verification and statecharts had no terms, and neither did memory safety. Eighteen chapter agents, a research pass and a layman’s review all worked on this site, and none of them said so.

    What caught it

    A person reading the field. The owner, reading three posts from early November 2026, saw people arguing about formal verification and registries in words the course didn’t have.

    What changed

    The tests chapter now climbs from examples to properties, to every state, to proof, with a real model checker, an exhaustive verifier that proves the wrong spec, and a statechart. Memory safety, code registries, review in the abstract and reading a forecast have terms too.

    Still open. No check can catch this kind of gap, and we have not written one that pretends to. Every check here compares the site with something already written down: a source, a rule, a list of terms. None of them knows what should be there and isn’t. That still takes a person who reads the field, and nothing tells us when the next topic is missing.

    src/parts/tests/words.ts, src/parts/tests/verify.tsx, src/parts/tests/verify-logic.test.ts, docs/curriculum.md

  17. Following the course for real broke the kit its own check had passed

    What went wrong

    On 5 and 6 November 2026 an agent built the /project app from an empty folder with a real model, using the kit as a reader downloads it, a chapter at a time. Four things in the kit were broken. At chapter 9 the check script ran no tests, because its tests gate came with chapter 10’s files. Chapter 6’s deny rules covered all of .claude/hooks/, so the agent couldn’t add chapter 9’s stop hook. The stop hook sent the agent back with the same words until Claude Code’s limit, and its final message was empty. ralph.sh called green checks done with every plan item still open. Two more were nearly as bad: the loop’s agent had no permission to commit, which the loop prompt asks it to do, and AGENTS.md said CI runs every gate in downloads that had no CI at all. Running the chapter 9 prompt again on the fixed kit found one more: early downloads’ AGENTS.md, ratchet log and package.json named scripts those downloads didn’t have yet.

    What caught it

    We ran into it. The agent ran into each one and worked round it in its own prompts, so the files a reader downloads still had them. The kit’s check rendered every chapter’s download and ran the whole one and two partial ones, but never a chapter’s download on top of an earlier one, never a red suite held to Claude Code’s limit, never a plan left open with the checks green, and never a loop pass inside the real Claude Code program.

    What changed

    Each is fixed in both stacks, and each has a check in scripts/check/kit.ts that was run on the old files first and failed there: the chapter 9 check has to run a test and refuse a failing one, the deny rules are tried against every later chapter’s new files, the stop hook has to ask for a final message on its 8th block in a row and still block, ralph.sh has to keep going while an item is open, a loop pass has to commit inside real Claude Code, AGENTS.md may mention CI only where the download’s CI runs every gate, and a download’s AGENTS.md, ratchet log and package.json may name only files it has. KIT_TEMPLATES lets anyone rerun the check on an old copy.

    Still open. The stop hook’s final message and the loop’s commit were seen with a script standing in for the model, inside the real Claude Code program. Nobody has yet seen a real model given that last block. The /project page keeps the run as it happened, with a note under each stage saying what changed since.

    scripts/check/kit.ts, src/kit/verified.json, src/project/verified.json, src/kit/templates/both/.claude/hooks/stop-gate.sh.tmpl, src/kit/templates/both/scripts/ralph.sh.tmpl

What these checks can’t see

Whether an explanation is right
A definition can pass every check and still be wrong. Nothing here understands the sentence. Most of the mistakes in the log above were found by something other than a check.
Whether a quote is fair
The facts check confirms that the words are on the page. It can’t see a quote that’s accurate and misleading, or a source that is itself wrong.
Quotes outside one field
Only a term’s seen field is searched, and only for quotes of 12 characters or more. Words in a bench’s notes or a term’s other fields aren’t fetched against anything.
What a link says
A status under 400 means something answered. It doesn’t mean the page still says what we cite it for.
Most of a bench
The sweep presses the first control of each bench once. Every later control, and every combination of them, is unvisited by the sweep.
Real browsers and real hands
The sweep is Chromium with emulated touch. Safari, Firefox, a real phone and a screen reader aren’t in it.
The chapters’ logic under mutation
Mutation testing covers two files of the kit. A chapter’s tests could be as weak as the kit’s first ones were, and this score wouldn’t move.
What nobody asked for
The experiment’s own lesson applies to this site too: a mechanical gate can’t catch a requirement no one wrote down.
Comparisons that ran once
The bash and git comparisons in the log were scripts an agent ran while writing the chapter. They aren’t in bun run check, so nothing would notice if those engines drifted. The tsc comparison is a real test and runs every time.
3

The facts check

Every quote on this site is meant to be verbatim, and someone may repeat one to an interviewer. So a script fetches each source and looks for the words. It needs the network, so it isn’t part of every run, and what it last found is published here with its date.

Quotes

1078 fetched, 5 November 2026, 21:04 UTC.

verified: the words are on the page
1078
not found: the words aren’t in what we were given
0
fetch failed: at least one source couldn’t be read
0

Links

654 fetched, 5 November 2026, 21:04 UTC.

answered
644
refused the robot, so not verified
10
couldn’t be reached
0
dead
0
What verified means
The exact words are in the HTML of one of the term’s sources, ignoring case, spacing and typographic quotes. It says nothing about whether we quoted them fairly, or whether the source is right.
What not found means
The words aren’t in the HTML we were given. That can be a real misquote. It can also be a page built by JavaScript, a citation of a GitHub folder when the words are in a file inside it, a quote from another source than the one listed, or our own phrasing in quotation marks.
What it doesn’t mean
Not found isn’t proof of a misquote, and nobody has cleared this list. Each row still needs someone to open the source. Fetch failed proves nothing either way: mostly sites that answer a script with 403, and PDFs this check can’t read.

Still unresolved

Show
  1. 403

  2. 403

  3. 403

    doi.org/10.1145/356635.356640

    Cited by 2 terms: performance/measure-first, performance/premature-optimisation

  4. 403

    doi.org/10.1287/orsc.2025.21838

    Cited by 1 term: model/jagged-frontier

  5. 403

    doi.org/10.1561/1500000019

    Cited by 1 term: context/rag

  6. 403

    openai.com/index/harness-engineering/

    Cited by 29 terms: agent/agents-md, backpressure/error-message-as-prompt, backpressure/false-positive-check, backpressure/linter, backpressure/quality-gate, backpressure/structural-test, context/agent-readable, context/codebase-map, context/docs-in-repo, context/instruction-budget, context/progressive-disclosure, context/repo-as-truth, design/boundary, design/coupling, design/good-code, design/layered-architecture, design/slop, design/technical-debt, many/dark-factory, many/doc-gardening, many/entropy, many/long-running-agent, many/merge-queue, many/software-factory, proof/explain-tradeoffs, review/review-bottleneck, ship/environment, ship/logs-metrics-traces, ship/observability

  7. 403

    openai.com/index/sycophancy-in-gpt-4o/

    Cited by 1 term: model/sycophancy

  8. 403

  9. 403

    steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04

    Cited by 1 term: agent/autonomy-slider

  10. 403

    w3.org/TR/png-3/

    Cited by 1 term: machine/binary

4

Numbers about the build

Counted from the chapters each time the site is built, except where it says the last run.

chapters
18
terms
526
benches
373
gate questions
108
drills
55
sources cited
1125
unit tests on the last run
676
routes swept on the last run
553

Cost and time

The one real figure in this repo: the main experiment spent $2.51 on 1097 model calls across 480 runs, over 33 minutes starting 5 November 2026, 09:21 UTC. What building the site itself cost, in money or hours, isn’t recorded in this repo, so there is no number for it here.

By chapter

ChapterTermsBenchesGateDrills
The machine302263
The network312363
Version control292063
Reading and debugging322663
The model241663
The agent262163
Context201363
The brief261663
Backpressure211763
Tests372164
Design341863
Review241263
Security392963
Data and failure363263
Performance and cost302963
Shipping and running332063
Many agents241563
The proof302363
All of them52637310855
5

What isn’t proven

  • The CI file has never run on GitHub

    .github/workflows/ci.yml is written and its actions are pinned, and it has never been executed. It also holds the only bun audit, so the dependency audit isn’t part of what the first section reports.

  • Lefthook has never run in a real git repository

    lefthook.yml is written, and this working folder isn’t a git repository, so no hook has fired. The pre-commit job calls gitleaks, which isn’t installed on the machine this was built on, so that job will fail until it is. No secret scan has run.

  • No real user has been through this course

    Nobody has started at chapter 1 knowing nothing and come out able to defend their work. Whether it teaches what it says it teaches is untested.

  • No human has reviewed every line

    The checks ran over all of it and other agents reviewed parts of it. The one person directing them hasn’t read it all, and no outside reader has either.

Security posture

The policy on this site is weaker than one you would want on an app with accounts. Its allowances exist because the benches run real code in your browser. The defence is that the site takes no input from other people and loads one third-party script, Cloudflare’s analytics beacon, which Cloudflare adds to every page for this domain and the policy names by its exact origin. Each allowance below was tested by removing it from a production build in Chromium. The ways to tighten them are ideas and haven’t been built.

The header every prerendered page gets

default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https://static.cloudflareinsights.com; worker-src blob:; style-src 'self' 'unsafe-inline'; img-src 'self' blob:; font-src 'self'; connect-src 'self' https://cloudflareinsights.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

script-src 'unsafe-inline'

Why it’s there
TanStack Start writes a small bootstrap script into every page, and it differs from page to page.
Take it away and
The bootstrap is blocked and the page throws a TypeError.
To tighten it
One hash per page doesn’t fit the limit of 100 rules in the headers file. It would take a nonce or hashes from the Worker, which means waking the Worker for pages that are served statically today. Not tried.

script-src 'unsafe-eval'

Why it’s there
Benches run real code with new Function inside a blob: Web Worker, and a blob: worker inherits the page’s policy, so this can’t be allowed for the worker alone.
Take it away and
Nothing shows in the console. Every sandboxed run just returns an error.
To tighten it
Serve the sandbox as a same-origin worker file with its own policy, so pages can drop this. That means changing src/bench/run.ts and two chapters. Not built, so not tested.

script-src blob:

Why it’s there
The reading chapter’s benches load real modules and scripts from blob: URLs inside those workers, so that a stack trace names a real file.
Take it away and
Those requests fail with “csp”.
To tighten it
Change those benches so they don’t load from blob: URLs, and give up a stack trace that names a real file.

worker-src blob:

Why it’s there
The same benches create their workers from blob: URLs.
Take it away and
Creating a worker is refused.
To tighten it
The same worker file would let this become worker-src 'self'.

img-src blob:

Why it’s there
The performance chapter’s image bench shows the picture it just compressed from a blob: URL, which the page made itself.
Take it away and
The picture is refused and the bench shows a broken image.
To tighten it
Have that bench report sizes only and show no picture, which is most of what it teaches with.

style-src 'unsafe-inline'

Why it’s there
The @font-face block in the page head, and React’s style attributes on the server-rendered HTML.
Take it away and
The font block and every style attribute are refused.
To tighten it
Move the font block into a stylesheet and every style attribute out of the server-rendered HTML. Wire colours and bench layouts use them throughout, so this is the largest of the five. Not tried.

One gap in coverage. These headers come from public/_headers, which only applies to static files. A response the Worker makes itself doesn’t get them. On this site that means the 404 page for an address nothing was prerendered for. It runs no benches, and the only thing it prints back is the address, which React escapes. It still has no policy, and fixing that means setting the headers in the Worker as well.

6

Run it yourself

The repo isn’t public yet, so for now you can’t run these yourself. They’re here so you can see exactly what would run, and they’ll work as written once it is. Until then, the first section is the only evidence you can see, and you’re taking the files behind it on trust.

Set up

The same two steps CI uses.

  • bun install --frozen-lockfile
  • bunx playwright install --with-deps chromium

Everything

This runs every check in the first section and writes src/data/receipts.json, which this page reads, so rebuild afterwards to see your own result here.

  • bun run check

The two that need the network

These fetch every external source and rewrite the records in src/data. They aren’t part of every run. A rerun is what would clear or confirm the lists in the facts check section.

  • bun scripts/check/links.ts --external
  • bun scripts/check/facts.ts --external

Look closer

The first lists every mutant that didn’t parse. The second rewrites the bench height record, for when the sweep reports page jumps, and you rebuild after it.

  • bun scripts/check/mutation.ts --min 90 --invalid
  • bun run heights

The workshop

Name a word, or describe what you mean in everyday language.