# Working on an explico course (with an agent)

Guidance for an AI agent — or a person — editing a course built on
`explico`; the engine ships it so every course inherits a baseline.

**Examples to start from.** [The start page](https://danieltuzes.github.io/explico/start/)
serves three, each at its newest version
([`sources.json`](https://danieltuzes.github.io/explico/downloads/sources.json)
lists them): **the starter**
([zip](https://danieltuzes.github.io/explico/downloads/explico-starter-latest.zip)),
one part and one chapter with every file one example of its kind, the place to
start a course of your own; **the playground**
([zip](https://danieltuzes.github.io/explico/downloads/explico-playground-latest.zip)),
every feature wired — download it to see how one is wired before writing your
own; every `playground/…` path in these docs is a file in it — and
**credit-and-derivatives**, a full course as taught.

This is a guide, not a rule area. The enforceable rules are in
[`notation.md`](notation.md) (`N`), [`visualizations.md`](visualizations.md)
(`V`), [`versioning.md`](versioning.md) (`C`),
[`structure-rules.md`](structure-rules.md) (`S`) and [`prose.md`](prose.md)
(`P`); where the engine's behaviour and a rule disagree, the engine wins
([`README.md`](README.md)).

## The loop

- **Read the rules in effect first, one at a time after that.**
  `pnpm content rules` (or `build/rules-in-effect.md` after
  `validate:content`) lists each rule this course has *on*, by element —
  prose, math, equation, notation, heading, part-page, plot, diagram, table,
  assessment, source, changelog — with its enforcement, codes, settings and a
  one-line summary; `--element table,prose` narrows it, and
  `--enforcement guide` lists what no script checks, which is on you.
  `pnpm content rule <ID>` prints one rule whole with its example (about 500
  tokens; N4 about 3,000); open a rule page only for more. A rule not listed
  is off here (or does not exist): neither apply nor flag it. For a deck,
  `--edition deck` (or `build/rules-in-effect-deck.md`) lists the `D` rules
  for frames, then those a deck shares with a chapter ([deck.md](deck.md)). A
  `waivable` rule can be broken at one place with
  `{/* explico-allow <ID>: <reason> */}` above the block, where the reason
  belongs ([README.md § Waiving](README.md#waiving-a-rule-at-one-place)). An
  `X` rule, tagged `course`, is this course's own (`courseRules` in
  `course.settings.json`): it binds like an engine rule and is the one kind
  you may be asked to *add* ([README.md § Rules of your own](README.md#rules-of-your-own)),
  never by editing this folder.
- **Start from the CLI, not a file hunt.** Run it from the course package (in
  a workspace, `pnpm --filter <course> …`): `pnpm content help` lists the
  verbs, `pnpm content help <verb>` gives one verb's flags and `--json` shape
  ([`cli.md`](cli.md) has them all), and every verb takes `--json`. Read what
  it prints before opening files.
- **Validate once.** `pnpm validate:content` rewrites `build/resolution/*` and
  fails only on a git-tracked report it had to rewrite: commit the rewritten
  reports with the change and run it again (an engine upgrade rewrites them
  all); a course that ignores `build/` passes on the first run.
- **Before handing work back,** `pnpm content verify` runs `validate:content`
  → `content check --changed` (skipped before the first commit) →
  `astro check` → the course's tests (its `test:unit` script, else `test`) →
  `astro build` (`--e2e` adds the browser tests): a line per step and a
  failed step's log tail; a rerun on an unchanged tree answers from its last
  pass. Only `astro build` runs every gate the CI does.

## The CLI

The `content` CLI ships in the engine (`explico/compiler/cli-main`); a course's
`scripts/content.ts` is a three-line shim over it (0.14.0's migration step 14).
It is read-only except `new`, which creates files and never overwrites one;
`verify`, which writes what `validate:content` and `astro build` write; and
`move`, `rename`, `fix`, `init-agent` and `release`, which print a diff and
change nothing without `--write`.

| You want to know or do | Verb | It prints |
| --- | --- | --- |
| how the course stands | `status` | counts, diagnostic totals, agent-surface sizes, whether the procedures are installed |
| what is wrong, where | `diagnostics [info\|warning\|error]` | counts by code (with its rule) and by file; `--all`, `--code`, `--lesson`, `--changed` list the lines |
| the warnings alone | `warnings` | each warning with its rule, then the count |
| what a lesson draws on | `context <lesson>` | its outcomes, notation, sources, diagnostics |
| which rules apply | `rules [--element …] [--enforcement guide]` | the rules this course has on, by element |
| one rule, whole | `rule <ID>` | the entry as this course has it and its page's section |
| is this symbol defined already | `symbol <latex \| key>` | the shared card, local entries and glosses with their pages, near misses |
| will this expression build | `equation "<latex>" [--lesson <id>]` | each glyph's card, unresolved glyphs and their definitions; exit 1 if the build would refuse it |
| what a lesson's math binds | `bindings <lesson>` | every span's bindings, unresolved glyphs, redundant `\explain` wraps |
| does it render | `check <lesson \| deck>… \| --changed` | schema, references, imports, MDX → KaTeX; exit 1 on an error |
| what an upgrade needs | `migrate [--from <v>] [--to <v>]` | the migration guide's sections between two versions, oldest first |
| a local entry becomes shared | `move <lesson>:<key> [--to <key>] [--write]` | the new card and every changed copy and reference, as a diff |
| rename a shared card | `rename <old> <new> [--write]` | the file and every reference, as a diff |
| the mechanical repairs | `fix <lesson>… \| --all [--write]` | redundant `\explain` wraps unwrapped, as a diff |
| a draft to start from | `new lesson <id>`, `new deck <lesson \| part>`, `new term <key>` | the files, which pass `check` as written (a lesson in a new directory once its part is in `parts`), and what is left: that part, the track, a deck's frame fit at `astro build` (rule D2) |
| install the procedures | `init-agent [--write]` | the skills and the `rules-reviewer` agent for `.claude/`, a baseline `AGENTS.md` if there is none |
| cut a content release | `release [major\|minor\|patch] [--write]` | `## [Unreleased]` dated and `about.version` bumped, as a diff (rule C3) |
| is it ready to hand back | `verify [--e2e] [--force]` | a line per step and a failed step's log tail; exit 1 if one failed |
| a verb's flags and `--json` | `help [<verb>]` | the usage; with a verb, its flags, writes, exit code and `--json` type |

The procedures — drafting a lesson, upgrading the engine, releasing the
course's content, reviewing a lesson against the guide-only rules — ship in
`node_modules/explico/docs/course-agent/`; `pnpm content init-agent --write`
installs them as project skills and a sub-agent (rerun it after an upgrade).
The course's `AGENTS.md` opens by pointing here, then holds only what is
particular to the course: `docs/course-agent/AGENTS.md` is that baseline, and
`content status` says when the course's file does not point here.

## Reading and running

- Grep for a symbol and open a line range; don't read a whole engine module
  out of `node_modules/explico/`. You need [`notation.md`](notation.md), this
  file and the CLI's output; for what can go in a lesson body — a tooltip, a
  table, a diagram, an aside — [`components.md`](components.md) is a
  syntax-first catalog, not a rule file to read through.
- A page that 500s, or a change that doesn't show, means a stale dev-server
  content store: a **new file in a `content/` collection needs a restart**,
  not just a save. Restart it; don't diagnose it with `ps` / `curl`.
- Ad-hoc Node scripts run from the course package (workspace deps aren't at
  the repo root); Playwright imports as `@playwright/test`.
- Pipe long output to `| tail -n 40`; one-off probes and fetched pages go to a
  scratch file you grep, not into the conversation.

## Prose

Clarity comes before variety and entertainment, in lecture notes and
documentation alike. The `P` rules say how; check a draft against the ones
this course has on (`content rules --element prose`), not a remembered
[`prose.md`](prose.md), before handing it back. No script checks a `P` rule:
they are all `guide`, and the review is yours.

## Notation `formula:` fields (the sharp edges)

- Every glyph in a `formula:` resolves (rule N4, locked): to the entry, one of
  its `glosses:`, a card named with `\explain{key}{latex}`, or the base
  library; an unresolved bare letter fails the build, naming the entry. A
  formula resolves against its own card's table, so an index entry (`{}_0`,
  rule N23) does not compose there: explain `v_0` with a whole form
  (`\explain{motion.launch-speed}{v_0}`), a gloss, or the index entry's key
  around the subscript, `\mathbf{v}_{\explain{motion.initial}{0}}`.
- The reference graph is **acyclic** through meanings and bodies: if `a`'s
  meaning names `b` and `b`'s names `a`, the build rejects it; link only
  *down* a chain (acceleration → velocity → position). A `formula` may name
  any card, even one whose formula names it back: `confidence-level`
  (γ = 1 − α) and `significance-level` (α = 1 − γ), or three entries each
  showing the one model equation they appear in.
- `\mathbf` is the house style for a vector, and a bold letter is its own
  symbol (rule N4): `\mathbf{v}` needs an entry (`latex: \mathbf{v}`) or an
  `\explain{key}{\mathbf{v}}` wrap even beside a scalar `v`; so do
  `\boldsymbol`, `\bm`, `\mathcal`, `\mathbb`, `\mathscr`, `\mathfrak`,
  `\mathsf` and `\mathtt`. `\vec` renders, but is a decoration (rule N22), not
  a font.

## Decorations: `\hat`, `\bar`, `'`, `^*` (rule N22)

- A decoration is an operator. Before drafting, decide for each which of three
  it is on the page: a **whole form** with its own entry (`latex: \hat{\beta}`)
  when the decorated symbol is a quantity in its own right; an **operator
  entry** (`latex: \hat{}`, `{}'`, `{}^{*}`) when it means the same wherever it
  appears; or on the course's `rules.N22.allow` list. An unexplained one is an
  `accent-explained` warning.
- **Suspend** it (`notation.suspend` in the lesson frontmatter, with a
  `reason`) only where it is *not* its usual operator — primes that number
  versions (`b'`, `b''`) rather than differentiate. Every decorated form on
  that page then needs its own entry, so it is stricter, not a shortcut; one
  whole-form entry is enough for a single special symbol.

## Indices: `x_0`, `v_x`, `x_i` (rule N23)

- A subscript is part of the symbol; a card on the base alone does not explain
  it. Before drafting, decide for each indexed symbol which of three it is: a
  **whole form** with its own entry (`latex: v_0`, "Launch speed") for a named
  quantity; a **family** (`latex: x_k`) for an index that runs, explaining
  `x_1`, `x_{k+1}`, `x_N`; or an **index entry** (`latex: "{}_0"`, quoted) for
  a subscript with one meaning on the page, which makes each carded symbol it
  indexes one derived card. An index entry takes no `units`; its title for the
  symbol comes from `reads`.
- A base-library `i`, `e` or `d`, a digit or a `\mathrm{…}` label in a
  subscript needs one of the three; an index letter with its own card
  (`X_{t-1}`) explains its slot. A running index (lowercase, not
  `fixedIndex: true`) states its set once: `range` on its card, or after a
  `\quad` on the first display line using it (`\quad i = 1, \dots, n`).
- Do not wrap an indexed symbol in a card without the index:
  `\explain{motion.velocity}{v_x}` fails `index-swallowed`. An indexed entry
  lends its card to no bare symbol (rule N4): a bare `\alpha` beside a family
  `\alpha_i` needs its own entry.
- Lab and `<Diagram>` math is checked as it renders, at `astro build`. A lab
  passes the table `labMathScope` returned to `renderLabMath` unchanged; a
  copy gets no derived cards and no check.

## Versioning

Every content change adds a bullet under `## [Unreleased]` in
`content/CHANGELOG.md`; a release (`pnpm content release`) moves it to a dated
`## [x.y.z]` heading and sets `about.version` to match, in one commit. Rules
C1–C3, [`versioning.md`](versioning.md).

## Upgrading the engine

Every `explico` bump is a migration, PATCH included, and its instructions ship
with the code. Record the installed version
(`node -p "require('explico/package.json').version"`), install the target,
and run `pnpm content migrate`: it prints the sections of the installed
`migrating.md` to apply, from the version `package.json` pinned at HEAD to the
one installed, oldest first (`--from` and `--to` override either end), and
refuses when one is missing — the one or two an upgrade needs, not the
forty-thousand-token guide. Apply every section it prints: `content/`,
`content/course.config.ts`, Astro and source wiring, scripts, dependencies,
generated files. `CHANGELOG.md` is not the procedure, and a release without a
**Breaking** heading may still need work: each section gives the work or says
no course changes are required. Finish with [`migrating.md`](migrating.md)'s
validation sequence.
