Adding readings
The specs an AI agent followed to parse this Greek and Latin, and the file format the reader loads.
The readings here were prepared by handing the text to an AI coding agent along with a CLAUDE.md describing exactly what to produce. There is one for each language, and both are reproduced in full at the bottom of this page. If you want to put your own text through this reader, that file is the thing to start from: give it to an agent together with the passage.
Download Greek CLAUDE.mdDownload Latin CLAUDE.md
The unit is a reading
One reading is one chapter, one file, plus one entry in the manifest. That is the whole job. A reading is complete on its own and is added without reference to anything else: there is nothing to group it under, no deck to build, and no question bank to write.
Readings are addressed as work / book / chapter. A short standalone passage is simply chapter n of book 1 of its work; a real text uses its own book and chapter numbers. Chapters live at public/data/<work>/<book>/<chapter>.json, which means they are also plain static files: the reader never puts a database in the reading path.
The vocabulary deck and the practice questions are derived from the same token data rather than written separately, so a well-parsed chapter is already a question bank and already a deck.
The chapter file
One object per chapter. segments is the aligned text and translation, and each segment's greek is one token per word, in order. (That field is named greek in Latin chapters too: it predates the reader having a second language, and it means “the source-language tokens” either way.)
{
"work": "grk30",
"book": 1,
"chapter": 1,
"slug": "ody5-55-94",
"title": "Odyssey 5.55–94",
"citation": "Homer, Odyssey 5.55–94 — Hermes comes to Calypso's island",
"defaultSrc": "human-verified",
"segments": [
{
"lines": "56–57",
"translation": "there, stepping from the violet-dark sea onto the mainland, he kept going, until he came to a great cave,",
"greek": [
{
"w": "ἔνθ᾽",
"lemma": "ἔνθα",
"pos": "adverb",
"parse": "(elided ἔνθα)",
"gloss": "there, thereupon",
"note": "picks up the island just named and anchors the next movement to it"
},
{ "w": ",", "punct": true }
]
}
]
}One field the specs have no equivalent for is defaultSrc, the provenance of the analysis, which any single token can override with its own src. It shows as a badge in the word panel. Be honest with it: the point is that generated analysis cannot quietly pass itself off as checked analysis.
treebank, from a gold-standard dependency treebankmorpheus+model, where a morphological analyser licensed the parse and a model chose among the options it returnedmodel-only, where no analyser covers the form (crasis, proper nouns, corrupt text)human-verified, where a person checked it
The manifest entry
public/data/manifest.json is what the contents page reads. Add the chapter to its work's chapters array in reading order and bump the book's count. The segments, tokens and bytes figures are the file's own numbers, used for the word counts on the contents page.
{
"id": "grk30",
"title": "GRK30 readings",
"author": "various",
"note": "Optional one-line description, shown under the title.",
"defaultSrc": "human-verified",
"books": [{ "book": 1, "chapters": 16 }],
"chapters": [
{
"book": 1,
"chapter": 1,
"slug": "ody5-55-94",
"title": "Odyssey 5.55–94",
"segments": 34,
"tokens": 274,
"bytes": 52441
}
]
}The specs
Reproduced in full. Both began as the CLAUDE.md of a course site, where the organizing unit was the quiz a reading would be tested on; they are rewritten here around the reading itself. The Latin one is the Greek one adapted, and its substantive difference is macrons: the text as displayed carries none, while the lemma does, because the lemma is what the vocabulary deck drills.
Greek GRK30-CLAUDE.md view raw
# Classics Reader — Greek readings
Context for Claude when preparing a reading for the Classics Reader.
Adapted from the CLAUDE.md of the GRK30 course site this reader grew out of.
That site organized everything around the **quiz** it would be tested on. This
one does not: **the unit is a reading — one chapter, one file.** A reading is
complete on its own, stands on its own, and is added without reference to
anything else.
## What this is
An interactive reader for whole Greek texts. Greek on the left, a literal English
translation on the right, aligned clause by clause; every word is a clickable
token carrying lemma, part of speech, parse, gloss and a syntax note. From that
same token data the reader derives a parse trainer, form drills, practice
questions and Anki vocabulary decks.
Readings are addressed as `work / book / chapter`. A short standalone passage is
simply chapter *n* of book 1 of its work; a real text uses its own book and
chapter numbers.
## The task — when the user uploads Greek
Two things happen for every new reading. Do both.
1. **Write the chapter file** — one JSON object at
`public/data/<work>/<book>/<chapter>.json` (schema below).
2. **File it in the manifest** — one entry appended to that work's `chapters`
array in `public/data/manifest.json`, in reading order, and bump the book's
chapter count.
That is the whole job. There is no quiz to assign it to, no deck to build and no
question bank to write — see "What you do not write" below.
## Chapter object schema
```js
{
work: "grk30", // work id; matches the folder under data/
book: 1,
chapter: 1, // position in the book; the URL is
// /read/<work>/<book>/<chapter>
slug: "ody5-55-94", // unique, no spaces — stable id for the reading
title: "Odyssey 5.55–94", // "<Work> <book>.<lines>"
citation: "Homer, Odyssey 5.55–94 — Hermes comes to Calypso's island",
defaultSrc: "human-verified", // provenance of the analysis; see below
segments: [ // each segment = one aligned Greek↔English row
{
lines: "56–57", // verse line/range, or a prose locus ("230b",
// "1.1.1"). Verse: label EVERY segment. Prose:
// label the first segment of each block; the
// engine carries the label forward.
translation: "literal English for this chunk",
greek: [ // one token per word, in order
{ w:"μῆνιν", // the Greek word as it appears
lemma:"μῆνις, -ιος, ἡ",
pos:"noun",
parse:"acc. sg. fem.",
gloss:"wrath, anger",
note:"direct object of ἄειδε" },
{ w:".", punct:true } // punctuation: renders but is NOT clickable
]
}
]
}
```
## Manifest entry
`public/data/manifest.json` is what the contents page reads. Each work carries its
own `chapters` array:
```js
{ book: 1,
chapter: 1,
slug: "ody5-55-94",
title: "Odyssey 5.55–94",
segments: 34, // segments in the file
tokens: 274, // non-punctuation tokens
bytes: 52441 } // the file's own size
```
The three numbers are just the file's own figures; they drive the word counts on
the contents page. A new work needs an object of its own with `id`, `title`,
`author`, an optional one-line `note`, `defaultSrc`, and a `books` array.
## Provenance — say where the analysis came from
`defaultSrc` records the provenance of a chapter's analysis, and any single token
may override it with its own `src`. It shows in the word panel as a badge.
- `treebank` — from a gold-standard dependency treebank
- `morpheus+model` — an analyser licensed the parse; a model chose among the options
- `model-only` — no analyser coverage (crasis, proper nouns, corruption)
- `human-verified` — a person checked it
Be honest with this field. The whole point of carrying it is that generated
analysis must never quietly present itself as checked analysis. If you parsed a
reading and nobody has read it back against the text, it is not `human-verified`.
## Conventions
- **Layout:** Greek on the LEFT, literal English on the RIGHT, aligned per segment.
- **Segment size:** clause/sentence-sized. This matters more than it looks: the
segment is the unit the practice window is cut out of, and the unit a citation
points at. Aim for ~8–15 Greek words. Never one giant segment per chapter.
- **Translation style:** **as literal as possible while still reading sensibly** —
faithful to case, tense, and word force, but not so wooden it stops making sense.
Not smooth idiomatic paraphrase. (Practice uses these as its model translation
answers, so they must be defensible word by word.)
- **Parsing style:** spell it out in full — "aorist active indicative, 3rd singular",
not "aor. act. ind. 3 sg." (lemma/pos can stay conventional).
- **Every Greek word** is its own clickable token with lemma / pos / parse / gloss,
plus a syntax `note` where useful. The `note` is not decoration: practice reuses
it as the explanation for auto-generated parsing questions, so write it as a
reason, not a label.
- **Line weight:** the engine scores a verse segment by its line range and a prose
segment at ~8 Greek words to the line, so a "20-line window" means the same amount
of reading in Homer and in Plato. Label `lines` honestly and this takes care of
itself.
## What you do not write
The reader derives these from the token data. Do not hand-write them.
- **Practice questions** — translation, parsing and vocabulary questions are
generated, and the answer key *is* the token's lemma/parse/gloss and the
segment's translation. They cannot drift out of sync with the text, and they
come free with a well-parsed reading.
- **Vocabulary decks** — the Anki deck is built from the tokens. General
vocabulary is included; the article, particles, prepositions, pronouns,
conjunctions and interjections are skipped. The deck nests under its book, so a
whole book can be drilled at once.
The GRK30 site also had hand-written "what construction is this, and why"
questions. Those are dropped here: 327 of them covered 16 short readings, which
does not scale to a whole text. If you want that kind of explanation, put it in a
token's `note`, where it is attached to the word it is about and costs nothing to
maintain.
## Checking your work
Two checks catch nearly everything that matters.
1. **The tokens must reconstruct the text.** Concatenate every `w` in order and
compare against the source. This catches dropped words, invented words,
reordering and skipped lines — the failures that would poison every drill,
question and deck built on top of the reading.
2. **The morphology must be bounded.** A model asked to parse freely produces
confident wrong answers a student has no way to catch. Constrain it to the
analyses the surface form actually licenses and let it choose among them. A
parse outside that set is a bug, not a judgement call.
Then open `/read/<work>/<book>/<chapter>` and read it.
Latin LAT30-CLAUDE.md view raw
# Classics Reader — Latin readings
Context for Claude when preparing a Latin reading for the Classics Reader.
Adapted from the CLAUDE.md of the LAT30 course site this reader grew out of.
That site organized everything around the **quiz** it would be tested on. This
one does not: **the unit is a reading — one chapter, one file.** A reading is
complete on its own, stands on its own, and is added without reference to
anything else.
This is the Latin twin of the Greek spec beside it. Same engine, same workflow,
same file layout — the differences are Latin's, not the project's.
## What this is
An interactive reader for whole Latin texts. Latin on the left, a literal English
translation on the right, aligned clause by clause; every word is a clickable
token carrying lemma, part of speech, parse, gloss and a syntax note. From that
same token data the reader derives a parse trainer, form drills, practice
questions and Anki vocabulary decks.
Readings are addressed as `work / book / chapter`. A short standalone passage is
simply chapter *n* of book 1 of its work; a real text uses its own book and
chapter numbers.
## The task — when the user uploads Latin
Two things happen for every new reading. Do both.
1. **Write the chapter file** — one JSON object at
`public/data/<work>/<book>/<chapter>.json` (schema below).
2. **File it in the manifest** — one entry appended to that work's `chapters`
array in `public/data/manifest.json`, in reading order, and bump the book's
chapter count.
That is the whole job. There is no quiz to assign it to, no deck to build and no
question bank to write — see "What you do not write" below.
## Chapter object schema
```js
{
work: "lat30", // work id; matches the folder under data/
book: 1,
chapter: 1, // position in the book; the URL is
// /read/<work>/<book>/<chapter>
slug: "aen4-1-30", // unique, no spaces — stable id for the reading
title: "Aeneid 4.1–30", // "<Work> <book>.<lines>"
citation: "Vergil, Aeneid 4.1–30 — Dido confesses her love to Anna",
defaultSrc: "human-verified", // provenance of the analysis; see below
segments: [ // each segment = one aligned Latin↔English row
{
lines: "1–2", // verse line/range, or a prose section ("1.3").
// Verse: label EVERY segment. Prose: label the
// first segment of each section; the engine
// carries the label forward.
translation: "literal English for this chunk",
greek: [ // one token per word, in order. NOTE the field
// is `greek` in Latin chapters too: it predates
// the reader having a second language and means
// "the source-language tokens" either way.
{ w:"regina", // the Latin word as it appears in the text
lemma:"rēgīna, -ae, f.",
pos:"noun",
parse:"nom. sg. fem.",
gloss:"queen",
note:"subject of the sentence; Dido is not named until line 9" },
{ w:".", punct:true } // punctuation: renders but is NOT clickable
]
}
]
}
```
## Manifest entry
`public/data/manifest.json` is what the contents page reads. Each work carries its
own `chapters` array:
```js
{ book: 1,
chapter: 1,
slug: "aen4-1-30",
title: "Aeneid 4.1–30",
segments: 30, // segments in the file
tokens: 210, // non-punctuation tokens
bytes: 41022 } // the file's own size
```
The three numbers are just the file's own figures; they drive the word counts on
the contents page. A new work needs an object of its own with `id`, `title`,
`author`, an optional one-line `note`, `defaultSrc`, and a `books` array.
## Provenance — say where the analysis came from
`defaultSrc` records the provenance of a chapter's analysis, and any single token
may override it with its own `src`. It shows in the word panel as a badge.
- `treebank` — from a gold-standard dependency treebank
- `morpheus+model` — an analyser licensed the parse; a model chose among the options
- `model-only` — no analyser coverage (proper nouns, corruption)
- `human-verified` — a person checked it
Be honest with this field. The whole point of carrying it is that generated
analysis must never quietly present itself as checked analysis. If you parsed a
reading and nobody has read it back against the text, it is not `human-verified`.
## Conventions
- **Layout:** Latin on the LEFT, literal English on the RIGHT, aligned per segment.
- **Segment size:** clause/sentence-sized. This matters more than it looks: the
segment is the unit the practice window is cut out of, and the unit a citation
points at. Aim for ~7–12 Latin words. Never one giant segment per chapter.
- **Translation style:** **as literal as possible while still reading sensibly** —
faithful to case, tense, and word force, but not so wooden it stops making sense.
Not smooth idiomatic paraphrase. (Practice uses these as its model translation
answers, so they must be defensible word by word.) In verse, do NOT reorder into
prose order silently — if the word order is doing something, say so in a `note`.
- **Macrons:** the `w` field is the text as the edition prints it (normally without
macrons). The `lemma` field DOES carry macrons — it is what the vocab deck drills.
Be consistent: the deck's Front is the lemma string character for character.
- **Lemma format:**
- noun — `rēgīna, -ae, f.` (nom., gen., gender)
- adjective — `magnus, -a, -um` / `fortis, -e` / `ingēns, -entis`
- verb — all four principal parts: `amō, amāre, amāvī, amātum`
(deponent: three — `sequor, sequī, secūtus sum`)
- **Parsing style:** spell it out in full — "perfect active indicative, 3rd singular",
not "perf. act. ind. 3 sg." Nominals may abbreviate case/number/gender in the usual
way ("abl. sg. fem."), which is what the parse trainer expects.
- Participles get both halves: "perfect passive participle, feminine ablative singular".
- Deponents are labelled **deponent**, not passive: "perfect deponent indicative,
3rd singular". The parse trainer offers deponent as its own voice.
- Gerund, gerundive, and supine are moods as far as the parser is concerned —
write "gerundive, neuter nominative singular", "gerund, genitive singular",
"supine, accusative".
- **Every Latin word** is its own clickable token with lemma / pos / parse / gloss,
plus a syntax `note` where useful. The `note` is not decoration: practice reuses
it as the explanation for auto-generated parsing questions, so write it as a
reason, not a label. Word order in verse is worth a note when it is doing work —
hyperbaton, golden lines, enjambment, framing.
- **Line weight:** the engine scores a verse segment by its line range and a prose
segment at ~7 Latin words to the line, so a "20-line window" means the same amount
of reading in Vergil and in Cicero. Label `lines` honestly and this takes care of
itself.
## What you do not write
The reader derives these from the token data. Do not hand-write them.
- **Practice questions** — translation, parsing and vocabulary questions are
generated, and the answer key *is* the token's lemma/parse/gloss and the
segment's translation. They cannot drift out of sync with the text, and they
come free with a well-parsed reading.
- **Vocabulary decks** — the Anki deck is built from the tokens. General
vocabulary is included; particles, prepositions, pronouns, conjunctions and
interjections are skipped. The deck nests under its book, so a whole book can be
drilled at once, and Latin decks live in their own tree, separate from Greek.
The LAT30 site also had hand-written "what construction is this, and why"
questions — ablative absolute, indirect statement, purpose and result clauses,
`cum`-clauses, the gerundive of obligation, and the rest. Those are dropped here:
they do not scale to a whole text. If you want that kind of explanation, put it in
a token's `note`, where it is attached to the word it is about and costs nothing
to maintain.
## Checking your work
Two checks catch nearly everything that matters.
1. **The tokens must reconstruct the text.** Concatenate every `w` in order and
compare against the source. This catches dropped words, invented words,
reordering and skipped lines — the failures that would poison every drill,
question and deck built on top of the reading.
2. **The morphology must be bounded.** A model asked to parse freely produces
confident wrong answers a student has no way to catch. Constrain it to the
analyses the surface form actually licenses and let it choose among them. A
parse outside that set is a bug, not a judgement call.
Careful with shell dedup checks on macronised Latin: macOS can hand you the same
word in two Unicode normalisations (precomposed `ē` versus `e` plus a combining
macron), and a UTF-8 locale may also collate `e` and `ē` as equal. Use `LC_ALL=C`,
or check in Python.
Then open `/read/<work>/<book>/<chapter>` and read it.