Skip to main content

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.

FolderRoleReaches the model via
system-prompt/Identity, routing, standing rulesroot CLAUDE.md import
guardrails/Socratic rules, terminal rule, web safetyroot 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.pyskills 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-reflector drafts proposals → a human reviews them in git. Agents never edit harness/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​

  1. Raw records: the dated, append-only files: interaction logs, grill history, progress journals, retro files.
  2. Curated facts: the typed graph (concepts, learnings, mnemonics, mastery events, mistakes).
  3. The learner profile: short lines about how this learner learns, shared by every env and read at each session start (below tutor.md in 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.