Build on yat, volume 17
Architecture
The pedagogy, the runtime and the env stay separate. Here is what lives under harness/ and how it reaches the model.
Architecture
Three things stay separate: the pedagogy (how the tutor teaches), the runtime (Claude Code), and the env (what the learner is studying). The pedagogy is a set of plain files under harness/. An env is one env.toml. Two feedback loops improve the harness over time.
What is under harness/
Each is a folder. Edit any of them to change how the tutor behaves.
| Folder | Role | Reaches the model via |
|---|---|---|
system-prompt/ | Identity, routing, standing rules | root CLAUDE.md import |
guardrails/ | Socratic rules, terminal rule, web safety | root CLAUDE.md import; web safety is read by yat:reverse-engineer when a browser is involved |
memory/ | The typed graph's schema (schema.toml) | yat:memory |
tool-guidance/ | How to use tools (Chrome, ...) | skills reference it |
agents/ | Delegated roles (retro-reflector) | .claude/agents symlink, or plugin loader |
skills/ | Repeatable behaviours with a declared API; each owns the shapes of the files it writes in references/ | .claude/skills symlink, or plugin loader |
scripts/ | Deterministic helpers: ontology.py (validated graph engine), interaction-stats.py | skills run them |
plugins/ | Optional integrations (Anki, Chrome) | docs plus skill requires |
Envs are data
An env is a value, not a code path. Skills read env.toml instead of hard-coding env names, so adding a topic never touches the harness. The spec is parsed by your_ai_tutor/spec.py; the daily schedule is derived from it in your_ai_tutor/schedule.py.
Two ways to load
Inside the repo, .claude/skills and .claude/agents are symlinks into harness/, so Claude Code loads them with no install. To use the tutor elsewhere, yat-install registers the repo as a local plugin marketplace and installs harness/ as the tp plugin. The files are the same either way.
Two feedback loops
- Session level:
resume→meditate→wrap up. What the learner just did changes the next session's opener (a retrieval question from the last miss). - Harness level:
retro→memory/retros/improvement-queue.md→retro-reflectordrafts proposals → a human reviews them in git. Agents never editharness/skills/directly; git is the gate.
Teaching style is per-env data
envs/<env>/tutor.md is the learner's description of how they want to be taught. It is data, not harness: yat:learn reads it each session. Precedence is guardrails, then env.toml, then tutor.md, then the learner profile, then the default voice, so a style preference can change tone, question style, analogies and visuals but never the Socratic rules. Details: harness/skills/learn/references/tutor-voice.md.
Mastery maps
An env can have a mastery map (yat:mastery): the ideas behind its roadmap units, the prerequisites between them, and a
state per idea. The map lives in the typed graph. A state is never stored; it is replayed from append-only events,
and every event must cite a real interaction-log line or grill row of the right kind (a passed gate for "explained", a
correct answer on a later day for "recalled"). The rules are in harness/scripts/mastery.py, reached only as
ontology.py mastery, so the terminal rule's bookkeeping exception covers every call without rewording it.
Reviews are reviewed events on the same points. A point's next review date is derived from its events on a fixed
ladder of days (1, 3, 7, 14, 30, 60), never stored. The mistake book is Mistake nodes, opened and closed from grill
results by ontology.py practice (harness/scripts/practice.py), with the learner's own correction line on each.
Three layers of memory
- Raw records: the dated, append-only files: interaction logs, grill history, progress journals, retro files.
- Curated facts: the typed graph (concepts, learnings, mnemonics, mastery events, mistakes).
- The learner profile: short lines about how this learner learns, shared by every env and read at each session start (below
tutor.mdin precedence).
Higher layers cite lower ones and never copy them. A profile line needs at least one evidence node or two raw records, across at least two sessions. It is proposed at retro, vetted first, and saved only when the learner accepts it. Rejected and deleted lines are remembered, so the same evidence never brings them back. "why do you think that about me?" walks a line down to the dated records. The code is harness/scripts/learner_profile.py, reached as ontology.py profile.
Grounded reading
When yat:media absorbs a text source it also writes media/out/<stem>.index.md: sections found from headings, pages from the form feeds pdftotext writes, or fixed parts when a text has neither. Locations (<stem>:p14, <stem>:s2) are what the tutor cites. Lookup is keyword scoring over pages, with no embeddings and no network; quote checks that a phrase is really on the cited page and returns at most three sentences. Each env keeps its own reading position per source. All of it is harness/scripts/reading.py, reached as ontology.py reading; the read-along itself (predict, read, located questions, the learner's own words, never a summary) is the yat:read skill.
The learner notebook
Each env with a mastery map can keep a notebook in envs/<env>/notebook/: one Markdown page per point, in the learner's own words. When a verbalization gate passes, yat:learn (or yat:read) captures the explanation as pending; the capture must cite that passed gate. At wrap up the learner chooses which to keep. Pages are only ever appended to, and every version has a learner origin (gate, revision, hand, spelling); there is no tutor origin, and a spelling fix is limited to small per-word edits. Review is questions only. compile writes a plain Markdown book in roadmap order, with gaps left for the learner to write. All of it is harness/scripts/notebook.py, reached as ontology.py notebook.
Data vs harness
harness/ is code and ships with the repo. envs/, media/, tracker/, memory/ are the learner's data, ship empty, and are git-ignored by default.
Evals
harness/evals/ checks how the tutor behaves when it runs these skills. A case is a short scripted conversation in a
synthetic fixture env, plus the rules to check: the learner's target never written by the tutor, no minutes, today's
block named first, the terminal rule, the skill that handled the request, and the judged rules (questions over
answers, predict before reveal, one hint rung at a time, one idea per question). yat-evals builds the cases for
Claude Code's claude plugin eval. The earlier turns become a resumed history and the last learner line the prompt, so
only the tutor's next reply is graded. Eval runs load no CLAUDE.md, so the build appends SYSTEM.md and the
guardrails to each case's system prompt, verbatim. Hard rules are regex and tool-call checks; judged rules use a model
grader whose rubric quotes the rule from its source file, and yat-evals calibrate checks that grader against labelled
replies before its verdicts count. The suite only reports; it never edits the harness.
Lint before a session
yat-lint validates every skill manifest, env spec and cross-reference, so a broken file is caught before it reaches the model.