An expression referenced a name that was never declared as `param`/`bool`/`choice` state (or isn't a frame variable like `x`/`t`). Typos get a "did you mean" suggestion.
bad
param t = 0
curve f = t2 { color: primary }good
param t = 0
curve f = t { color: primary }Older versions silently turned `None` into `Infinity`, producing baffling off-screen geometry. It's a parse error now — omit the prop instead, or bind it to a real boolean.
bad
curve f = x^2 { show: None }good
bool showF = true
curve f = x^2 { show: showF }Prism is state-centric, not object-centric — there's no `point.x`. Give each coordinate its own state variable instead.
bad
param p = 0
curve f = p.x
good
param px = 0
curve f = px
`x: [min, max]` is required on every scene (and `y:` too, for `plane`). Without it the renderer has no coordinate system to map to pixels.
bad
scene plane {
grid
axes
}good
scene plane {
x: [-5, 5]
y: [-5, 5]
grid
axes
}A slide is prose? + scene? + exercise? + goal* — exactly zero or one exercise. Two answerable things on one slide is ambiguous for the Check button. Split into two slides instead.
bad
lesson "L" {
slide "s" {
quiz {
ask "pick one"
* "a"
- "b"
}
numeric {
ask "and a number?"
answer: 1
}
}
}good
lesson "L" {
slide "s1" {
quiz {
ask "pick one"
* "a"
- "b"
}
}
slide "s2" {
numeric {
ask "and a number?"
answer: 1
}
}
}Exactly one option must be marked `*` — the rest use `-`.
bad
quiz {
ask "pick one"
- "a"
- "b"
}good
quiz {
ask "pick one"
- "a"
* "b"
}Every `slider`/`toggle`/`stepper`/`picker`/`button` writes to a `param`/`bool`/`choice` declared earlier in the same scene — declare the state first.
bad
slider t { label: "t" }good
param t = 0 { range: [-3, 3] }
slider t { label: "t" }A `hotspot` exercise needs exactly one `target rect (...)` or `target circle (...)`.
bad
hotspot {
ask "tap it"
}good
hotspot {
ask "tap it"
target circle (0, 0) { r: 1 }
}Every `row:` cell is either a plain given number or a `blank(<answer>)` — at least one cell across the whole table must be a blank, or there's nothing for the learner to fill in.
bad
table {
ask "fill it in"
row: 1, 2
}good
table {
ask "fill it in"
row: 1, blank(2)
}Each `pair` line is `"left" -> "right"` — both the left and right text are required.
bad
match {
ask "match them"
pair "a"
pair "b" -> "2"
}good
match {
ask "match them"
pair "a" -> "1"
pair "b" -> "2"
}An `onwrong:` target has to name a slide in the same lesson that is marked `hidden: true`. Without `hidden`, the scaffold would also appear on the main path, so every learner would see the remediation whether they needed it or not. Unknown ids get a "did you mean" suggestion.
bad
lesson "L" {
slide "Question" {
numeric {
ask "2 + 2?"
answer: 4
onwrong: "help"
}
}
slide "Help" {
id: "help"
> count on your fingers
}
}good
lesson "L" {
slide "Question" {
numeric {
ask "2 + 2?"
answer: 4
onwrong: "help" retry
}
}
slide "Help" {
id: "help"
hidden: true
> count on your fingers
}
}`expect:` states the derivation of a numeric answer so the compiler can double-check it. If the two don't agree within the exercise's tolerance, the build fails and names both values. That's the point: it catches a mistyped answer before a learner ever sees it. Fix whichever one is actually wrong.
bad
numeric {
ask "What is 3/8 as a decimal?"
answer: 0.385
tolerance: 0.0001
expect: 3/8
}good
numeric {
ask "What is 3/8 as a decimal?"
answer: 0.375
tolerance: 0.0001
expect: 3/8
}Slide ids are derived from the title by keeping `[a-z0-9]` and nothing else, so an Arabic (or any non-latin) title leaves nothing to build an id from. The compiler would fall back to a positional `slide-2`, which silently changes the moment you reorder slides, and slide ids are a durable key: saved notes, recorded attempts and every `onwrong:` target point at them. A title mixing Arabic with latin math is worse, because it keeps only the scraps, so two different slides can both end up as `f-x`. Write the id yourself.
bad
lesson "النهايات" {
slide "المشتقة تقيس معدل التغير" {
> نص
}
}good
lesson "النهايات" {
slide "المشتقة تقيس معدل التغير" {
id: "derivative-intro"
> نص
}
}