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 set and are the
codes course/lab/check.py emits. Unit numbers and checkpoint ids come from
course/course-map.md and course/lab/checkpoints.py.
Summary
| # | misconception | code | unit | checkpoint |
|---|---|---|---|---|
| 1 | B1 is +1/2 |
b1-sign |
3 | cp03-convention |
| 2 | Ada's B7 is the modern B7 |
ada-indexing, odd-terms |
3, 7 | cp03-ada-index, cp05-table-evidence, cp05-diagnose, cp07-note-g |
| 3 | Decimal output is close enough | not-fraction |
3, 5 | cp05-bernoulli, cp05-table-evidence |
| 4 | The indices line up | off-by-one |
4, 5 | cp05-bernoulli |
| 5 | It works for the values I tried | raises |
5, 6 | cp05-bernoulli, cp06-spec-clauses |
| 6 | Correct code implies correct comments | — | 4 | cp04-trust-triage |
| 7 | The model knew, decided, tried | — | 2 | cp02-risk-triage |
| 8 | "The AI said so" is a reason | — | 8 | cp08-disclosure |
| 9 | Passing tests means understanding | — | 5, 8 | cp05-bernoulli, cp08-disclosure |
| 10 | Disclosure discharges responsibility | — | 0, 8 | cp00-contract, cp08-disclosure |
| 11 | Generated code can be run unread | import-error |
5 | cp05-bernoulli |
| 12 | Published means true | — | 7 | cp07-note-g |
| 13 | Nothing can really be known | — | 9 | — |
| 14 | AI cannot be trusted at all | — | 9 | related to cp04-trust-triage |
| 15 | Gödel/Tarski prove AI cannot verify itself | — | 9 | related to cp09-sort-the-claims |
| 16 | Attention, training, and retrieval are one truth-finding process | — | 2 | cp02-system-layers |
Errors in the numbers and the code
1. B1 is +1/2
Failure-mode code: b1-sign · Unit 3 · Checkpoint cp03-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. cp03-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 6 precise prompt.
2. Ada's B7 is the modern B7
Failure-mode codes: ada-indexing, odd-terms · Units 3 and 7 ·
Checkpoints cp03-ada-index, cp05-diagnose, cp07-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. cp03-ada-index asks for the modern index of
Ada's B7; cp05-diagnose asks the learner to name the failure mode from output
alone; cp07-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 3 and 5 ·
Checkpoints cp05-bernoulli, cp05-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 4 and 5 ·
Checkpoint cp05-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 5 and 6 ·
Checkpoints cp05-bernoulli, cp06-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. cp06-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 4 · Checkpoint cp04-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. cp04-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 2 · Checkpoint cp02-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. cp02-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 2 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 8 · Checkpoint cp08-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. cp08-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 5 and 8 · Checkpoints cp05-bernoulli, cp08-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 8 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. cp08-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 8 · Checkpoints cp00-contract, cp08-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. cp08-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 5 ·
Checkpoint cp05-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 imports and executes the file it is given. There is
no sandbox. Generated code is code from an untrusted source — not maliciously
authored, but 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 7 · Checkpoint cp07-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. cp07-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 an error; its location and form are established (a facsimile
shows the inverted division at operation 4) and only its attribution stays
contested, and course/historical-notes.md states which is which rather than
settling 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 9, and they are recorded here rather than only there because a learner who never opens Unit 9 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 9 · 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 7, where a famous published table turns out to contain an error.
How it is caught. By what the learner has already done. In Unit 5 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 9 · related to cp04-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 2'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 9 · related to cp09-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 2 · Checkpoint cp02-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. cp02-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.
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 cp03-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.