@heroiclands/package-build 23.6.0 → 23.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 23.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ **Citations**
8
+
9
+ - A dictionary cites a word from every note that marks it in italics, by its form or a variant, whatever the accents and capitals.
10
+ - A note whose name, alias, rank or office title is the word cites it too.
11
+ - A word's `attested` key is refused: delete it, and mark the word in italics where a note uses it.
12
+
13
+ **Checks**
14
+
15
+ - A word's `frame` must name one of its language's `sounds.frames`, or one of an ancestor's.
16
+ - An `sql` fence set as a table refuses a `key` attribute, which only a dictionary or a glossary uses.
17
+
18
+ ### Patch Changes
19
+
20
+ **Language notes**
21
+
22
+ - A language note can state its register of names, tongues, retired forms, scope, sound changes, name elements, roots, cognates and calling names in frontmatter, each checked where it is written.
23
+ - Its sound rules go there too: letters, word edges, clusters, stress, syllables and harmony, joins, written marks, root frames and the spellings read past.
24
+ - Each of these sets back as a table from an `sql` fence, and the register lists the notes in its scope that carry each name without anyone writing them.
25
+ - A word's gloss can list several senses, which a dictionary numbers, and a word can name its variant forms and carry a note.
26
+ - A dictionary or glossary fence takes `key=only` to print its key alone, so a page with one `key=false` fence per heading states the key once at its head.
27
+ - An affix can state what it replaces at the close of its stem, that it lengthens the stem's last vowel, and its front-vowel form.
28
+
29
+ ## 23.6.1
30
+
31
+ ### Patch Changes
32
+
33
+ **Dictionaries and glossaries**
34
+
35
+ - A language's Suffixes and Fields tables can be set from its frontmatter, by querying `affixes` and `fields` in an `sql` fence.
36
+ - An affix can state its own tone pattern in `tones`.
37
+ - `key=false` leaves the key off a dictionary or glossary fence, so a page with one fence per heading states the key once.
38
+ - A daughter language's words built from its parent's words and affixes no longer warn that a piece matches nothing, and their glosses print.
39
+ - Dictionaries abbreviate particle as _ptcl._, since _part._ reads as participle.
40
+
41
+ **Diagnostics**
42
+
43
+ - File paths stay relative to the working directory when `PACKAGE_BUILD_CONFIG` names the configuration through a symlinked path, as macOS temporary directories are.
44
+
3
45
  ## 23.6.0
4
46
 
5
47
  ### Minor Changes
@@ -309,18 +309,41 @@ A column's heading is its SQL alias, so `AS "Name"` is what a reader sees. A cel
309
309
 
310
310
  ### What a query can read
311
311
 
312
- | Relation | What it holds |
313
- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
314
- | `notes` | Every note except a stub. This is the one to use unless a stub is wanted. |
315
- | `entries` | Every note, stubs included. |
316
- | `events` | One row per event a `lore`, `place` or `affiliation` note writes in `data.events`, with `whenSort` to order them by date. |
317
- | `words` | One row per word a language note writes in `data.words`, with that note as its `language`, its `position` and what it is built from. |
318
- | `glossary` | One row per entry a culture or an affiliation writes in `data.glossary`, with the `note` it belongs to and the `language` it resolves to. |
319
- | `language_lineage` | One row per language note and each language it inherits words from through `data.family`, itself included at `depth` 0. Join it to `words` for a language's words with inherited ones. |
320
- | `dictionary` | One row per word per language it is listed under, inherited words included, with its `depth`. See [dictionaries and glossaries](#dictionaries-and-glossaries). |
321
- | `terms` | One row per glossary entry, rank or office per note that owns it, with the `cultures` it reaches. See [dictionaries and glossaries](#dictionaries-and-glossaries). |
322
- | `market` | The market scale as three columns — `value`, `name`, `trade` — so a table can print `village` beside the number a note wrote. |
323
- | `<package>.notes` | The same relations — `notes`, `entries`, `events`, `words`, `glossary`, `language_lineage`, `dictionary` and `terms` — for each package this one declares a dependency on, in a schema named for it. |
312
+ | Relation | What it holds |
313
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
314
+ | `notes` | Every note except a stub. This is the one to use unless a stub is wanted. |
315
+ | `entries` | Every note, stubs included. |
316
+ | `events` | One row per event a `lore`, `place` or `affiliation` note writes in `data.events`, with `whenSort` to order them by date. |
317
+ | `words` | One row per word a language note writes in `data.words`, with that note as its `language`, its `position` and what it is built from. |
318
+ | `affixes` | One row per affix a language note writes in `data.affixes`, with that note as its `language`, its `position`, and each key the affix states: `form`, `gloss`, `attaches`, `replaces`, `lengthens`, `front`, `examples` and `tones`. |
319
+ | `fields` | One row per field a language note writes in `data.fields`, with that note as its `language`, its `position`, and the field's `name` and what it `holds`. |
320
+ | `glossary` | One row per entry a culture or an affiliation writes in `data.glossary`, with the `note` it belongs to and the `language` it resolves to. |
321
+ | `language_lineage` | One row per language note and each language it inherits words from through `data.family`, itself included at `depth` 0. Join it to `words` for a language's words with inherited ones. |
322
+ | `dictionary` | One row per word per language it is listed under, inherited words included, with its `depth`. See [dictionaries and glossaries](#dictionaries-and-glossaries). |
323
+ | `terms` | One row per glossary entry, rank or office per note that owns it, with the `cultures` it reaches. See [dictionaries and glossaries](#dictionaries-and-glossaries). |
324
+ | `register` | One row per name a language note writes in `data.register`, with its `tongue`, `kind` and `why`, its pieces as `built_text`, and the `notes` carrying the name, derived from the index. |
325
+ | `tongues` | One row per tag a language note writes in `data.tongues`: `tag`, `meaning` and `tongue_language`. |
326
+ | `retired` | One row per form a language note writes in `data.retired`: `form`, `instead` as a list, and `why`. |
327
+ | `scope` | One row per value of a language note's `data.scope`: the `kind` it is under and the `value`. |
328
+ | `letters` | One row per entry of a language note's `data.sounds.letters`: `letters` as a list, `kind`, `like` and `tongue`. |
329
+ | `edges` | One row per entry of a language note's `data.sounds.edges`: the `edge` and its `sounds` as a list. |
330
+ | `clusters` | One row per language note stating `data.sounds.clusters`, with its `max`. |
331
+ | `stress` | One row per language note stating `data.sounds.stress`, with its `stress`. |
332
+ | `finals` | One row per entry of a language note's `data.sounds.finals`: `final` and `like`. |
333
+ | `syllables` | One row per language note stating `data.sounds.syllables`: each rule a column, the harmony as `harmony_back`, `harmony_front`, `harmony_neutral` and `harmony_neutralAs`. |
334
+ | `joining` | One row per entry of a language note's `data.sounds.joining`: `first`, `second`, `written` and `example`. |
335
+ | `joins` | One row per entry of a language note's `data.sounds.joins`: `meeting`, `rule`, `letters` and `"where"`. |
336
+ | `marks` | One row per entry of a language note's `data.sounds.marks`: `mark`, `letters`, `kind` and `"limit"`. |
337
+ | `skeleton` | One row per entry of a language note's `data.sounds.skeleton`: `second` and `between`. |
338
+ | `frames` | One row per frame of a language note's `data.sounds.frames`: `frame`, `shape`, `class`, `makes` and `examples` as `{ root, form }`. |
339
+ | `read_past` | One row per entry of a language note's `data.sounds.readPast`: `written`, `readAs` and `what`. |
340
+ | `sound_changes` | One row per step of each set in a language note's `data.soundChanges`: the set's `position`, `name`, `from_language` and `to_language`, then `step_position`, `step`, `change` and `"where"`. |
341
+ | `elements` | One row per element of a language note's `data.elements`: `form`, `variants`, `element_position`, `sense` and `examples`. |
342
+ | `roots` | One row per root of a language note's `data.roots`: `root`, `sense` and `field`. |
343
+ | `cognates` | One row per form of each set in a language note's `data.cognates`: the set's `position`, `gloss` and `proto`, the `form_language` and the `form`. |
344
+ | `calling_names` | One row per closing of a language note's `data.callingNames`: `form`, `doubled`, `usual` and the language's `min_syllables`. |
345
+ | `market` | The market scale as three columns — `value`, `name`, `trade` — so a table can print `village` beside the number a note wrote. |
346
+ | `<package>.notes` | The same relations — `notes`, `entries`, `events`, `words`, `affixes`, `fields`, `glossary`, `language_lineage`, `dictionary`, `terms`, `register`, `tongues`, `retired`, `scope`, `letters`, `edges`, `clusters`, `stress`, `finals`, `syllables`, `joining`, `joins`, `marks`, `skeleton`, `frames`, `read_past`, `sound_changes`, `elements`, `roots`, `cognates` and `calling_names` — for each package this one declares a dependency on, in a schema named for it. |
324
347
 
325
348
  Nested frontmatter is addressed exactly as a note writes it: `name.full`, `sohl.weight`, `data.born`. Every Address is a string — the full canonical Address, as in `list_contains(data.parents, 'thalorna-note-place-xerathia')` — whichever package published it. Every note carries a computed `state` column of `full`, `draft` or `stub`. Notes tagged `gm` are absent from the relations on public surfaces.
326
349
 
@@ -417,15 +440,219 @@ ORDER BY e.whenSort, e.address
417
440
 
418
441
  An event with no `where.locus` keeps its row with an empty **Where** cell.
419
442
 
443
+ ### A language's affixes and fields
444
+
445
+ `affixes` and `fields` hold the entries of a language note's `data.affixes` and `data.fields`, so a page can set them as tables. A Suffixes table is a `layout=table` fence over `affixes`; the `tones` key is the tone pattern an affix carries:
446
+
447
+ ````markdown
448
+ ```sql
449
+ SELECT form AS "Suffix", attaches AS "Stem", gloss AS "Makes", array_to_string(examples, ', ') AS "Example", tones AS "Tones"
450
+ FROM affixes
451
+ WHERE language = 'thalorna-note-skill-ekpelulng' AND form LIKE '-%'
452
+ ORDER BY position
453
+ ```
454
+ ````
455
+
456
+ | Suffix | Stem | Makes | Example | Tones |
457
+ | ------ | ----- | -------------------------------------------- | ------- | ----- |
458
+ | -ndo | added | the keeper of it: one who tends and holds it | luwando | L |
459
+ | -ma | added | the child of it: one who is fed by it | yesoma | H |
460
+
461
+ `ORDER BY position` keeps the order the note states them in. The same shape serves `fields`, with `name` and `holds`.
462
+
463
+ ### A language's register, sounds and history
464
+
465
+ A language note states its register, tongues, the forms it does not write, scope, letters, sound rules, sound changes, name elements, roots and cognates in frontmatter, and each is a relation, so the page sets every table back with a fence. Each row carries the note as its `language`, and a list's entries their `position`, so `WHERE language = '…' ORDER BY position` keeps the order the note states. A list column, such as `sounds` or `instead`, prints its entries joined by commas; `array_to_string` and `list_transform` set them otherwise.
466
+
467
+ The tags the register uses:
468
+
469
+ ````markdown
470
+ ```sql
471
+ SELECT '`' || tag || '`' AS "Tongue", meaning AS "Meaning"
472
+ FROM tongues WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
473
+ ```
474
+ ````
475
+
476
+ | Tongue | Meaning |
477
+ | --------- | --------------------------------------------------------------------- |
478
+ | `dunhari` | a Dunhari name |
479
+ | `sovidur` | a Sovidur name, carried by a Khazryn wanderer living among the tribes |
480
+
481
+ The register, each name with the notes in the language's scope that carry it, each linked under the name itself. `notes` is derived, so a name no note carries prints `—`:
482
+
483
+ ````markdown
484
+ ```sql
485
+ SELECT '**' || name || '**' AS "Name",
486
+ coalesce(array_to_string(list_transform(notes, n -> '[[' || n || '|' || name || ']]'), ', '), '—') AS "Notes",
487
+ '`' || tongue || '`' AS "Tongue", coalesce(built_text, '—') AS "Built from"
488
+ FROM register WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
489
+ ```
490
+ ````
491
+
492
+ The forms the setting does not write, a form with several replacements joined:
493
+
494
+ ````markdown
495
+ ```sql
496
+ SELECT '`' || form || '`' AS "Form",
497
+ coalesce(array_to_string(list_transform(instead, x -> '`' || x || '`'), ', '), '—') AS "Written instead",
498
+ why AS "Why"
499
+ FROM retired WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
500
+ ```
501
+ ````
502
+
503
+ The scope, one row per kind:
504
+
505
+ ````markdown
506
+ ```sql
507
+ SELECT kind AS "Kind", string_agg('`' || value || '`', ' ') AS "Value"
508
+ FROM scope WHERE language = 'thalorna-note-skill-dunharlng' GROUP BY kind ORDER BY kind
509
+ ```
510
+ ````
511
+
512
+ The letters, the word edges and the cluster limit. `like` is an SQL keyword, so it is named in double quotes:
513
+
514
+ ````markdown
515
+ ```sql
516
+ SELECT array_to_string(list_transform(letters, x -> '`' || x || '`'), ', ') AS "Letter",
517
+ kind AS "Kind", "like" AS "Sounds like"
518
+ FROM letters WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
519
+ ```
520
+
521
+ ```sql
522
+ SELECT edge AS "Edge", coalesce(nullif(array_to_string(list_transform(sounds, x -> '`' || x || '`'), ', '), ''), '—') AS "Sounds"
523
+ FROM edges WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
524
+ ```
525
+
526
+ ```sql
527
+ SELECT 'Most consonants together inside a word' AS "Rule", max AS "Count"
528
+ FROM clusters WHERE language = 'thalorna-note-skill-dunharlng'
529
+ ```
530
+ ````
531
+
532
+ The marks and spellings a word is read through:
533
+
534
+ ````markdown
535
+ ```sql
536
+ SELECT '`' || written || '`' AS "Written", coalesce('`' || readAs || '`', '—') AS "Read as", what AS "What it is"
537
+ FROM read_past WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
538
+ ```
539
+ ````
540
+
541
+ The sound changes, one table per set. `where` is an SQL keyword, so it is named in double quotes; `name` picks the set:
542
+
543
+ ````markdown
544
+ ```sql
545
+ SELECT step AS "Step", change AS "Change", coalesce("where", '—') AS "Where"
546
+ FROM sound_changes
547
+ WHERE language = 'thalorna-note-skill-helonclng' AND name = 'From Helonic to Byzarian'
548
+ ORDER BY step_position
549
+ ```
550
+ ````
551
+
552
+ The stress rule, the syllable rules and the finals. A rule of `syllables` is a column, so a `Rule | Forms` table is one `SELECT` per row joined with `UNION ALL`:
553
+
554
+ ````markdown
555
+ ```sql
556
+ SELECT 'Stress falls on' AS "Rule", stress AS "Value"
557
+ FROM stress WHERE language = 'thalorna-note-skill-vylarilng'
558
+ ```
559
+
560
+ ```sql
561
+ SELECT 'Codas' AS "Rule", array_to_string(codas, ' ') AS "Forms" FROM syllables WHERE language = 'thalorna-note-skill-sowideslng'
562
+ UNION ALL
563
+ SELECT 'Finals', array_to_string(finals, ' ') FROM syllables WHERE language = 'thalorna-note-skill-sowideslng'
564
+ UNION ALL
565
+ SELECT 'Initial vowel', CASE WHEN initialVowel THEN 'yes' ELSE 'no' END FROM syllables WHERE language = 'thalorna-note-skill-sowideslng'
566
+ ```
567
+
568
+ ```sql
569
+ SELECT '`' || final || '`' AS "Final", "like" AS "Sounds like"
570
+ FROM finals WHERE language = 'thalorna-note-skill-tantsoulng' ORDER BY position
571
+ ```
572
+ ````
573
+
574
+ The joining of vowels, the joins of parts and the marks. `limit` is an SQL keyword, so it is named in double quotes:
575
+
576
+ ````markdown
577
+ ```sql
578
+ SELECT array_to_string(first, ', ') AS "First closes on", array_to_string(second, ', ') AS "Second opens on",
579
+ '`' || written || '`' AS "Written", coalesce(example, '—') AS "Example"
580
+ FROM joining WHERE language = 'thalorna-note-skill-vedyarilng' ORDER BY position
581
+ ```
582
+
583
+ ```sql
584
+ SELECT '`' || meeting || '`' AS "Meeting", '`' || rule || '`' AS "Rule",
585
+ array_to_string(letters, ' ') AS "Letters", coalesce("where", 'anywhere') AS "Where"
586
+ FROM joins WHERE language = 'thalorna-note-skill-khazrilng' ORDER BY position
587
+ ```
588
+
589
+ ```sql
590
+ SELECT mark AS "Mark", array_to_string(letters, ' ') AS "Letters", kind AS "Kind", "limit" AS "Limit"
591
+ FROM marks WHERE language = 'thalorna-note-skill-atarzadilng' ORDER BY position
592
+ ```
593
+ ````
594
+
595
+ A root's vowel and its frames. A frame's form for one root is read out of `examples`:
596
+
597
+ ````markdown
598
+ ```sql
599
+ SELECT array_to_string(second, ' ') AS "Second consonant of the skeleton",
600
+ coalesce(nullif("between", ''), 'nothing') AS "Between the second and third"
601
+ FROM skeleton WHERE language = 'thalorna-note-skill-khazrilng' ORDER BY position
602
+ ```
603
+
604
+ ```sql
605
+ SELECT '**' || frame || '**' AS "Frame", '`' || shape || '`' AS "Shape", array_to_string(class, ', ') AS "Class",
606
+ makes AS "What it makes", list_filter(examples, e -> e.root = 'd-l-k')[1].form AS "On d-l-k"
607
+ FROM frames WHERE language = 'thalorna-note-skill-khazrilng' ORDER BY position
608
+ ```
609
+ ````
610
+
611
+ The calling names, a doubled first syllable named in words:
612
+
613
+ ````markdown
614
+ ```sql
615
+ SELECT coalesce('`' || form || '`', 'the first syllable doubled') AS "Closing", usual AS "Usual for"
616
+ FROM calling_names WHERE language = 'thalorna-note-skill-vedyarilng' ORDER BY position
617
+ ```
618
+ ````
619
+
620
+ The name elements, the roots and the cognates. A cognate set is one row per form, so a table of them pivots on `form_language`, and its reconstructed `proto` form takes its asterisk in the query:
621
+
622
+ ````markdown
623
+ ```sql
624
+ SELECT '`' || form || '`' AS "Element", element_position AS "Stands", sense AS "Sense",
625
+ array_to_string(examples, ', ') AS "Example"
626
+ FROM elements WHERE language = 'thalorna-note-skill-aelwthlng' ORDER BY position
627
+ ```
628
+
629
+ ```sql
630
+ SELECT '`' || root || '`' AS "Skeleton", sense AS "Sense", field AS "Field"
631
+ FROM roots WHERE language = 'thalorna-note-skill-khazrilng' ORDER BY position
632
+ ```
633
+
634
+ ```sql
635
+ SELECT '*' || max(proto) AS "Ancient",
636
+ max(form) FILTER (WHERE form_language = 'thalorna-note-skill-helonclng') AS "Helonic",
637
+ max(form) FILTER (WHERE form_language = 'thalorna-note-skill-byzarnlng') AS "Byzarian",
638
+ gloss AS "Gloss"
639
+ FROM cognates WHERE language = 'thalorna-note-skill-helonclng'
640
+ GROUP BY position, gloss ORDER BY position
641
+ ```
642
+ ````
643
+
644
+ See [a language's register, sounds and history](../reference/format-details.md#a-languages-register-sounds-and-history) for every key and what the lint holds it to.
645
+
420
646
  ### Fence attributes
421
647
 
422
- A braced list may follow the language on the opening fence. Three attributes are accepted, and anything else is reported by name.
648
+ A braced list may follow the language on the opening fence. Four attributes are accepted, and anything else is reported by name.
423
649
 
424
- | Attribute | Value | Default | What it does |
425
- | --------------- | ----------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
426
- | `allow-empty` | `true` or `false` | `false` | Whether a query selecting no rows is allowed. A zero-row result is a finding otherwise, because a table that silently prints nothing is the commonest way a query goes wrong unnoticed. |
427
- | `section-level` | an integer from 1 through 6 | `2` | The heading level given to each group a `_section` column creates, and to each letter of a dictionary or a glossary. |
428
- | `layout` | `table`, `dictionary` or `glossary` | `table` | How the result is set: as a table, as a dictionary of words, or as a glossary of terms — see [dictionaries and glossaries](#dictionaries-and-glossaries). |
650
+ | Attribute | Value | Default | What it does |
651
+ | --------------- | ----------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
652
+ | `allow-empty` | `true` or `false` | `false` | Whether a query selecting no rows is allowed. A zero-row result is a finding otherwise, because a table that silently prints nothing is the commonest way a query goes wrong unnoticed. |
653
+ | `section-level` | an integer from 1 through 6 | `2` | The heading level given to each group a `_section` column creates, and to each letter of a dictionary or a glossary. |
654
+ | `key` | `true`, `false` or `only` | `true` | Whether a dictionary or a glossary prints its key. `false` leaves it out; `only` prints the key for the fence's rows and no letter or entry, so a page with one `key=false` fence per heading states its key once. Refused on a table. |
655
+ | `layout` | `table`, `dictionary` or `glossary` | `table` | How the result is set: as a table, as a dictionary of words, or as a glossary of terms — see [dictionaries and glossaries](#dictionaries-and-glossaries). |
429
656
 
430
657
  ````markdown
431
658
  ```sql {allow-empty=true section-level=3}
@@ -453,7 +680,7 @@ See [SQL details](../reference/format-details.md#content-tables) for query optio
453
680
 
454
681
  ### Dictionaries and glossaries
455
682
 
456
- Two layouts set a result as paragraphs under letter headings rather than as a table. **`layout=dictionary` sets words**, read from the `dictionary` relation: each with its class, its etymology, its senses and the notes it is cited from. **`layout=glossary` sets terms**, read from the `terms` relation: a culture's or an affiliation's own glossary entries and an affiliation's ranks and offices, each sense tagged with the body or culture it comes from. Each needs the `name`, `senses` and `usage` columns, and a query that leaves one out is reported, naming the column.
683
+ Two layouts set a result as paragraphs under letter headings rather than as a table. **`layout=dictionary` sets words**, read from the `dictionary` relation: each with its class, its etymology, its senses, numbered where a word's `gloss` lists several, and the notes it is cited from — every note whose prose marks the word in italics. **`layout=glossary` sets terms**, read from the `terms` relation: a culture's or an affiliation's own glossary entries and an affiliation's ranks and offices, each sense tagged with the body or culture it comes from. Each needs the `name`, `senses` and `usage` columns, and a query that leaves one out is reported, naming the column.
457
684
 
458
685
  **The query decides what appears**, and any note may carry either fence. These are the common ones:
459
686
 
@@ -488,9 +715,28 @@ SELECT * FROM terms WHERE list_contains(cultures, 'thalorna-note-lore-provenzian
488
715
  ```
489
716
  ````
490
717
 
718
+ A page that sets several fences, one per heading, states its key once: a `key=only` fence at the head over all the rows the page sets, then `key=false` on each fence below it, so the abbreviations are not repeated under every heading. The `key=only` fence prints the key for its rows and no letter heading and no entry:
719
+
720
+ ````markdown
721
+ ## Words {#words}
722
+
723
+ ```sql {layout=dictionary key=only}
724
+ SELECT * FROM dictionary WHERE language = 'thalorna-note-skill-vylarilng' AND depth = 0
725
+ ```
726
+
727
+ ### Love and its conduct
728
+
729
+ ```sql {layout=dictionary key=false section-level=4}
730
+ SELECT d.* FROM dictionary d JOIN words w ON w.language = d.language AND w.form = d.name
731
+ WHERE d.language = 'thalorna-note-skill-vylarilng' AND w.field = 'Love and its conduct'
732
+ ```
733
+ ````
734
+
735
+ `key`, whatever its value, sets a dictionary's or a glossary's key, so it is refused on a fence set as a table.
736
+
491
737
  Either layout prints, in order:
492
738
 
493
- - **a key**, once, listing only the abbreviations the result uses — a language's tag, and for words a class or a modifier — each with what it stands for, and the common mark where an entry carries it;
739
+ - **a key**, once, unless the fence sets `key=false`, and nothing else where it sets `key=only`, listing only the abbreviations the result uses — a language's tag, and for words a class or a modifier — each with what it stands for, and the common mark where an entry carries it;
494
740
  - **a heading per letter**, at `section-level`, accents taken off and apostrophes skipped, so `'ostal` files under O and `Écuyer` under E;
495
741
  - **one paragraph per entry**: `•` for a common entry, the headword in bold, its pronunciation between slashes, its language tag in italics, the senses, and after a dash the notes it is cited from, as links.
496
742
 
@@ -143,6 +143,6 @@ Places, affiliations, lore and beings may name their culture in `data.culture`,
143
143
 
144
144
  An affiliation may name the trade, temple or order tongue it keeps in its own `data.language`. An affiliation that reaches no culture is a warning, in a tree holding a culture note. A language needs no culture and no affiliation. See [type: affiliation](reference/format-details.md#type-affiliation).
145
145
 
146
- A language note (`type: skill`, `subType: language`) states its words in frontmatter: `data.words` states each word as a map of its form, class, field and gloss, with its source, the pieces it is built from and the notes it is attested in, beside `data.classModifiers`, `data.fields` and `data.affixes`. The language of a word is the note it sits on, and none of it reaches the compiled Item. A daughter language names its parent in `data.family` and reads the parent's words as its own; the `language_lineage` relation joins a language to its ancestors. Classes come from the [master word classes](reference/format-details.md#word-classes). A culture or an affiliation states its own glossary terms in `data.glossary`, and every entry resolves to a language — its own `language`, else the affiliation's, its parents' or its culture's, else the culture's — or is an error at the entry. The `words`, `glossary` and `language_lineage` relations hold them for `sql` fences, the `dictionary` relation lists each word under every language that has it, inherited words included, and the `terms` relation each glossary entry, rank and office with the cultures it reaches; a fence sets them with `layout=dictionary` or `layout=glossary`. A language note names the tag a dictionary prints for it in `data.abbreviation`. See [a language's words](reference/format-details.md#a-languages-words), [glossary terms](reference/format-details.md#glossary-terms) and [dictionaries and glossaries](reference/format-details.md#dictionaries-and-glossaries).
146
+ A language note (`type: skill`, `subType: language`) states its words in frontmatter: `data.words` states each word as a map of its form, class, field and gloss — one sense or a list of them, which a dictionary numbers — with its source, its variant forms, the pieces it is built from and a note, beside `data.classModifiers`, `data.fields` and `data.affixes`. The notes a word is cited from are derived, not written: a note cites a word by marking it in italics in its prose, matched by form or a variant with diacritics and case set aside. The language of a word is the note it sits on, and none of it reaches the compiled Item. A daughter language names its parent in `data.family` and reads the parent's words as its own; the `language_lineage` relation joins a language to its ancestors. Classes come from the [master word classes](reference/format-details.md#word-classes). A culture or an affiliation states its own glossary terms in `data.glossary`, and every entry resolves to a language — its own `language`, else the affiliation's, its parents' or its culture's, else the culture's — or is an error at the entry. The `words`, `affixes`, `fields`, `glossary` and `language_lineage` relations hold them for `sql` fences, the `dictionary` relation lists each word under every language that has it, inherited words included, and the `terms` relation each glossary entry, rank and office with the cultures it reaches; a fence sets them with `layout=dictionary` or `layout=glossary`. A language note names the tag a dictionary prints for it in `data.abbreviation`. Every fact a check reads about a language's names, sounds and history is frontmatter too: `data.tongues` and `data.register` (every name the setting gives in the language's reach, the notes carrying each derived from the index), `data.retired`, `data.scope`, `data.sounds` (letters, word edges from a closed list, the cluster limit, the stress rule, syllable finals, syllable rules and vowel harmony, the joining of parts, written marks, a root's skeleton vowel and frames, and the spellings read past), `data.soundChanges` (named sets of ordered steps), `data.elements` (each standing `opening`, `closing`, `either` or `link`), `data.roots`, `data.cognates` (with a reconstructed `proto` form) and `data.callingNames`, each a relation — `sounds` as `letters`, `edges`, `clusters`, `stress`, `finals`, `syllables`, `joining`, `joins`, `marks`, `skeleton`, `frames` and `read_past`, `soundChanges` as `sound_changes`, `callingNames` as `calling_names` — that a fence sets back on the page. A word's citations also come from a note whose name, alias or rank or office title spells it whole. See [a language's words](reference/format-details.md#a-languages-words), [a language's register, sounds and history](reference/format-details.md#a-languages-register-sounds-and-history), [glossary terms](reference/format-details.md#glossary-terms) and [dictionaries and glossaries](reference/format-details.md#dictionaries-and-glossaries).
147
147
 
148
148
  Blockquote alerts use `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, or `[!]`, with optional attributes after the marker. They replace the former info and warn div fences; secret blocks remain supported. See [alerts](authoring/links-and-markup.md#alerts).