tonus 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +3 -3
  3. package/dist/census.d.ts +4 -0
  4. package/dist/census.js +23 -0
  5. package/dist/corpus.d.ts +4 -0
  6. package/dist/corpus.js +17 -0
  7. package/dist/data/am.js +10220 -3006
  8. package/dist/data/attestation.js +38640 -4568
  9. package/dist/data/attestation.json +38639 -4567
  10. package/dist/data/census.d.ts +2 -2
  11. package/dist/data/census.js +4 -4
  12. package/dist/data/corpus-overlap.js +5 -118
  13. package/dist/data/gr.d.ts +29 -2
  14. package/dist/data/gr.js +2436 -6411
  15. package/dist/data/kyriale.d.ts +1 -1
  16. package/dist/data/kyriale.js +38 -38
  17. package/dist/data/la.js +12369 -1103
  18. package/dist/data/lh.js +3139 -158
  19. package/dist/data/lu.js +17332 -2379
  20. package/dist/data/nocturnale-romanum.js +10656 -1899
  21. package/dist/data/psm.js +463 -33
  22. package/dist/data/types.d.ts +1 -0
  23. package/dist/engines/chant/chant.js +60 -13
  24. package/dist/engines/chant/data/masses.js +18 -18
  25. package/dist/engines/chant/ordinary.js +12 -5
  26. package/dist/engines/chant/psalm.js +4 -0
  27. package/dist/engines/chant/types.d.ts +32 -4
  28. package/dist/engines/chant/types.js +47 -4
  29. package/dist/engines/score/emitters/moderna.js +150 -12
  30. package/dist/engines/score/infer.js +1 -1
  31. package/dist/engines/temper/gabc.d.ts +12 -0
  32. package/dist/engines/temper/gabc.js +51 -18
  33. package/dist/index.d.ts +5 -9
  34. package/dist/inscriptio.d.ts +5 -0
  35. package/dist/inscriptio.js +23 -0
  36. package/dist/score.d.ts +6 -0
  37. package/dist/score.js +20 -0
  38. package/docs/api/calendar.md +4 -4
  39. package/docs/api/census.md +31 -22
  40. package/docs/api/chant.md +102 -82
  41. package/docs/api/heavens.md +7 -7
  42. package/docs/api/index.md +40 -6
  43. package/docs/api/score.md +44 -44
  44. package/docs/api/tuning.md +14 -14
  45. package/package.json +22 -4
  46. package/dist/data/ams.d.ts +0 -5
  47. package/dist/data/ams.js +0 -122
  48. package/dist/data/cot.d.ts +0 -5
  49. package/dist/data/cot.js +0 -172
  50. package/dist/data/cse.d.ts +0 -5
  51. package/dist/data/cse.js +0 -122
package/docs/api/chant.md CHANGED
@@ -9,9 +9,9 @@ carries page-level provenance back to its book.
9
9
 
10
10
  - [Chant](#chant)
11
11
  - [The corpora](#the-corpora)
12
- - [The cut](#the-cut)
12
+ - [The shelf is the books](#the-shelf-is-the-books)
13
13
  - [The books — `corpus`](#the-books--corpus)
14
- - [The ledger of the cut — `full`](#the-ledger-of-the-cut--full)
14
+ - [What the book holds — `full`](#what-the-book-holds--full)
15
15
  - [Retrieval — `cantus`](#retrieval--cantus)
16
16
  - [Reaching the ordinary — `ordinary`](#reaching-the-ordinary--ordinary)
17
17
  - [On chant ids](#on-chant-ids)
@@ -30,33 +30,29 @@ carries page-level provenance back to its book.
30
30
 
31
31
  ## The corpora
32
32
 
33
- Nine Solesmes books, extracted from
33
+ Six Solesmes books, extracted from
34
34
  [GregoBase](https://gregobase.selapa.net/), joined by the Divinum Officium
35
35
  propers, office, and psalter, plus the Nocturnale Romanum for the night office:
36
36
 
37
- | Source | Book | Edition | Chants |
38
- | ------ | --------------------------------- | ------------------ | ------ |
39
- | `gr` | Graduale Romanum | Solesmes, 1961 | 780 |
40
- | `lu` | The Liber Usualis | Solesmes, 1961 | 707 |
41
- | `la` | Liber antiphonarius | Solesmes, 1960 | 160 |
42
- | `lh` | Liber Hymnarius | Solesmes, 1983 | 25 |
43
- | `am` | Antiphonale Monasticum | Solesmes, 1934 | 576 |
44
- | `ams` | Antiphonale Monasticum Solesmense | Solesmes, 1935 | 11 |
45
- | `psm` | Psalterium Monasticum | Solesmes, 1981 | 11 |
46
- | `cse` | Cantus selecti | Solesmes, 1957 | 11 |
47
- | `cot` | Chants of the Church | Solesmes, 1956 | 16 |
48
- | `nr` | Nocturnale Romanum | Sandhofe, 2002 | 470 |
49
-
50
- **2,187 chants in all**, plus the Mass ordinary (120 settings, reached by
51
- [`ordinary`](#reaching-the-ordinary--ordinary) rather than by book). The books
52
- hold 10,156 between them; tonus ships only what the calendar calls for on some
53
- day of the year, so a chant with no day to be sung on is not here. See
54
- [The cut](#the-cut) below.
55
-
56
- The ten books list 2,767 rows for those 2,187 chants: a melody printed in two
57
- books is stored once and listed under both.
58
-
59
- `am`, `ams`, and `psm` are the monastic (Benedictine) books; the rest are
37
+ | Source | Book | Edition | Chants |
38
+ | ------ | ----------------------- | -------------- | ------ |
39
+ | `gr` | Graduale Romanum | Solesmes, 1961 | 1,373 |
40
+ | `lu` | The Liber Usualis | Solesmes, 1961 | 2,446 |
41
+ | `la` | Liber antiphonarius | Solesmes, 1960 | 2,506 |
42
+ | `lh` | Liber Hymnarius | Solesmes, 1983 | 361 |
43
+ | `am` | Antiphonale Monasticum | Solesmes, 1934 | 1,448 |
44
+ | `psm` | Psalterium Monasticum | Solesmes, 1981 | 60 |
45
+ | `nr` | Nocturnale Romanum | Sandhofe, 2002 | 1,564 |
46
+
47
+ **7,840 chants in all**, the Mass ordinary among them (120 settings, reached by
48
+ [`ordinary`](#reaching-the-ordinary--ordinary) rather than by book). That is the
49
+ books themselves: tonus carries what they print, whether or not the calendar
50
+ reaches it.
51
+
52
+ The seven books list 9,758 rows for those 7,840 chants: a melody printed in two
53
+ books is stored once and listed under each, with each book's own page citation.
54
+
55
+ `am` and `psm` are the monastic (Benedictine) books; the rest are
60
56
  Roman. Every book here bears the rhythmic markings the score engine reads; that
61
57
  is the admission rule. `nr` is the night-office repertoire (responsories,
62
58
  antiphons) from the
@@ -64,37 +60,48 @@ antiphons) from the
64
60
  community restitution, the one non-Solesmes source, admitted because it carries
65
61
  those marks too.
66
62
 
67
- ### The cut
63
+ ### The shelf is the books
68
64
 
69
- The corpus is **assignment-driven**: a chant ships when some day of the
70
- liturgical year calls for it. The calendar is walked year by year until it stops
71
- finding new assignments (39 years, in the event), and what it never reaches is
72
- not shipped — 10,156 book chants become 2,187.
65
+ The corpus was **assignment-driven** until 2026-08-31: a chant shipped only when
66
+ some day of the liturgical year called for it, and 10,156 book chants became
67
+ 2,187. That gate is gone. The shelf now carries the books whole.
73
68
 
74
- Everything here answers "what was sung on this day". A query for a chant the
75
- calendar never calls for returns nothing.
69
+ The reason is that the gate answered four questions with one silence. A chant
70
+ absent from the shelf might be genuinely superseded repertoire, or printed in an
71
+ edition the calendar does not address, or proper to a sanctorale the calendar
72
+ does not enumerate, or simply missed by the matcher that places texts on days —
73
+ and nothing downstream could tell those apart, because the chant was not there
74
+ to ask about. Worse, the population every `census` measurement was taken against
75
+ was itself the product of that gate, so "how typical is this chant" quietly meant
76
+ "how typical among the chants one rite happened to call for".
77
+
78
+ Whether a day calls for a chant is now a question for the day verbs — `festum`,
79
+ `proprium`, `officium` — which answer it directly and say what their tables
80
+ hold. It is not a filter on the shelf. `cantus` and `corpus` ask what the books
81
+ print; that a chant has no day to be sung on is a fact about the calendar, not a
82
+ reason it should be unfindable.
76
83
 
77
84
  ## The books — `corpus`
78
85
 
79
86
  `corpus(code)` returns one book's bibliographic identity and a breakdown of what
80
- it holds — how many chants, in what genres, in what modes. `corpus({ book })` is
87
+ it holds: how many chants, in what genres, in what modes. `corpus({ book })` is
81
88
  the same question in the query form every other verb uses; both spellings return
82
89
  one answer.
83
90
 
84
- `corpus()` with no argument returns **the whole shelf** — the rollup plus every
91
+ `corpus()` with no argument returns **the whole shelf**, the rollup plus every
85
92
  book's ledger:
86
93
 
87
94
  ```js
88
95
  tonus.corpus();
89
- // { count: 2187, // chants tonus holds, each counted once
90
- // listings: 2767, // rows on the shelf — a chant in two books appears twice
91
- // total: 10156, // what the books hold, before the cut
92
- // genera: [ { office: "an", genus: "Antiphona", count: 693 }, … ],
93
- // modes: [ { mode: "1", modus: "Modus I", count: 392 }, … ],
94
- // books: [ …10 Corpus entries, in registry order ] }
96
+ // { count: 7840, // chants tonus holds, each counted once
97
+ // listings: 9758, // rows on the shelf — a chant in two books appears twice
98
+ // total: 9811, // what the books hold, before dedup
99
+ // genera: [ { office: "an", genus: "Antiphona", count: 3959 }, … ],
100
+ // modes: [ { mode: "1", modus: "Modus I", count: 1520 }, … ],
101
+ // books: [ …7 Corpus entries, in registry order ] }
95
102
  ```
96
103
 
97
- **`count` is the number of chants** — the one to quote. `listings` is how long
104
+ **`count` is the number of chants**, the one to quote. `listings` is how long
98
105
  the shelf is: a melody printed in several books is stored once and listed under
99
106
  each, so the shelf runs longer than the repertory. The breakdowns describe the
100
107
  same population `count` does, so `genera` and `modes` sum to it.
@@ -128,42 +135,44 @@ interface Corpus {
128
135
  }
129
136
  ```
130
137
 
131
- ### The ledger of the cut — `full`
138
+ ### What the book holds — `full`
132
139
 
133
- Every `Corpus` carries `full`: what the book HELD, before the keep set ran, in
140
+ Every `Corpus` carries `full`: what the book holds in GregoBase's catalogue, in
134
141
  the same genera/modes shape as the shipped counts.
135
142
 
136
143
  ```js
137
144
  const am = tonus.corpus("am");
138
- am.count; // 576 — antiphons and the rest tonus kept
139
- am.full.total; // 1456 — what the Antiphonale Monasticum holds
140
- am.genera[0]; // { office: "an", genus: "Antiphona", count: 458 }
145
+ am.count; // 1448 — chants tonus lists under the Antiphonale
146
+ am.full.total; // 1456 — what GregoBase catalogues for the book
147
+ am.genera[0]; // { office: "an", genus: "Antiphona", count: 1045 }
141
148
  am.full.genera[0]; // { office: "an", genus: "Antiphona", count: 1049 }
142
149
  ```
143
150
 
144
- Reading the two tallies side by side names what was left out — 1,049 antiphons
145
- in the book, 458 sung.
151
+ The two now sit close, because the shelf carries the book rather than a
152
+ selection from it. What remains between them is what the extractor drops for
153
+ reasons of its own — a chant GregoBase marks as a duplicate of another, or one
154
+ whose GABC will not parse — not a liturgical judgement.
146
155
 
147
- Only the extractor can measure this. By the time tonus loads, the keep set has
148
- already run, so the pre-cut tally is read from an artifact rather than derived.
149
- Every shelved book reports one, including the Nocturnale, whose tally comes
150
- from its own extract rather than from GregoBase.
156
+ Only the extractor can measure `full`. By the time tonus loads, its record is
157
+ one per chant, so the catalogue tally is read from an artifact rather than
158
+ derived. Every shelved book reports one, including the Nocturnale, whose tally
159
+ comes from its own extract rather than from GregoBase.
151
160
 
152
161
  The metadata is drawn from GregoBase's own catalogue. The `genera` list is the
153
162
  office distribution (descending by count); `modes` counts modes I–VIII, with a
154
163
  final `mode: null` bucket for chants outside the eight modes (psalm tones and the
155
164
  like) so the counts reconcile with `count`.
156
165
 
157
- **Overlap.** tonus keeps one copy of each chant (the Liber Usualis is the primary
158
- source; the Antiphonarius and Hymnarius fill gaps), so a book's stored `count`
159
- undercounts what it holds. `total` is the full pre-dedup count, `unique` the
166
+ **Overlap.** tonus stores one copy of each chant — the Liber Usualis owns the
167
+ record where several books print it — but LISTS it under every book that does,
168
+ so `count` is what a book prints rather than what it happens to store. `total` is the full pre-dedup count, `unique` the
160
169
  chants a book alone has, and `shared` how many it holds in common with each other
161
170
  book (by GregoBase chant id). These reveal, for instance, that the Liber Usualis
162
171
  is largely the Graduale and the Antiphonarius bound together (it shares hundreds
163
172
  of chants with each), while the Antiphonale Monasticum is almost entirely its own.
164
173
 
165
174
  The Nocturnale (`nr`) is compared differently, because it has no GregoBase
166
- catalogue: its counts come from its own extract, and it shares **nothing** —
175
+ catalogue: its counts come from its own extract, and it shares **nothing**, so
167
176
  `unique` is all 1,564 chants it holds. That is a measurement, not a gap. The
168
177
  nocturnale–GregoBase crosswalk is a route to metadata, not a claim that the two
169
178
  books print the same chant, so it does not count as sharing.
@@ -234,11 +243,20 @@ name:
234
243
 
235
244
  | `office` | `genus` | `office` | `genus` | `office` | `genus` |
236
245
  | -------- | --------- | -------- | ------------ | -------- | ------------------ |
237
- | `an` | Antiphona | `hy` | Hymnus | `rb` | Responsorium Breve |
238
- | `al` | Alleluia | `in` | Introitus | `se` | Sequentia |
239
- | `ca` | Canticum | `of` | Offertorium | `tr` | Tractus |
240
- | `co` | Communio | `ps` | Psalmus | `tp` | Tonus Peregrinus |
241
- | `gr` | Graduale | `re` | Responsorium | `or` | Ordinarium |
246
+ | `an` | Antiphona | `hy` | Hymnus | `se` | Sequentia |
247
+ | `al` | Alleluia | `in` | Introitus | `tr` | Tractus |
248
+ | `ca` | Canticum | `of` | Offertorium | `tp` | Tropa |
249
+ | `co` | Communio | `ps` | Psalmus | `or` | Toni Communes |
250
+ | `gr` | Graduale | `re` | Responsorium | `ky` | Kyriale |
251
+ | `im` | Improperia| `rb` | Resp. Breve | `va` | Varia |
252
+ | `pa` | Prosa | `su` | Supplicatio | | |
253
+
254
+ The labels are GregoBase's own vocabulary (`$txt['usage']`), not tonus's
255
+ gloss. Three were corrected in 0.10.0: `tp` reads **Tropa** (it had said
256
+ "Tonus Peregrinus"), `or` reads **Toni Communes** (it had said "Ordinarium"),
257
+ and `pa` reads **Prosa**. `ky` is the Mass ordinary's roll-up genus — the
258
+ part itself rides `ordinary` (`ke`, `gl`, `cr`, …), and `va` is the
259
+ catch-all for a chant whose office-part tonus does not recognise.
242
260
 
243
261
  ```ts
244
262
  interface Chant {
@@ -249,7 +267,9 @@ interface Chant {
249
267
  genus: string; // Latin genre name, "Antiphona", "Introitus" …
250
268
  mode: string | null; // raw from source: "1"–"8", differentia forms ("2d", "8g"), "p"/"d"/"e" …
251
269
  modus: string | null; // Latin mode name, "Modus I"–"Modus VIII"
252
- pages: { page: string; sequence: number; extent: number }[];
270
+ books: (ChantSource | "user" | "ky")[]; // every book that prints it
271
+ pages: { page: string; sequence: number; extent: number }[]; // the presented book's
272
+
253
273
  source: {
254
274
  book: string;
255
275
  year: number | null;
@@ -282,7 +302,7 @@ interface CantusQuery {
282
302
  The Mass ordinary is addressable but not shelved. `ordinary` is the door:
283
303
 
284
304
  ```js
285
- tonus.cantus({ ordinary: "ky" }); // all 31 Kyries
305
+ tonus.cantus({ ordinary: "ke" }); // all 31 Kyries
286
306
  tonus.cantus({ ordinary: "gl", mode: 4 }); // mode-4 Glorias
287
307
  tonus.cantus({ ordinary: ["as", "va"] }); // the sprinkle antiphons
288
308
  ```
@@ -295,18 +315,18 @@ A plain search does not sweep it in: `{ mode: 5 }` returns the shelf. Ask for a
295
315
  Kyrie and you get Kyries.
296
316
 
297
317
  For the setting a given DAY calls for, [`ordinarium`](#the-ordinary--ordinarium)
298
- is the verb — it applies the Kyriale's own rubrics. This is flat retrieval.
318
+ is the verb, and it applies the Kyriale's own rubrics. This is flat retrieval.
299
319
 
300
320
  ### On chant ids
301
321
 
302
- An id's prefix names **the catalogue the identifier came from** — not the book
322
+ An id's prefix names **the catalogue the identifier came from**, not the book
303
323
  the chant is printed in, and not a claim about who the melody belongs to. A
304
324
  chant carrying `gregobase:1210` is a Solesmes book chant that GregoBase happens
305
- to have catalogued; the corpus is assembled from ten books, and GregoBase is
325
+ to have catalogued; the corpus is assembled from seven books, and GregoBase is
306
326
  one source among several.
307
327
 
308
328
  The prefix is therefore **not a namespace you can query against**. GregoBase
309
- holds 18,148 chants; tonus ships 1,717 of them — 9.5% — because the corpus is
329
+ holds 18,148 chants; tonus ships 1,717 of them (9.5%), because the corpus is
310
330
  assignment-driven, so an id copied from the GregoBase site will usually return
311
331
  `[]` here. That is not a lookup failure; it means no day of the calendar calls
312
332
  for that chant. The two prefixes in the shipped corpus are `gregobase:` (1,717)
@@ -322,7 +342,7 @@ to a chant rather than to a printing.
322
342
  `before: 1098` keeps only chants a manuscript of the 10th century or earlier
323
343
  already holds. This is **evidence, not existence**: the dates come from
324
344
  CANTUS's manuscript index, a terminus ante quem, so the filter answers "what
325
- is attested by then," never "what existed then" — and a chant with no dated
345
+ is attested by then," never "what existed then." A chant with no dated
326
346
  witness is excluded rather than assumed old. CANTUS dates only to the century,
327
347
  so a year admits the centuries that have CLOSED before it (`before: 1098` →
328
348
  through the 900s).
@@ -339,7 +359,7 @@ tonus.ordinarium({ feast: easter }); // the ordinary the view attests
339
359
  ```
340
360
 
341
361
  What happens to a slot the view excludes differs by verb, on the rubric's
342
- own logic: `ordinarium` **re-picks** — the Kyriale offers ranked
362
+ own logic: `ordinarium` **re-picks**, because the Kyriale offers ranked
343
363
  alternatives by design, so the rotation runs over the admissible pool and
344
364
  the day still sings. `proprium` and `officium` have no pool
345
365
  of alternatives, so an excluded chant **falls silent**. A `before` given to a
@@ -371,15 +391,15 @@ interface PropriumQuery extends CantusQuery {
371
391
  ## The ordinary — `ordinarium`
372
392
 
373
393
  `ordinarium(query?)` retrieves the fixed chants of the Mass from the
374
- Kyriale. A feast drives mass selection through its `masses` list — the
394
+ Kyriale. A feast drives mass selection through its `masses` list, the
375
395
  masses the day's Kyriale RUBRIC appoints, derived as described in
376
396
  [calendar.md](calendar.md#the-days-feasts--festum); `mass` pins a kyriale
377
397
  number directly. Where the rubric names several masses, the year rotates
378
398
  through them (same feast, same year → same answer, every time), and sibling
379
399
  printings under one number (Mass I prints two dismissals; Mass XVII prints
380
400
  Kyrie A/B/C) rotate with it. Slots resolve independently, which the book
381
- licenses outright — "chants from one Mass may be used together with those
382
- from others" — with one exception, the book's own: **"the Ferial Masses
401
+ licenses outright ("chants from one Mass may be used together with those
402
+ from others") with one exception, the book's own: **"the Ferial Masses
383
403
  excepted."** Under a ferial rubric the sung ordinary is not gathered from
384
404
  several masses; only the dismissal travels.
385
405
 
@@ -397,10 +417,10 @@ tonus.ordinarium({ feast: easter });
397
417
 
398
418
  The **Gloria follows the day's rank rubric, not its season**: the ferial
399
419
  masses print none (XVI, XVIII) and the penitential-Sunday mass none (XVII),
400
- while a I-class feast inside Advent or Lent — the Immaculate Conception —
420
+ while a I-class feast inside Advent or Lent (the Immaculate Conception)
401
421
  keeps its Gloria. At a Gloria-less Mass the dismissal is the Benedicamus
402
422
  Domino, and a mass with no dismissal of its own borrows one exactly as the
403
- book directs: "Benedicamus Domino **as in Mass II**" — so a green feria
423
+ book directs: "Benedicamus Domino **as in Mass II**," so a green feria
404
424
  sings Mass XVI whole with the Mass II Benedicamus. The ad libitum appendix
405
425
  is a **solemnity boost**, reachable only under the festal rubrics (it takes
406
426
  its turn in the rotation once every _n + 1_ years); it never reaches a
@@ -418,13 +438,13 @@ and selected by season: **Vidi aquam** (`va`) in Paschaltide, **Asperges
418
438
  me** (`as`) otherwise.
419
439
 
420
440
  ```js
421
- tonus.ordinarium({ ordinary: "ky" }); // every Kyrie
441
+ tonus.ordinarium({ ordinary: "ke" }); // every Kyrie
422
442
  tonus.ordinarium({ mass: 9, ordinary: "gl" }); // Gloria of Cum jubilo
423
443
  ```
424
444
 
425
445
  | `ordinary` | `ordinarium` |
426
446
  | ---------- | --------------------------------------------- |
427
- | `ky` | Kyrie eleison |
447
+ | `ke` | Kyrie eleison |
428
448
  | `gl` | Gloria |
429
449
  | `cr` | Credo |
430
450
  | `sa` | Sanctus |
@@ -475,7 +495,7 @@ seasonal ordo and returned in liturgical order. With no feast, each resolves
475
495
  for the [default epoch](index.md#dates).
476
496
 
477
497
  **Matins is returned flat.** The night office answers like any other hour,
478
- its responsories drawn from the Nocturnale Romanum (`nr`) — but the
498
+ its responsories drawn from the Nocturnale Romanum (`nr`). But the
479
499
  three-nocturn, twelve-psalm division is not modelled: the chants are right,
480
500
  their grouping into nocturns is not expressed.
481
501
 
@@ -492,7 +512,7 @@ interface OfficiumQuery extends CantusQuery {
492
512
  }
493
513
  ```
494
514
 
495
- The eight hours ship as [`HORAE`](index.md#the-appendix), Matins first — read
515
+ The eight hours ship as [`HORAE`](index.md#the-appendix), Matins first. Read
496
516
  them from there rather than transcribing them, and an unrecognised `hora`
497
517
  throws rather than matching nothing, so a misspelling cannot read as an empty
498
518
  hour.
@@ -507,9 +527,9 @@ tonus.officium({ hora: "vespers" }); // throws: unknown hora "vespers"
507
527
 
508
528
  ### One cursus, the Benedictine
509
529
 
510
- tonus assembles a single office — the monastic cursus — with no option to
530
+ tonus assembles a single office (the monastic cursus) with no option to
511
531
  choose another. The chants come from the Antiphonale Monasticum (`am`) and its
512
- companions; the psalmody follows the Benedictine distribution — the little
532
+ companions; the psalmody follows the Benedictine distribution, the little
513
533
  hours take the gradual psalms (Terce 119–121, Sext 122–124, None 125–127),
514
534
  with Sunday and Monday walking their portions of Ps 118 instead; Prime walks
515
535
  Pss 1–19 across the week (Sunday opens Ps 118); and Compline is the fixed
@@ -530,7 +550,7 @@ the opening formula is included, as it is for a psalm's first verse.
530
550
  mediant, as a psalm sung without an antiphon; `solemn` uses a tone's
531
551
  ornamented mediant where it has one. Canticles are addressed by name:
532
552
  `benedictus`, `magnificat`, `nunc dimittis`, `benedicite`. (The Te Deum
533
- is not psalmody — it carries its own melody and is not addressable here.)
553
+ is not psalmody: it carries its own melody and is not addressable here.)
534
554
 
535
555
  ```js
536
556
  tonus.psalmus({ psalm: 109, verse: "1a", mode: 1 });
@@ -168,12 +168,12 @@ and moves with precession; a table carrying "March 21" would be wrong for most
168
168
  of the period this library models, and wrong differently every century.
169
169
 
170
170
  **What is omitted, and why.** The exaltation degrees Ptolemy gives (the Sun at
171
- 19° Arietis and the rest) are not carried — the sign is the resolution anything
172
- here reads. Nor are the lunar nodes' exaltations, because the nodes are not
171
+ 19° Arietis and the rest) are not carried, because the sign is the resolution
172
+ anything here reads. Nor are the lunar nodes' exaltations, because the nodes are not
173
173
  tonus bodies. Five signs exalt nobody: that silence is the tradition's, not a
174
174
  gap in the table.
175
175
 
176
- The `melothesia` is the *homo signorum* of medieval calendars — Aries at the
176
+ The `melothesia` is the *homo signorum* of medieval calendars, Aries at the
177
177
  head down to Pisces at the feet. It was practice, not decoration: phlebotomy
178
178
  was timed against it, and while the Moon stood in a sign its member was not to
179
179
  be touched. Sourced from Ptolemy's *Tetrabiblos* I.17 and I.19
@@ -216,7 +216,7 @@ The doctrinae:
216
216
  Sphere pitches are computed directly from the doctrina's pure ratios,
217
217
  anchored at the temperamentum's A4, so historical coherence holds:
218
218
  `temperamentum("ptolemy-intense")` with `harmonia({ doctrina: "ptolemy" })`
219
- gives pure Ptolemaic intervals throughout — Sun→Jupiter a pure 3/2,
219
+ gives pure Ptolemaic intervals throughout: Sun→Jupiter a pure 3/2,
220
220
  Sun→Saturn a pure 2/1. The temperamentum's scale governs pitch naming and
221
221
  the imprint.
222
222
 
@@ -322,7 +322,7 @@ h.tabula.find((r) => r.name === "Jupiter");
322
322
  ```
323
323
 
324
324
  `ratio` is the doctrina's own fraction against the mese, the primary datum of
325
- the whole scheme — `spn` and `hz` are that ratio sounded against A4, not
325
+ the whole scheme. `spn` and `hz` are that ratio sounded against A4, not
326
326
  independent claims. Comparing doctrinae means comparing these: the table
327
327
  under [Theory & Context](#theory--context) is what the field returns.
328
328
 
@@ -364,7 +364,7 @@ The doctrina ratios are reconstructed from the primary texts through
364
364
  Joscelyn Godwin's syntheses, mapping each body to a Greek tone-name and
365
365
  deriving its ratio by Pythagorean interval arithmetic normalized to the
366
366
  mese (Sun = 1/1). The full method, the taxonomy, and the decisions taken
367
- along the way are documented at the data — see `DOCTRINAE` in
367
+ along the way are documented at the data. See `DOCTRINAE` in
368
368
  [`harmonia/data/doctrines.ts`](https://github.com/jeffreypierce/tonus/blob/main/src/engines/harmonia/data/doctrines.ts).
369
369
  The same arithmetic is laid out from the tuning side in
370
370
  [tuning.md](tuning.md#theory--context).
@@ -385,7 +385,7 @@ The resulting ratios, by sphere from the outermost:
385
385
 
386
386
  The single pitch separating Pythagoras from Boethius is Venus: a whole
387
387
  tone above the Sun in the disjunct system (9/8, B durum), a semitone in
388
- the conjunct (256/243, B molle) — the origin of the durum/molle
388
+ the conjunct (256/243, B molle). This is the origin of the durum/molle
389
389
  distinction that runs through all of medieval music theory.
390
390
 
391
391
  ## Sources
package/docs/api/index.md CHANGED
@@ -4,7 +4,7 @@ The technical center of tonus: the full public API, the conventions every method
4
4
  obeys, and the error contract. The API is **fourteen methods on the `tonus`
5
5
  namespace**, no sub-namespaces.
6
6
 
7
- **[Orreliquum — the library at work →](https://jeffreypierce.github.io/orreliquum/)**
7
+ **[Interactive demo](https://orreliquum.com/)**
8
8
 
9
9
  ```js
10
10
  import tonus from "tonus";
@@ -12,6 +12,7 @@ import tonus from "tonus";
12
12
 
13
13
  - [The methods](#the-methods) — by engine
14
14
  - [The appendix](#the-appendix) — the canonical constant tables
15
+ - [The entries](#the-entries) — the four subpaths, and what each holds
15
16
  - [Full contents](#full-contents) — every method and section
16
17
  - [Conventions](#conventions) — Latin/English, dates, determinism, error contracts, bibliography
17
18
 
@@ -88,7 +89,7 @@ table of codes or English keeps English.
88
89
  | ----------- | -------------------------------------------------------------------------- |
89
90
  | `HORAE` | the eight canonical hours, Matins first — the order is the content |
90
91
  | `OFFICIA` | office code → the Latin genus (`an` → "Antiphona") |
91
- | `ORDINARIA` | ordinary code → the Latin name (`ky` → "Kyrie eleison") |
92
+ | `ORDINARIA` | ordinary code → the Latin name (`ke` → "Kyrie eleison") |
92
93
  | `MODI` | mode number → the Latin name (`"1"` → "Modus I") |
93
94
  | `SOURCES` | book code → its bibliographic record; the codes `cantus({ source })` takes |
94
95
 
@@ -123,9 +124,42 @@ is for.
123
124
  | `CENSUS_GROUPS` | the field groups → `{ offset, count }`; the keys are the valid `by:` values **and** the `profile` keys |
124
125
  | `CENSUS_ORDER` | every censused chant id, in block order — so membership is a lookup, not a `try/catch` |
125
126
 
126
- Use these to pool blocks without reproducing the distance rule — see [the census
127
+ Use these to pool blocks without reproducing the distance rule. See [the census
127
128
  contract](census.md#distance-is-cosine-per-field-group).
128
129
 
130
+ ## The entries
131
+
132
+ The fourteen methods and every table above are on the root, and stay there. Four
133
+ subpath entries hold what the root index was carrying without being asked to:
134
+ the **anatomy** of a return value, and the types whose values a caller had to
135
+ spell by hand against a query or an option bag.
136
+
137
+ ```js
138
+ import { cantus, corpus } from "tonus/corpus";
139
+ import { inscriptio } from "tonus/inscriptio";
140
+ import type { Note, Phrase } from "tonus/score";
141
+ ```
142
+
143
+ | Entry | Holds |
144
+ | ------------------ | ------------------------------------------------------------------------- |
145
+ | `tonus/corpus` | the shelf and its vocabulary — `cantus`, `corpus`, `SOURCES`, `HORAE` … |
146
+ | `tonus/score` | what a `Score` is made of — `Note`, `Phrase`, `Syllable`, `Metrics` … |
147
+ | `tonus/inscriptio` | the drawing surface — `inscriptio`, `Theme`, `TrackName`, `NoteGeometry` |
148
+ | `tonus/census` | one chant against the corpus — `census`, `CENSUS_BLOCK_FLOATS` |
149
+
150
+ Nothing is hidden by the split. The verbs stay on the namespace — the export law
151
+ puts them there — and the types a root signature names (`Score`, `Chant`,
152
+ `Cadence`, `Metrics`, `Imprint`) stay on the root as well, because the root's own
153
+ signatures name them. What moved off is what only a caller already **holding** a
154
+ `Score` could reach.
155
+
156
+ Each entry also surfaces types that were reachable through a signature but
157
+ nameable nowhere: `ChantSource` and `OfficeCode` (the codes `cantus({ source })`
158
+ and `office` take), `Theme` and `TrackName` (what an `opts` bag accepts), and
159
+ `CENSUS_BLOCK_FLOATS` (the stride a caller decoding a block had to get by
160
+ counting `CENSUS_GROUPS`). That drift — a value you must pass but cannot name —
161
+ is what the entries exist to stop.
162
+
129
163
  ## Full contents
130
164
 
131
165
  Every method and every section, page by page, in dependency order. Pages later in
@@ -218,7 +252,7 @@ Other fields carry only one register. Latin-only, _e.g._ `genus`, `ordinarium`,
218
252
  `incipit`, `differentia`, `accentus`. English-only, _e.g._ `date`, `velocity`, `hz`.
219
253
 
220
254
  Display strings live in exported maps (_e.g._ `SEASON_LABEL`),
221
- never as label fields on objects — the maps are [the appendix](#the-appendix).
255
+ never as label fields on objects. The maps are [the appendix](#the-appendix).
222
256
 
223
257
  ### Dates
224
258
 
@@ -238,14 +272,14 @@ an ensemble) it is seeded, so the same seed yields byte-identical output.
238
272
 
239
273
  ### Error contract
240
274
 
241
- - Query functions return `[]` on no match, never throw — but an **empty or
275
+ - Query functions return `[]` on no match, never throw. But an **empty or
242
276
  unknown-key query** throws (a mistyped filter is a bug, not an empty result):
243
277
  `festum({ month: 12 })` and `cantus({})` throw rather than silently resolving a
244
278
  plausible-looking answer.
245
279
  - Builder functions throw `Error` with a descriptive message on invalid input.
246
280
  - `notatio` throws on invalid `Chant` input.
247
281
  - `inscriptio` throws on a non-`Score` argument or an unknown notation species.
248
- - `temperamentum.tonus()` throws if `mode` is `"auto"` — mode must be set
282
+ - `temperamentum.tonus()` throws if `mode` is `"auto"`. Mode must be set
249
283
  explicitly.
250
284
  - Malformed `comma`, ratio, or Scala input throws `RangeError`; custom scales
251
285
  must supply 7 or 12 steps, beginning at `1/1` (a degree list) or ending at