The errors this module expects, and how each one is caught. These are not marks of carelessness. Most of them are what a reasonable person produces on first contact with the material, which is why the module is built around catching them rather than warning against them.
The failure-mode codes are the module's fixed sets. The code column names
either a computable failure mode, which course/lab/check.py emits, or a
source-checking failure mode, which no program emits and a learner assigns --
see docs/AUTHORING.md section 3. Unit numbers and checkpoint ids come from
Summary
| # | misconception | code | unit | checkpoint |
|---|---|---|---|---|
| 1 | B1 is +1/2 |
b1-sign |
3 | cp06-convention |
| 2 | Ada's B7 is the modern B7 |
ada-indexing, odd-terms |
3, 7 | cp06-ada-index, cp08-table-evidence, cp08-diagnose, cp16-note-g |
| 3 | Decimal output is close enough | not-fraction |
3, 5 | cp08-bernoulli, cp08-table-evidence |
| 4 | The indices line up | off-by-one |
4, 5 | cp08-bernoulli |
| 5 | It works for the values I tried | raises |
5, 6 | cp08-bernoulli, cp10-spec-clauses |
| 6 | Correct code implies correct comments | — | 4 | cp07-trust-triage |
| 7 | The model knew, decided, tried | — | 2 | cp03-risk-triage |
| 8 | "The AI said so" is a reason | — | 8 | cp17-disclosure |
| 9 | Passing tests means understanding | — | 5, 8 | cp08-bernoulli, cp17-disclosure |
| 10 | Disclosure discharges responsibility | — | 0, 8 | cp00-contract, cp17-disclosure |
| 11 | Generated code can be run unread | import-error |
5 | cp08-bernoulli |
| 12 | Published means true | — | 7 | cp16-note-g |
| 13 | Nothing can really be known | — | 9 | — |
| 14 | AI cannot be trusted at all | — | 9 | related to cp07-trust-triage |
| 15 | Gödel/Tarski prove AI cannot verify itself | — | 9 | related to cp18-sort-the-claims |
| 16 | Attention, training, and retrieval are one truth-finding process | — | 2 | cp03-system-layers |
| 17 | The claim is true, so the citation is fine | misattributed |
13 | cp09-diagnose-source |
| 18 | The source is about this, so it supports this | right-source-wrong-claim |
13 | cp09-diagnose-source |
| 19 | A precise number is a better number | over-specified |
13 | cp09-diagnose-source |
| 20 | Reported together means one caused the other | unsupported-inference |
13 | cp09-diagnose-source |
| 21 | A real work means a real page | fabricated-locator |
13 | cp09-diagnose-source |
| 22 | The rule I found is the rule in force | stale-version |
13 | cp09-diagnose-source |
Errors in the numbers and the code
1. B1 is +1/2
Failure-mode code: b1-sign · Unit 6 · Checkpoint cp06-convention
What it looks like. A routine returns B0 = 1, B1 = 1/2, B2 = 1/6,
B4 = -1/30. Every value except B1 matches the oracle. The author is confident
and can point at a published source.
Why it is tempting. Because it is correct. The second Bernoulli convention has
B1 = +1/2, it is standard, it is published, and several common derivations
produce it naturally — the Akiyama–Tanigawa transform among them. Nothing about
the code looks wrong, because nothing about it is wrong. The specification was
incomplete.
How it is caught. check.py reports divergence at n=1 and then names
b1-sign, distinguishing a convention mismatch from an arithmetic error. The
learner meets it first in examples/wrong_b1_sign.py. cp06-convention presents
the output and asks what is going on, with a diagnostic for the tempting answer
"it has a sign bug", which it is not: a bug would not leave every other value
untouched.
What fixes it. Naming the convention in the specification, and therefore in the prompt. This is the first clause of the Unit 10 precise prompt.
2. Ada's B7 is the modern B7
Failure-mode codes: ada-indexing, odd-terms · Units 6 and 16 ·
Checkpoints cp06-ada-index, cp08-diagnose, cp16-note-g
What it looks like. A routine returns B0 = 1, B1 = 1/6, B2 = -1/30,
B3 = 1/42. Every value it produces is a genuine Bernoulli number; every one is
filed under the wrong label. Or, in prose: a claim that Note G computes the
modern B7, which is zero.
Why it is tempting. Lovelace numbers only the non-zero Bernoulli numbers, so
her B1, B3, B5, B7 are the modern B2, B4, B6, B8. Both schemes use the letter
B and a small number. Asked for "Ada Lovelace's Bernoulli numbers", a model may
answer faithfully in her notation, producing something that is historically
accurate and specification-breaking at the same time. A routine can fail by being
right about the wrong century.
How it is caught. check.py reports odd-terms — the odd indices are carrying
values instead of zeros — and then ada-indexing when the submitted B1, B2, B3
match the modern B2, B4, B6. The learner meets it in
examples/wrong_odd_terms.py. cp06-ada-index asks for the modern index of
Ada's B7; cp08-diagnose asks the learner to name the failure mode from output
alone; cp16-note-g requires the mapping to be applied before the historical
table can be compared. exercises/ex06_ada_translation.py (optional CS
extension) has the learner write the translation and reject labels that do not
exist.
What fixes it. Stating that bernoulli(n) is defined for every n >= 0 and
returns Fraction(0) for odd n > 1 — which rules out any scheme that numbers
only the non-zero terms. The mapping table is in course/historical-notes.md.
3. Decimal output is close enough
Failure-mode code: not-fraction · Units 6 and 8 ·
Checkpoints cp08-bernoulli, cp08-table-evidence
What it looks like. A routine returns -0.03333333333333333 for B4. A test
with a tolerance passes. B3 comes back as -1.1102230246251565e-16 rather than
zero.
Why it is tempting. Most Python numerical code uses floats, so most generated
Python numerical code does too. At small indices the output is convincing to look
at, and a comparison with a tolerance will confirm it. The error only becomes
visible where the denominators grow: B30 is 8615841276005/14322, and the
float is not equal to it.
How it is caught. check.py reports not-fraction at the first index where a
non-Fraction is returned, and separately reports odd-terms because the
rounding noise means the odd terms are not exactly zero.
examples/float_output.py demonstrates it. selfcheck.py explore has a float
command that prints the exact and floating-point values side by side, so the
learner can watch it fail rather than be told it does.
What fixes it. Specifying the return type: exact fractions.Fraction, never
float. Note that this is a specification clause, not a coding style preference
— an approximate comparison must choose a tolerance, and any tolerance loose
enough to pass rounding error is loose enough to pass a real bug.
4. The indices line up
Failure-mode code: off-by-one · Units 7 and 8 ·
Checkpoint cp08-bernoulli
What it looks like. bernoulli(0) returns -1/2, bernoulli(4) returns 0.
Every value returned is a real Bernoulli number, shifted one index early.
Why it is tempting. This is the failure mode that most resists reading. The code is clean, the arithmetic is exact, and a reviewer skimming for mistakes finds nothing, because there is no mistake to find in any individual line. The sequence simply starts in the wrong place — often because a loop was written to count non-zero terms, or because the recurrence was solved for the wrong term.
How it is caught. Only by comparing index by index against a reference.
check.py reports off-by-one when the submitted B_n equals the oracle's
B_(n+1) across several indices. examples/off_by_one_range.py demonstrates it,
and produces divergence, odd-terms and off-by-one together — worth reading
as one description of a single fault rather than three separate bugs.
What fixes it. Differential testing. There is no reading strategy that reliably catches this one, which is the point at which "I checked it carefully" stops being a method.
5. It works for the values I tried
Failure-mode code: raises · Units 8 and 10 ·
Checkpoints cp08-bernoulli, cp10-spec-clauses
What it looks like. A routine that returns correct values for n = 2 to 12
and raises IndexError or RecursionError at n = 0, n = 1, or n = 30.
Why it is tempting. The indices a learner tries first are the ones in the worked example, and those are exactly the indices the generated code was most likely to get right. The boundaries — the empty case, the first case, the large case — are where implementations break and where nobody looks.
How it is caught. check.py runs every index from 0 to --max-n (default 30)
and reports raises with the exact index and exception. The default range
deliberately includes B30, where recurrence bugs hidden by small values become
visible.
What fixes it. Specifying the valid range as part of the contract: defined for
every n >= 0. cp10-spec-clauses asks the learner to match this clause to the
failure it prevents.
Errors in reasoning about generated output
6. Correct code implies correct comments
Unit 7 · Checkpoint cp07-trust-triage
What it looks like. Generated code carries a comment such as
# Ada Lovelace used this same recurrence in Note G (1843). The code passes the
checker. The comment is accepted along with it, and ends up in a submitted piece
of work as an unsourced historical claim.
Why it is tempting. The two arrived together, in one response, in one voice. Having checked the part that was checkable, it feels as though the checking is done. There is also a reasonable-sounding inference available: something that got the hard part right is unlikely to get the easy part wrong.
How it is caught. cp07-trust-triage presents exactly this situation and asks
what to do with the comment, with a diagnostic for the tempting answer. The point
made in the explanation: code and historical claims are produced by the same
process and fail independently. The arithmetic being checkable is precisely why
it got checked, and the history did not.
What fixes it. Treating a generated response as a mixture of things with
different failure modes, and matching the check to each: arithmetic you can run,
assertions about the world you can only look up. Any historical claim that
survives into your work needs a citation in course/references.md or an
explicit uncertainty label.
7. The model knew, decided, tried
Unit 3 · Checkpoint cp03-risk-triage
What it looks like. "It knew the recurrence but got confused about the convention." "It was trying to be helpful." "It thought I meant Ada's numbering." Also the mirror image: "it lied", "it made that up on purpose".
Why it is tempting. The output is fluent, contextual and responsive, and the vocabulary we have for describing fluent responsive text is the vocabulary of minds. It is also genuinely difficult to describe model behaviour without it, so the anthropomorphic phrasing arrives before anyone has decided to use it.
Why it matters. It is not a manners problem. Attributing knowledge and intention produces bad predictions about when the output will be wrong. If the model "knows" things, then an error is a lapse, and asking it to try harder should help. If it produces statistically plausible continuations, then a fluent false answer is the same kind of event as a fluent true one, no amount of re-prompting makes the output self-verifying, and the model's own confidence carries no information. The second description tells you to go and check; the first tells you to ask again.
How it is caught. cp03-risk-triage requires the learner to sort outputs by
how they can be wrong rather than by how convincing they look, which cannot be
done from an intentional description. Unit 3 supplies the alternative vocabulary,
and course/glossary.md defines next-token prediction, plausibility and
hallucination in behavioural terms.
What fixes it. Describing behaviour: what was produced, under what conditions, and how it could be checked. "The output used the second convention" says everything "it got confused" was trying to say, and says it in terms you can act on.
8. "The AI said so" is a reason
Unit 17 · Checkpoint cp17-disclosure
What it looks like. A defence that reads "I used an AI assistant, which produced this implementation and confirmed it was correct." Or a citation of the tool's name and version in place of evidence.
Why it is tempting. It is true, it is specific, and it sounds like the honest thing to say. It also resembles the way we cite human authorities, where naming a reliable source really is part of an argument.
How it is caught. cp17-disclosure asks which items belong in a correctness
defence and includes both the tool's name and the model's own assurance among the
options, with diagnostics for each. The tool name belongs in the disclosure — it
describes provenance. The model's confidence belongs nowhere: it is not a
measurement of anything.
What fixes it. Keeping the two statements separate and writing both.
course/learner-evidence-template.md has a field for each, so the defence
cannot quietly become a longer disclosure.
9. Passing tests means understanding
Units 8 and 17 · Checkpoints cp08-bernoulli, cp17-disclosure
What it looks like. The checker prints PASS. The learner moves on, and cannot
afterwards say which convention the code uses, why the odd terms are zero, or
what would have happened at B30 under a different implementation.
Why it is tempting. A green result is designed to feel conclusive, and in a narrow sense it is: the code agrees with the oracle for every index from 0 to 30. The slide is from "this passed the checks" to "this is right" to "I have done the work", and each step looks small.
How it is caught. Deliberately, and in the tool itself. A passing run of
check.py prints: "Passing is not the end of the job. You still have to be able
to say why it is correct. That is the Unit 17 defence." The evidence export from
selfcheck.py progress --evidence carries the same caveat in writing: the record
shows which checkpoints were passed and is not a claim that the learner can
defend the answers. cp17-disclosure then requires the defence to name the
checks and the convention fixed, because test results are only meaningful
against a stated specification.
What fixes it. Writing the two-sentence defence while the work is fresh, and
noticing whether it comes easily. The optional CS extension
exercises/ex05_write_a_test.py makes the same point from the other side: it
asks the learner to write the test rather than pass it, and a test that catches
only one of four faults is a fast way to discover what one did not understand.
10. Disclosure discharges responsibility
Units 0 and 17 · Checkpoints cp00-contract, cp17-disclosure
What it looks like. "I declared that I used AI, so the error is not really mine." Or, in a milder form, treating the disclosure statement as the last task rather than as one of two.
Why it is tempting. Disclosure is the visible, procedural obligation, and meeting a stated obligation reasonably feels like completing something. In other contexts declaring an interest genuinely does resolve it.
How it is caught. cp00-contract puts this at the very start of the module,
before any content, with a diagnostic on the tempting option: disclosure tells
the reader how the work was made; it does not transfer accountability for whether
the work is right. cp17-disclosure returns to it at the end from the other
direction.
What fixes it. Holding the two questions apart — how was this made and why is this right — and answering both in writing. Using a tool changes how you produce work, not who answers for it.
11. Generated code can be run unread
Failure-mode code: import-error · Unit 8 ·
Checkpoint cp08-bernoulli
What it looks like. Code pasted from a model straight into check.py without
being read, on a working machine, in a directory that matters.
Why it is tempting. The friction is real and the risk is invisible. Reading thirty lines of unfamiliar code is slower than running it, the code is almost certainly harmless, and there is no immediate signal that a decision has been made at all.
Why it matters. check.py executes the file it is given. It does so in a
separate process with a timeout, resource limits and a stripped environment,
which contains accidents — but that is a smaller blast radius, not a sandbox in
the security sense: the code still runs as you. Generated code is code from an
untrusted source, not yet reviewed by anyone accountable to you, and its
appearance carries no information about what it does. Import-time side effects
run before any function is called.
How it is caught. The safety section of course/lab/README.md, the SAFETY
block in check.py's own docstring, the epilogue printed by check.py --help,
and a note in the exercise stub itself: "Read it. It is code from an untrusted
source, and running it is a decision you are making, not a formality." If a file
does fail on import, check.py reports import-error with a truncated traceback
rather than crashing.
What fixes it. Reading the file first, looking specifically for filesystem,
network or shell access, and running it somewhere disposable. A Bernoulli routine
needs fractions and math and nothing else.
12. Published means true
Unit 16 · Checkpoint cp16-note-g
What it looks like. Taking the printed Note G table as correct because it is printed, primary, and famous. Or the opposite overcorrection: repeating a confident modern account of exactly what the error was and who introduced it, because that account is also published.
Why it is tempting. Both moves substitute the authority of a source for a check. Print, age and reputation are proxies for reliability that usually work, which is what makes them hard to notice when they fail.
How it is caught. cp16-note-g requires the learner to state the correct value
independently — mapping Ada's B7 to the modern B8, then looking up -1/30 —
before comparing with the historical table. Being able to say what the value
should be is what makes the published table checkable at all. The published Note
G table does contain errors; the operation-4 inverted division is established
from a facsimile, a second fault in operation 24 rests on a published
reconstruction, the attribution of either stays contested, and what "the
faulty table computes" depends on which of them you repair.
course/historical-notes.md states which is which rather than settling any of it
in passing.
What fixes it. The same discipline in both directions: check the claim against
something independent, and where the record does not support a confident answer,
say so and label the uncertainty. See course/references.md for the source
policy.
Errors about the limits of checking
These three come from Unit 18, and they are recorded here rather than only there because a learner who never opens Unit 18 can still arrive at all three on their own. The module spends nine units arguing that a system cannot check itself; the overcorrections below are the natural way to get that argument wrong, and they need an answer whether or not anyone reads the capstone.
13. Nothing can really be known
Unit 18 · reachable from any unit
What it looks like. Concluding that because no check is ever fully self- contained, and because published sources turn out to be wrong, nothing is actually settled and any claim is as good as any other.
Why it is tempting. It sounds rigorous, it is unfalsifiable, and it removes the work. It also arrives naturally after Unit 16, where a famous published table turns out to contain an error.
How it is caught. By what the learner has already done. In Unit 8 they established agreement with an external oracle over tested indices and checked properties, under a stated convention. That is scoped knowledge based on independent records rather than confidence.
What fixes it. Noticing that the module replaced a guarantee with a method, which is not the same as replacing it with nothing. State your context, keep records, compare independent ones, and say what kind of support each claim has.
14. Therefore AI cannot be trusted at all
Unit 18 · related to cp07-trust-triage
What it looks like. Treating "the model cannot certify its own output" as "generated output is worthless", and refusing to use the tool rather than learning to check it.
Why it is tempting. It is the safe-looking conclusion, and it feels like the responsible reading of a module that spends its time on failure modes.
How it is caught. The limitation is about the structure of self-certification, not about the quality of any particular system. It applies with exactly the same force to a human reasoner marking their own work, to peer review, and to the printed Note G table. Singling out AI misidentifies what the problem is.
What fixes it. An architectural response rather than a verdict: put the check somewhere the thing being checked does not control, index the claim with its context, and know when to escalate. Unit 3's triage does this concretely — the question is never "trust or not" but "what kind of check does this need".
15. Gödel and Tarski prove that AI cannot verify itself
Unit 18 · related to cp18-sort-the-claims
What it looks like. Citing the incompleteness or undefinability theorems as though they settled a question about language models, machine intelligence, or institutions.
Why it is tempting. The theorems are real, famous, and about limits of self- reference, so the analogy is genuinely suggestive. It is also extremely common: this particular leap appears in a great deal of confident published writing, which makes it feel established.
How it is caught. By asking what the theorems are actually about. They are
results concerning formal systems of a specific kind, proved under specific
conditions. Transferring them to a language model, a person, or an organisation is
an argument that has to be made and defended — it is not a corollary anyone can
quote. course/second-order.md §4 states the limits explicitly.
What fixes it. The same habit as everywhere else in this module: separate what a source establishes from what it is being used to suggest. Noticing this particular leap is a good test of whether the module worked, precisely because the leap is so widely made.
16. Attention, training, and retrieval are one truth-finding process
Unit 3 · Checkpoint cp03-system-layers
What it looks like. Saying that attention looks up sources, retrieval makes an answer true, or pre-training describes the complete assistant a learner uses.
Why it is tempting. The layers are hidden behind one interface and contribute to one fluent response. Product descriptions also use broad terms such as “model” or “AI” for the whole service, so distinct mechanisms collapse into one apparent operation.
How it is caught. cp03-system-layers requires five functions to be matched
separately. Its diagnostics name attention-versus-retrieval,
retrieval-versus-truth, and pre-training-versus-post-training confusions.
What fixes it. Ask which operation is being described. Attention combines positions already in context. Pre-training learns next-token patterns. Post-training shapes assistant behaviour. Retrieval supplies external material. Next-token scores rank continuations. None is a truth predicate.
Errors in checking a source
These six are the source-checking failure modes, and they belong together because telling them apart is the skill. Each one names a different repair, which is why "the citation was bad" is not a diagnosis: it licenses no particular action.
They are primary diagnoses rather than exclusive descriptions. A real claim can fail at the pointer, the edition and the meaning at once; the reading gives a decision order, and what you name is the first fault that applies.
None of them is detected by a program. There is no oracle for whether a source supports a claim, and a checker that claimed to settle it would be making the overreach this module exists to catch.
17. The claim is true, so the citation is fine
Unit 9 · Checkpoint cp09-diagnose-source · misattributed
What it looks like. Confirming that a stated finding is real, and stopping there. The work cited turns out not to discuss the topic at all.
Why it is tempting. Checking the claim and checking the citation feel like the same act, and the claim is the part you care about. If it is true, the citation looks like a formality.
How it is caught. Opening the cited work and asking whether the finding is in
it — not whether the work is relevant, which it often is.
cp09-diagnose-source gives an observation where the finding is well established
and the cited work does not contain it.
What fixes it. Treating the citation as a separate claim with its own evidence. The repair here is cheap and specific: credit the source that does contain the finding. The claim survives, which is what distinguishes this from entry 18.
18. The source is about this, so it supports this
Unit 9 · Checkpoint cp09-diagnose-source · right-source-wrong-claim
What it looks like. The cited paper is squarely on the topic, the page exists, and the result attributed to it is not the result it reports.
Why it is tempting. This is the hardest of the six, because every cheap test passes. Author, title, year, venue, page — all real, all right. Only reading the passage catches it.
How it is caught. Question 3 of the reading's three: does this passage support this claim, as stated? Being the right place to look is not support.
What fixes it. Nothing about the citation, which is the tell that separates this from entry 17. The cited work is the correct one and it reports something else, so the claim itself has to change. If correcting the citation would repair it, you are looking at a misattribution instead.
19. A precise number is a better number
Unit 9 · Checkpoint cp09-diagnose-source · over-specified
What it looks like. The source says "roughly a fifth"; the summary says "19.6 per cent". The source says "in the 1990s"; the summary says 1994.
Why it is tempting. Precision reads as care. A specific figure looks like someone did the work, and vagueness looks like someone did not — so the sharper version is the one that survives being repeated.
How it is caught. Comparing the quantity with the quantity in the source, at the strength the source states it. A range is a claim about uncertainty, and replacing it with a point discards that claim.
What fixes it. Carrying the source's own hedging forward. If the source says "roughly", the summary says "roughly", and a reader who wants the exact figure learns that nobody has it.
20. Reported together means one caused the other
Unit 9 · Checkpoint cp09-diagnose-source · unsupported-inference
What it looks like. A source reports an association and states that its design cannot separate the causes. The summary says one caused the other.
Why it is tempting. The causal reading is the interesting one, and it is usually what the reader wants to know. A careful source's hedge is the least quotable sentence in it.
How it is caught. Asking what changed between the source and the summary. Here it is the strength of the claim, not any number — which is what separates this from entry 19.
What fixes it. Restating the claim at the strength the source supports, and saying explicitly what the source declined to conclude. That sentence is often more useful than the overstated one.
21. A real work means a real page
Unit 9 · Checkpoint cp09-diagnose-source · fabricated-locator
What it looks like. The book exists, the author is right, and the cited page is past the end of it. Or a section number that the document does not have.
Why it is tempting. Confirming that a work exists is easy and satisfying, and it feels like the check. The locator is the part nobody opens.
How it is caught. Opening the locator. This is the cheapest of the six checks and it is skipped more than any of the others.
What fixes it. Asking for locators precisely so that they can be opened — and then opening one. The repair, when the claim itself is sound, is to correct the citation.
22. The rule I found is the rule in force
Unit 9 · Checkpoint cp09-diagnose-source · stale-version
What it looks like. A threshold, a target or a requirement quoted accurately from an edition that has been superseded, and presented as current.
Why it is tempting. The source is genuine and the quotation is exact. Nothing about the passage says it has been replaced, and the replacing document is a separate search.
How it is caught. Asking whether the claim is about the current state, and if so, whether this edition is the current one. A claim about what the rule is now needs a source about now.
What fixes it. Finding the current edition and re-checking. Distinguishing this from entry 21 matters because the repairs differ: a fabricated locator is fixed by correcting the pointer, a stale version by changing the source.
Using this file
For instructors: the checkpoint diagnostics in course/lab/checkpoints.py
are written against these misconceptions, so a learner's failed attempts are
informative. A cohort repeatedly failing cp06-convention with option 2 is
telling you something different from a cohort failing it with option 3.
For learners: recognising your own error in this list is the intended outcome, not an embarrassment. Every entry here was expected before you arrived, which is why there is a checkpoint waiting for it.