@heroiclands/package-build 25.5.5 → 25.6.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 (66) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/assets/styles/content-base.css +1 -1
  3. package/docs/api.md +24 -16
  4. package/docs/authoring/frontmatter.md +1 -1
  5. package/docs/authoring/links-and-markup.md +10 -9
  6. package/docs/content-format.md +40 -1
  7. package/docs/diagnostics.md +2 -2
  8. package/docs/guides/reference-pages.md +4 -1
  9. package/docs/guides/site.md +1 -1
  10. package/docs/reference/format-details.md +174 -129
  11. package/docs/reference/note-types.md +134 -121
  12. package/docs/reference/theme-pages.md +1 -0
  13. package/engine/content-format.yaml +9 -7
  14. package/engine/content-html.mjs +3 -2
  15. package/engine/content-index.mjs +23 -0
  16. package/engine/content-links.mjs +8 -0
  17. package/engine/content-render.mjs +2 -2
  18. package/engine/content-tables.mjs +12 -5
  19. package/engine/culture-reach.mjs +3 -0
  20. package/engine/declared-sections.mjs +100 -0
  21. package/engine/derived-sections.mjs +36 -2
  22. package/engine/dictionary-rows.mjs +2 -1
  23. package/engine/generated-sections.mjs +2 -0
  24. package/engine/holdings.mjs +107 -3
  25. package/engine/index-records.mjs +3 -1
  26. package/engine/index.mjs +1 -1
  27. package/engine/language-data.mjs +0 -1
  28. package/engine/language-rows.mjs +1 -3
  29. package/engine/language-terms.mjs +7 -10
  30. package/engine/map-dot.mjs +22 -0
  31. package/engine/markdown-blocks.mjs +23 -8
  32. package/engine/metadata-index.mjs +1 -0
  33. package/engine/note-addresses.mjs +16 -0
  34. package/engine/note-vocabulary.mjs +19 -8
  35. package/engine/office-holders.mjs +76 -0
  36. package/engine/page-breaks.mjs +36 -8
  37. package/engine/pdf-render.mjs +4 -3
  38. package/engine/place-kinds.mjs +106 -0
  39. package/engine/settlement-tables.mjs +410 -675
  40. package/engine/settlement-terms.mjs +118 -0
  41. package/engine/site-build.mjs +2 -4
  42. package/engine/sql-tables.mjs +101 -7
  43. package/engine/standing-terms.mjs +9 -5
  44. package/engine/standings.mjs +83 -31
  45. package/engine/unique-items.mjs +85 -0
  46. package/package.json +9 -4
  47. package/site-theme/data/banner-kinds.yaml +2 -0
  48. package/sohl/actors.mjs +4 -3
  49. package/types/engine/content-html.d.mts +2 -1
  50. package/types/engine/culture-reach.d.mts +3 -0
  51. package/types/engine/declared-sections.d.mts +30 -0
  52. package/types/engine/derived-sections.d.mts +5 -1
  53. package/types/engine/holdings.d.mts +34 -2
  54. package/types/engine/index-records.d.mts +2 -1
  55. package/types/engine/language-terms.d.mts +5 -1
  56. package/types/engine/markdown-blocks.d.mts +5 -1
  57. package/types/engine/office-holders.d.mts +33 -0
  58. package/types/engine/page-breaks.d.mts +8 -0
  59. package/types/engine/place-kinds.d.mts +18 -0
  60. package/types/engine/settlement-tables.d.mts +25 -158
  61. package/types/engine/settlement-terms.d.mts +53 -0
  62. package/types/engine/sql-tables.d.mts +35 -3
  63. package/types/engine/standing-terms.d.mts +3 -2
  64. package/types/engine/standings.d.mts +14 -2
  65. package/types/engine/unique-items.d.mts +12 -0
  66. package/vendor/markdownlint.mjs +855 -569
@@ -1444,8 +1444,8 @@ complete address here; `folder-poisonsandtoxins` and the fully qualified
1444
1444
  `sohl-none-folder-poisonsandtoxins` name the same folder. An address no folder
1445
1445
  note answers to is a build error naming the folders the package does declare.
1446
1446
 
1447
- Note this is the _pack_ folder, not the note's directory. The directory is
1448
- `file.path` / `file.folder`, which a content table reads separately.
1447
+ Note this is the _pack_ folder, and a note's directory has no bearing on it
1448
+ or on any query.
1449
1449
 
1450
1450
  **Where a folder materialises is derived from what references it.** Every pack
1451
1451
  holding a document that names a folder gets that folder, and its ancestors with
@@ -3404,7 +3404,7 @@ ORDER BY sohl.kbcat, name.full
3404
3404
  **`FROM notes`** is the content index: one row per note, plus one per
3405
3405
  documentation entry, so `type = 'miscgear'` selects the items and never their
3406
3406
  journals. A nested field is addressed exactly as a note authors it —
3407
- `sohl.weight`, `name.full`, `file.path` — because the index is read as JSON and
3407
+ `sohl.weight`, `name.full`, `data.culture` — because the index is read as JSON and
3408
3408
  every nested object is inferred as a struct. A field a note type does not carry
3409
3409
  reads `NULL` rather than failing. `data.culture` is the one field that reads as
3410
3410
  a derived value: the note's own culture, or the one it inherits. See
@@ -3534,7 +3534,7 @@ for the keys.
3534
3534
  | `register` | `data.register` | `name`, `tongue`, `kind`, `built` (a list), `gloss`, `why`, `built_text` (the pieces joined with `+`), and `notes`: every note of the package carrying the name, derived from the index, `NULL` where none does |
3535
3535
  | `tongues` | `data.tongues` | `tag`, `meaning`, and `tongue_language`, the tongue's `language` |
3536
3536
  | `retired` | `data.retired` | `form`, `instead` (always a list, `NULL` where nothing is written instead), `why` |
3537
- | `scope` | `data.scope` | No `position`: `kind`, the key the value is under (`folders`, `cultures`, `lore`, `parents`, `addresses`, `paths`, `types` or `skip`), and `value`, an Address in its canonical form or a path or a type |
3537
+ | `scope` | `data.scope` | No `position`: `kind`, the key the value is under (`folders`, `cultures`, `lore`, `parents`, `addresses`, `types` or `skip`), and `value`, an Address in its canonical form or a type |
3538
3538
  | `letters` | `data.sounds.letters` | `letters` (a list), `kind`, `like`, `place`, `ipa`, `tongue` (a list, `NULL` for every tongue); a [spelling guide's](#a-spelling-guides-sounds) rows too, keyed by the guide |
3539
3539
  | `edges` | `data.sounds.edges` | `edge`, `sounds` (a list: `[any]` for every sound, empty for none) |
3540
3540
  | `clusters` | `data.sounds.clusters` | No `position`: `max`, one row per language stating it |
@@ -3683,8 +3683,9 @@ relational operations:
3683
3683
  `_section` is why one query replaces the forty near-identical blocks a grouped
3684
3684
  table would otherwise need: the authored `ORDER BY` decides the section order too.
3685
3685
 
3686
- **Beware `packFolder`.** It is a note's _pack_ folder, not its directory — the
3687
- directory is `file.folder`.
3686
+ **Beware `packFolder`.** It is a note's _pack_ folder. A query never selects by
3687
+ the directory a note sits in: `file.path`, `file.folder` and `file.name` are
3688
+ refused wherever a query names them.
3688
3689
 
3689
3690
  ##### Culture parents and inherited fields
3690
3691
 
@@ -4038,8 +4039,8 @@ as literal text.
4038
4039
  Only `---` accepts `.page-break`; `*** {.page-break}` and `___ {.page-break}`
4039
4040
  are findings. Other classes, ids and attributes on an attributed `---` line
4040
4041
  are findings too. A page break stands between blocks at the top level of a
4041
- note: putting one inside a list, block quote, alert, secret block or fenced
4042
- div is a finding at the marker's line and column.
4042
+ note: putting one inside a list, block quote, alert, footnote definition,
4043
+ secret block or fenced div is a finding at the marker's line and column.
4043
4044
 
4044
4045
  ##### Captions and numbered references
4045
4046
 
@@ -4305,6 +4306,7 @@ the authored text as H1 sections with fixed anchors, in this order:
4305
4306
  | Within | `within` | a `place` | the places whose `data.parents` names it, one paragraph to each kind of place, in the order the place vocabulary lists its `subType`s |
4306
4307
  | Governed by | `governedby` | a `place` | the affiliation its `data.government` names, as a list entry with the affiliation's kind |
4307
4308
  | Governed places | `governedplaces` | an `affiliation` | the places whose `data.government` names it, grouped as **Within** groups them |
4309
+ | Office holders | `officeholders` | an `affiliation` | each office of its `governance.offices` held by a living being, one line each, with the beings whose `office` names it |
4308
4310
  | Chronology | `chronology` | a `place` | an [event view](#event-views) |
4309
4311
  | Events | `events` | a `being`, an `affiliation`, a `lore` note of `culture` | an event view |
4310
4312
  | Accounts | `accounts` | an `affiliation`, a `place`, a `lore` note of `culture` | an event view |
@@ -5066,8 +5068,8 @@ names, an empty name, a missing `pattern` or `given`, a missing `female` or
5066
5068
 
5067
5069
  #### The bodies a being belongs to, and its standing in each
5068
5070
 
5069
- **Membership**: `{rank: number, office: string, societies: string[]}` — `rank`
5070
- required, `office` and `societies` not.
5071
+ **Membership**: `{rank: number, office: string | string[], societies: string[]}` —
5072
+ `rank` required, `office` and `societies` not.
5071
5073
 
5072
5074
  `data.affiliations` is a **map keyed by the body's Address**, and each entry is
5073
5075
  the standing the being holds in that body. The key is what makes the pair
@@ -5081,7 +5083,8 @@ An entry carries three keys and nothing else:
5081
5083
  every rung, so a number indexes into it and the body stays the single source
5082
5084
  for what its level 5 is called.
5083
5085
  - **`office`** — optional, a **string**, matched against the keys of that body's
5084
- `governance.offices` map.
5086
+ `governance.offices` map, or a **list** of them where one being holds several
5087
+ posts in the same body, such as treasurer and chancellor of one council.
5085
5088
  - **`societies`** — optional, a **list of names**, the societies the membership
5086
5089
  is held in. It is always a list, even with one name; a bare string is an
5087
5090
  error at the key.
@@ -5110,13 +5113,15 @@ data:
5110
5113
  Both values are checked against the body the entry is keyed by. A `rank` that
5111
5114
  is not a level the body confers and an `office` that is not one of its keys are
5112
5115
  each an error naming the file, line and column — so a rung or a post that does
5113
- not exist fails on every being pointing at it. Naming one body twice, by its short
5116
+ not exist fails on every being pointing at it. Each post of a list is checked
5117
+ and located on its own. Naming one body twice, by its short
5114
5118
  and full Address, still names one body and is an error.
5115
5119
 
5116
5120
  The infobox prints one line per membership, in the order the note writes them,
5117
5121
  in a section of its own titled **Affiliations**: the office where one is held, then the rung's
5118
5122
  title with its level in parentheses, then the body — `War Chief, Hárár (5), of
5119
- Vrystwald Tribes`, or `Hárár (5), of Vrystwald Tribes` with no office. The title
5123
+ Vrystwald Tribes`, or `Hárár (5), of Vrystwald Tribes` with no office. Several
5124
+ posts lead in the order written: `Treasurer, Chancellor, Hárár (5), of …`. The title
5120
5125
  is read from the body's own `governance.ranks`; where the body declares no rung
5121
5126
  at that level, or its ladder is not available, the rung reads as `Rank 5` —
5122
5127
  `Rank 5, of Vrystwald Tribes`. The body's name links to its page, and is plain
@@ -5124,10 +5129,11 @@ text where it publishes none. A membership stating no
5124
5129
  standing is the body's name alone. The lines are the same on the website, in
5125
5130
  a Foundry journal and in the book. On the body's own page each office is a row
5126
5131
  anchored by its name, so the post a being holds is described where the body
5127
- declares it.
5132
+ declares it, and its **Office holders** section lists who holds each post.
5128
5133
 
5129
5134
  For SoHL, a being embeds one affiliation item per entry: `rank` **is** that
5130
- item's `system.level`, `office` its `system.office`, and the first name in
5135
+ item's `system.level`, `office` its `system.office` (several posts joined by
5136
+ commas), and the first name in
5131
5137
  `societies` its `system.society`. The SoHL item holds one society, so only the
5132
5138
  first name reaches it. A standing is a fact
5133
5139
  about the setting rather than about one system, so the `sohl:` block states
@@ -5690,7 +5696,33 @@ data:
5690
5696
  - { being: being-mara, start: "datefrom vrcal 2 Lusenar 725 VR" }
5691
5697
  ```
5692
5698
 
5693
- **Governed places are derived from place notes.** Each place names one governing affiliation in `data.government`. The affiliation's **Governed places** [generated section](#generated-sections) lists the places whose explicit references resolve to it, across local notes and fetched indexes, grouped by kind; with none, it has no such section. No affiliation-side list supplies government, and neither geographic containment nor organizational parents expands it.
5699
+ **Office holders are derived from beings.** Nothing names a holder on the
5700
+ office itself: a being states the post it holds in its own
5701
+ `data.affiliations.<body>.office`. For each office in `governance.offices`, the
5702
+ body's **Office holders** [generated section](#generated-sections) lists every
5703
+ being naming it whose `data.died` is unset, so several holders at once, a post
5704
+ held jointly, a vacant post that lists no one and a former holder who has died
5705
+ all need no further key. Offices follow the body's order, and holders are
5706
+ ordered by name; an office nobody living holds has no line, and a body with no
5707
+ held office has no section. A public body lists only public beings. The
5708
+ content index carries the same list on the body's record as `officeHolders`, one
5709
+ `{ office, holders }` entry per held office, each holder a being Address:
5710
+
5711
+ ```json
5712
+ {
5713
+ "officeHolders": [
5714
+ {
5715
+ "office": "War Chief",
5716
+ "holders": [{ "address": "thalorna-note-being-hrothgar", "anchor": null, "anchorKind": null }]
5717
+ }
5718
+ ]
5719
+ }
5720
+ ```
5721
+
5722
+ `holders`, the dated roster above, is a separate record of terms a body may
5723
+ keep; the derived list reads only the beings.
5724
+
5725
+ **Governed places are derived from place notes.** Each place names one governing affiliation in `data.government`. The affiliation's **Governed places** [generated section](#generated-sections) lists the places whose explicit references resolve to it, across local notes and fetched indexes, grouped by kind, and every district that states no government and falls under one of those places; with none, it has no such section. No affiliation-side list supplies government, organizational parents never expand it, and geographic containment expands it only to a district stating none.
5694
5726
 
5695
5727
  If `sohl` is present, this becomes an `affiliation` item.
5696
5728
 
@@ -5838,6 +5870,7 @@ Note: `data.quantity` may not be specified. Quantity is always 1.
5838
5870
  | `durability` | `number` | Gear durability |
5839
5871
  | `made` | an event Address | The event in which it was made — `lore-forging`, or `place-ironfells#raising` |
5840
5872
  | `lost` | an event Address | The event in which it was lost — `lore-flood`, or `place-ironfells#sack` |
5873
+ | `unique` | `boolean` | `true` for a one-of-a-kind item; its note links a place or a being |
5841
5874
 
5842
5875
  If a `sohl` property is present, a SoHL item of type "armorgear" is created.
5843
5876
 
@@ -5860,6 +5893,15 @@ without an `id`, a note holding none, an anchor the note does not declare, or
5860
5893
  a `prose` anchor is an error at that key. Neither reaches a system field, an
5861
5894
  infobox row or a page: each is a fact a content table can read.
5862
5895
 
5896
+ `unique: true` marks a one-of-a-kind item — a named hammer, a crown, a sword
5897
+ in a tomb — and an item without it is common, like ten feet of rope. No key says
5898
+ who holds or owns a unique item: possession changes in play, where Foundry
5899
+ tracks it in an actor's inventory, and ownership is a claim that can be
5900
+ disputed. Where the item lies and who claims it are prose in its note, linked
5901
+ to the places and beings involved, so a unique item whose note links no `place`
5902
+ and no `being` — in a body wikilink, or in a `made` or `lost` event Address — is
5903
+ an error at `unique`. Neither system reads the key; the infobox shows it as a **Unique** row.
5904
+
5863
5905
  ### type: armorlocation
5864
5906
 
5865
5907
  | `data` property | Values | Description |
@@ -5900,6 +5942,7 @@ if a `sohl` property is present, a SoHL item of type "attribute" is created.
5900
5942
  | `durability` | `number` | Gear durability |
5901
5943
  | `made` | an event Address | The event in which it was made — `lore-forging`, or `place-ironfells#raising` |
5902
5944
  | `lost` | an event Address | The event in which it was lost — `lore-flood`, or `place-ironfells#sack` |
5945
+ | `unique` | `boolean` | `true` for a one-of-a-kind item; its note links a place or a being |
5903
5946
  | `quantity` | `number` | Gear quantity (default: 1) |
5904
5947
  | `potency` | `na \| mild \| strong \| great` | Concoction Potency (mundane/exotic concoctions) |
5905
5948
  | `strength` | `number` | Strength: higher the number, greater the strength |
@@ -5930,6 +5973,7 @@ Note: `data.quantity` may not be specified; quantity is always set to 1.
5930
5973
  | `durability` | `number` | Gear durability |
5931
5974
  | `made` | an event Address | The event in which it was made — `lore-forging`, or `place-ironfells#raising` |
5932
5975
  | `lost` | an event Address | The event in which it was lost — `lore-flood`, or `place-ironfells#sack` |
5976
+ | `unique` | `boolean` | `true` for a one-of-a-kind item; its note links a place or a being |
5933
5977
  | `capacity` | `number` | HM3 container capacity (in lbs) |
5934
5978
 
5935
5979
  if a `sohl` property is present, a SoHL item of type "containergear" is created.
@@ -5957,6 +6001,7 @@ SoHL reads a container’s capacity in pounds from `sohl.maxCapacity`.
5957
6001
  | `durability` | `number` | Gear durability |
5958
6002
  | `made` | an event Address | The event in which it was made — `lore-forging`, or `place-ironfells#raising` |
5959
6003
  | `lost` | an event Address | The event in which it was lost — `lore-flood`, or `place-ironfells#sack` |
6004
+ | `unique` | `boolean` | `true` for a one-of-a-kind item; its note links a place or a being |
5960
6005
  | `quantity` | `number` | HM3 gear quantity (default: 1) |
5961
6006
 
5962
6007
  if a `sohl` property is present, a SoHL item of type "miscgear" is created.
@@ -6076,6 +6121,7 @@ the item’s base mastery and level from `sohl.masteryLevelBase` and
6076
6121
  | `durability` | `number` | Gear durability |
6077
6122
  | `made` | an event Address | The event in which it was made — `lore-forging`, or `place-ironfells#raising` |
6078
6123
  | `lost` | an event Address | The event in which it was lost — `lore-flood`, or `place-ironfells#sack` |
6124
+ | `unique` | `boolean` | `true` for a one-of-a-kind item; its note links a place or a being |
6079
6125
  | `quantity` | `number` | HM3 gear quantity (default: 1) |
6080
6126
 
6081
6127
  if a `sohl` property is present, a SoHL item of type "projectilegear" is created.
@@ -6205,13 +6251,12 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6205
6251
  | `retired[].form` | `string`, required | The form the setting does not write |
6206
6252
  | `retired[].instead` | `string` or `string[]` | What is written in its place; absent where nothing is |
6207
6253
  | `retired[].why` | `string`, required | Why the setting does not write it |
6208
- | `scope` | `{ folders?, cultures?, lore?, parents?, addresses?, paths?, types?, skip? }` | `language` only: the notes the register covers |
6254
+ | `scope` | `{ folders?, cultures?, lore?, parents?, addresses?, types?, skip? }` | `language` only: the notes the register covers |
6209
6255
  | `scope.folders` | `Address[]` | Every note whose `packFolder` is one of these folders |
6210
6256
  | `scope.cultures` | `Address[]` | Every note whose `culture` is one of these cultures |
6211
6257
  | `scope.lore` | `Address[]` | Every note whose `lore` names one of these notes |
6212
6258
  | `scope.parents` | `Address[]` | Every note whose `parents` names one of these notes |
6213
6259
  | `scope.addresses` | `Address[]` | These notes themselves |
6214
- | `scope.paths` | `string[]` | Every note whose file sits below one of these content paths, as `Regions/Aelwyth/` |
6215
6260
  | `scope.types` | `string[]` | The note types covered; a note of another type is out of scope whatever else catches it |
6216
6261
  | `scope.skip` | `Address[]` | Notes out of scope whatever else catches them |
6217
6262
  | `sounds` | `{ letters?, edges?, clusters?, stress?, finals?, syllables?, joining?, joins?, marks?, skeleton?, frames?, readPast? }` | `language` only: the letters it writes and the rules its words keep |
@@ -6269,7 +6314,7 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6269
6314
  | `sounds.marks` | `{ mark, letters, kind, limit }[]` | The marks the language writes on its letters |
6270
6315
  | `sounds.marks[].mark` | `string`, required | The mark's name, as `acute` |
6271
6316
  | `sounds.marks[].letters` | `string[]`, required | The letters that carry it |
6272
- | `sounds.marks[].kind` | `stress`, `length` or `apart`, required | What it says of its letter |
6317
+ | `sounds.marks[].kind` | `stress`, `length`, `apart` or `quality`, required | What it says of its letter |
6273
6318
  | `sounds.marks[].limit` | `one` or `any`, required | How many a word may carry |
6274
6319
  | `sounds.skeleton` | `{ second, third?, between? }[]` | The vowel set between a root's second and third consonants |
6275
6320
  | `sounds.skeleton[].second` | `string[]` or `any`, required | The second consonants the row holds for; `any` for every other |
@@ -6925,7 +6970,7 @@ closed list where its values are words:
6925
6970
  - `joins`: what happens where a consonant or a vowel meets another (`C+C`,
6926
6971
  `V+C`, `V+V`): `merge`, `insert` the `letters` between, `drop` the first, or
6927
6972
  `double` the consonant, everywhere or `where` stated.
6928
- - `marks`: the marks written on letters, each saying `stress`, `length`, or `apart` (the mark sets a vowel to be said apart from its neighbour, as a diaeresis does), and
6973
+ - `marks`: the marks written on letters, each saying `stress`, `length`, `apart` (the mark sets a vowel to be said apart from its neighbour, as a diaeresis does) or `quality` (the mark changes which vowel is said, as a grave writing the short vowel of _cat_ does), and
6929
6974
  whether a word carries `one` at most or `any`.
6930
6975
  - `skeleton`: the vowel set between a root's second and third consonants, by
6931
6976
  the second consonant and, where a row states `third`, the third; nothing
@@ -7213,6 +7258,7 @@ Note: `data.quantity` may not be specified. Quantity is always 1.
7213
7258
  | `durability` | `number` | Gear durability |
7214
7259
  | `made` | an event Address | The event in which it was made — `lore-forging`, or `place-ironfells#raising` |
7215
7260
  | `lost` | an event Address | The event in which it was lost — `lore-flood`, or `place-ironfells#sack` |
7261
+ | `unique` | `boolean` | `true` for a one-of-a-kind item; its note links a place or a being |
7216
7262
 
7217
7263
  if a `sohl` property is present, a SoHL item of type "weapongear" is created,
7218
7264
  carrying every strike mode the weapon has — melee and missile alike — on
@@ -7807,12 +7853,34 @@ level with no background prints nothing.
7807
7853
 
7808
7854
  - world: A self-contained whole in which places exist — a planet, plane, or realm.
7809
7855
  - region: A bounded division of a world or larger region — continents, marches, uplands, provinces.
7856
+ - district: A recognised division of a settlement or a region, usually administrative — a city's quarter, a town's ward, a kingdom's shire.
7810
7857
  - settlement: A place where folk dwell together — cities, towns, villages, holdings, camps.
7811
7858
  - site: A place significant by what was made or done there — ruins, monoliths, henges, works, battlefields.
7812
7859
  - structure: A single building or habitation — halls, keeps, temples, inns, towers.
7813
- - feature: A place significant by its terrain — forests, rivers, falls, passes, fords.
7860
+ - feature: A place significant by its terrain — forests, rivers, falls, fords.
7861
+ - route: A way travelled between places — a road, a pass, a sea lane.
7814
7862
  - celestial: A body observed from a world rather than located on one — a sun, a planet, a comet.
7815
7863
 
7864
+ A region is land defined by its geography or its people; a district is a
7865
+ division drawn by those who govern it. A valley crossing two shires is a
7866
+ region, and each shire is a district. **A district always stands within a
7867
+ `settlement` or a `region`**, named in `data.parents`: a district naming no
7868
+ parent is an error at its `subType`, and one whose parents all resolve and none
7869
+ is a settlement or a region is an error at `parents`. A district may name its
7870
+ own governing affiliation in `data.government`, a ward's governor or council;
7871
+ without the key it falls under its parent's government. The content index gives
7872
+ such a district the `data.government` of its nearest parent that states the key,
7873
+ reading `data.parents` in order and upward through parents that state none, so
7874
+ the district's **Governed by** section, the governing body's **Governed
7875
+ places** and every reader of the index name that body, and the "missing
7876
+ government" advisory does not fire. An explicit value on the district,
7877
+ `null` (complete anarchy) included, is its own and stands; a parent's explicit
7878
+ `null` is inherited as anarchy. **A route carries its
7879
+ journeys in `data.routes`**, at least one, and one stating none is an error at
7880
+ its `subType`; rivers stay features. Both compile as every place does, to a
7881
+ journal entry filed by `data.pack` and `data.packFolder`, and both are listed
7882
+ in their parent's **Within** section under **Districts** and **Routes**.
7883
+
7816
7884
  | `data` property | Values | Description |
7817
7885
  | --------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
7818
7886
  | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
@@ -7823,6 +7891,15 @@ level with no background prints nothing.
7823
7891
  | `culture` | `Address` | The people this place belongs to, naming a lore note with `subType: culture` |
7824
7892
  | `population` | `number` | Approximate population (precision 2 significant digits) |
7825
7893
  | `government` | `Address` or `null` (default type: `affiliation`) | Governing affiliation; explicit null means complete anarchy. Omission warns only for positive population. |
7894
+ | `settlements` | `{ rows, tail }` | Declared settlement rows and the sentence saying what the table leaves out |
7895
+ | `settlements.rows` | list of row maps | One row per settlement |
7896
+ | `settlements.rows[].place` | `Address` | Existing settlement note; exclusive with name |
7897
+ | `settlements.rows[].name` | `string` | A settlement without a note; exclusive with place |
7898
+ | `settlements.rows[].market` | integer, required | Market class, 1 to 6 |
7899
+ | `settlements.rows[].population` | positive integer, required | People living here |
7900
+ | `settlements.rows[].heldBy` | `Address` | What holds the land, where it is held; an affiliation or a being |
7901
+ | `settlements.rows[].for` | `string` | Optional reason the settlement exists |
7902
+ | `settlements.tail` | `string`, required | Sentence stating what the table leaves out |
7826
7903
  | `market` | `number` | Market class, 1 to 6 — what trade a settlement supports |
7827
7904
  | `borders` | `{ to, bearing }[]` | Places sharing a frontier with this one, and where each lies from here |
7828
7905
  | `borders[].to` | `Address`, required | The other place; the type defaults to `place` |
@@ -8069,7 +8146,7 @@ Write `government: null` for **complete anarchy**. Omitting the key leaves gover
8069
8146
  | Any | Explicit `null` | None; complete anarchy |
8070
8147
  | Any | Affiliation Address | None; the reference must resolve |
8071
8148
 
8072
- These rules apply to every place subtype, including settlements, sites, structures, features, regions and worlds. Empty strings, numbers, lists, maps, references to another note type and unresolved references are errors. They do not declare anarchy. `parents`, containment and capitals do not supply a government.
8149
+ These rules apply to every place subtype, including settlements, sites, structures, features, regions and worlds. Empty strings, numbers, lists, maps, references to another note type and unresolved references are errors. They do not declare anarchy. `parents`, containment and capitals do not supply a government, except to a `district` that omits the key, which falls under its nearest parent's government as [type: place](#type-place) describes.
8073
8150
 
8074
8151
  #### Population figures
8075
8152
 
@@ -8126,120 +8203,81 @@ languages belong to a polity's `sohl.system.commonSkills`, while the note's
8126
8203
 
8127
8204
  #### A region's settlements table
8128
8205
 
8129
- A region lists its settlements in a table on its own note: one row for each
8130
- settlement, stating what its market is, how many people it holds and who holds
8131
- it. **A row is the whole record of a settlement that has no note.** Where a
8132
- note exists the row links it, and the two must agree. A package that writes no
8133
- such table is not affected by anything in this section.
8134
-
8135
- The table is a markdown table under a level-two `## Settlements` heading on the
8136
- region's `place` note. Its header is exactly these columns, in this order. `For`
8137
- may be left out; the header carries no other column:
8206
+ A region declares its table in `data.settlements: { rows, tail }`. The rows
8207
+ carry the settlement's `population` and numeric `market`, and either `place`
8208
+ (the existing note's Address, default type `place`) or `name` (plain text where
8209
+ no note exists), never both. Where the region's culture holds land, a row's
8210
+ optional `heldBy` names what holds it by Address (an affiliation or a being,
8211
+ default type `affiliation`), and what that note is says how the land is held: a
8212
+ temple, a lineage, the city, a kin group. A people who hold no land state no
8213
+ `heldBy`, and their table has no Held by column. Optional `for` states why the
8214
+ settlement exists. `tail` is the sentence stating what the table leaves out.
8215
+
8216
+ The guard reads indexed rows. A linked row's population and market agree with
8217
+ its note, and every settlement note covered through `parents` has one row in
8218
+ the nearest declaring region. `government` states who governs; it never supplies
8219
+ who holds the land. HTML, Foundry and Typst render the declared table once.
8220
+ A body table under `## Settlements` is an error at its header, naming
8221
+ `data.settlements`; no facts are read from it.
8222
+
8223
+ The rendered columns are fixed, in this order; `Held by` appears when any row
8224
+ states `heldBy`, and `For` when any row states `for`:
8138
8225
 
8139
8226
  | Name | Market | People | Held by | For |
8140
8227
  | ---- | ------ | ------ | ------- | --- |
8141
8228
 
8142
- - **Name** — the settlement. Where it has a note, a link to it with the pipe
8143
- escaped for the table, `[[place-ashford\|Ashford]]`; otherwise plain text.
8144
- - **Market** — the market class as its number and its name, lower case:
8145
- `1 hamlet`, `2 village`, `3 town`, `4 market town`, `5 city`, `6 great city`.
8146
- The scale is the one [`data.market`](#what-a-settlements-market-supports)
8147
- states.
8148
- - **People** — the population, a whole number, grouped with commas or not:
8149
- `60`, `420`, `3,400`.
8150
- - **Held by** — who holds it, written in the region's holding model. Link the
8151
- holder where it has a note.
8152
- - **For** — a short phrase saying why the place is there: the ford, the mine,
8153
- the shrine.
8154
-
8155
- At least one sentence of prose follows the table, before the next heading,
8156
- saying what the table leaves out. A section filled by an `sql` fence is
8157
- generated from the notes and is not read as a settlements table.
8158
-
8159
- **The region names its holding model as a tag.** A note carrying a settlements
8160
- table carries at least one of these tags; a region whose countryside is held
8161
- two ways carries both. Tags stay open, so a misspelled model is not reported as
8162
- such: the table's note is reported for naming no model.
8163
-
8164
- | `holdingModel` value | the Held by cell names |
8165
- | -------------------- | ---------------------- |
8166
- | `held` | a person or a house |
8167
- | `endowed` | an institution |
8168
- | `registered` | an office |
8169
- | `kin` | a kindred |
8170
- | `polis` | the city |
8171
-
8172
- A linked holder is an `affiliation` or a `being`, and a `being` is a person: it
8173
- may hold land only in a region tagged `held`.
8174
-
8175
- **A settlement belongs to one table** — that of the nearest place above it, by
8176
- `parents`, that carries a settlements table. Every `settlement` note that table
8177
- covers has a row there and nowhere else.
8178
-
8179
- A worked example. `Vale.md`:
8229
+ `population` is a positive whole number. `market` is an integer from 1 to 6;
8230
+ the generated Market cell includes the class name (`4 market town`). A holder
8231
+ is an affiliation or a being, and its note says what kind of holding it is: a
8232
+ temple holds its endowment, a lineage its fiefs, the city its own fields. A row
8233
+ naming no holder leaves its Held by cell empty.
8180
8234
 
8181
- ```markdown
8182
- ---
8183
- shortcode: vale
8184
- name: { full: The Vale }
8185
- type: place
8186
- subType: region
8187
- tags: [region, held]
8188
- data:
8189
- population: 100000
8190
- government: crown
8191
- ---
8192
-
8193
- ## Settlements
8235
+ A worked example: the following `data` block belongs to `Vale.md`, whose
8236
+ `type` is `place`, `subType` is `region` and `shortcode` is `vale`:
8194
8237
 
8195
- | Name | Market | People | Held by | For |
8196
- | -------------------------- | ------------- | -----: | ---------------------------------------- | -------------------------- |
8197
- | [[place-ashford\|Ashford]] | 4 market town | 3,400 | [[affiliation-crown\|the Crown]] | the ford on the drove road |
8198
- | Weyshott | 2 village | 420 | [[being-aldwyn\|Sir Aldwyn of Brynwold]] | the manor's mill |
8199
- | Cold Hatch | 1 hamlet | 60 | Sir Aldwyn of Brynwold | the sheepwalk |
8200
-
8201
- Some four hundred further villages lie in the vale, none of them named here.
8238
+ ```yaml
8239
+ data:
8240
+ settlements:
8241
+ rows:
8242
+ - place: ashford
8243
+ market: 4
8244
+ population: 3400
8245
+ heldBy: affiliation-crown
8246
+ for: the ford on the drove road
8247
+ - name: Weyshott
8248
+ market: 2
8249
+ population: 420
8250
+ heldBy: being-aldwyn
8251
+ for: the manor's mill
8252
+ - name: Cold Hatch
8253
+ market: 1
8254
+ population: 60
8255
+ tail: Some four hundred further villages lie in the vale, none of them named here.
8202
8256
  ```
8203
8257
 
8204
8258
  `Ashford.md` states `parents: [vale]`, `population: 3400` and `market: 4`, so its
8205
- row agrees with it. Weyshott and Cold Hatch have no notes; their rows are their
8206
- whole record. Change Ashford's row to `3,000` and `package-build lint` reports:
8207
-
8208
- ```text
8209
- assets/content/Vale.md:16:49: error: figure disagrees: Ashford is carried at 3,000 here and 3,400 on its own note
8210
- ```
8211
-
8212
- `package-build lint` reports each of these as an error, each named by the
8213
- phrase its message begins with:
8214
-
8215
- - **`settlements columns`** — the header is not the declared columns in
8216
- order. The table's rows are not read until it is.
8217
- - **`no holding model`** — the note carries a settlements table and none of
8218
- the holding-model tags. Reported at its `tags`.
8219
- - **`market class`** — a Market cell is not a class written as its number and
8220
- its name.
8221
- - **`people figure`** — a People cell is not a positive whole number.
8222
- - **`no holder`** — a Held by cell is empty.
8223
- - **`not a holder`** — a Held by link names a note that is neither an
8224
- affiliation nor a being.
8225
- - **`holder out of model`** — a Held by link names a being in a region not
8226
- tagged `held`.
8227
- - **`not a settlement`** — a Name link names a note that is not a place.
8228
- - **`figure disagrees`** — a linked row's People differs from its note's
8229
- `data.population`, or the note states none.
8230
- - **`market disagrees`** — a linked row's Market differs from its note's
8231
- `data.market`, or the note states none.
8232
- - **`unlinked settlement`** — an unlinked Name is the name or an alias of a
8233
- settlement note the table covers. The finding gives the link to write.
8234
- - **`missing row`** — a settlement note the table covers has no row. Reported
8235
- at the table's header.
8236
- - **`duplicate settlement`** — a settlement has a second row in one table, or
8237
- a row in a table other than the one covering it.
8238
- - **`no tail`** — no sentence follows the table before the next heading.
8239
- Reported at the table's last row.
8240
-
8241
- A cell's finding is located at the cell. A link in a cell that resolves to no
8242
- note is reported by the link check, once, and not again here.
8259
+ row agrees. `affiliation-crown` names the existing Crown note; `being-aldwyn`
8260
+ names Sir Aldwyn's existing note. Weyshott and Cold Hatch have no notes; these
8261
+ rows hold their complete settlement record, and Cold Hatch names no holder. The generated table uses the
8262
+ current note names for its links and prints `3,400` and `4 market town`.
8263
+ Changing Ashford's row population to `3000` produces `figure disagrees` at
8264
+ that YAML value; removing its row produces `missing row` at `rows`.
8265
+
8266
+ Every semantic finding is an error, located where its YAML changes:
8267
+
8268
+ - **`body settlements table`** — A body table under Settlements is refused; declare data.settlements.
8269
+ - **`settlements shape`** — A region declares rows and tail, with one name or place per row.
8270
+ - **`unresolved settlement reference`** — A place or heldBy Address does not resolve.
8271
+ - **`market class`** — A row market is not an integer from 1 to 6.
8272
+ - **`people figure`** — Population is not a positive whole number.
8273
+ - **`not a holder`** — A heldBy Address names neither an affiliation nor a being.
8274
+ - **`not a settlement`** — A place Address names a subject that is not a place.
8275
+ - **`figure disagrees`** — A linked population differs from its note, or the note states none.
8276
+ - **`market disagrees`** — A linked market differs from its note, or the note states none.
8277
+ - **`unlinked settlement`** — A plain name has an existing settlement note in this region.
8278
+ - **`missing row`** — A covered settlement note has no row.
8279
+ - **`duplicate settlement`** — A settlement has another row here or in another region.
8280
+ - **`no tail`** — The declaration has no sentence stating what it leaves out.
8243
8281
 
8244
8282
  ### type: scenario
8245
8283
 
@@ -8251,6 +8289,14 @@ Content prepared to be played — a situation with its cast, places, and possibl
8251
8289
  - adventure: A self-contained undertaking with a specific objective, playable in a few sessions.
8252
8290
  - encounter: A single scene or challenge, reusable within an adventure or on its own.
8253
8291
 
8292
+ A scenario's state is two independent signals. The `draft` tag is the author's
8293
+ judgement that it still needs work, lifted when it is finished whether or not
8294
+ it has been played. An optional `## Playtest {#playtest}` section, absent until
8295
+ the scenario is first run at a table, records each run: the date, the table's
8296
+ size, the party's makeup and the scenes covered, the difficulty as played scene
8297
+ by scene, adjustments a GM can make, and what the party did that the scenario
8298
+ did not foresee. Holes found in play are fixed in the scenario itself.
8299
+
8254
8300
  | `data` property | Values | Description |
8255
8301
  | ------------------ | -------------------------------------------- | ------------------------------------------------------------------------------ |
8256
8302
  | `parents` | `Address[]` | List of parent scenarios of this scenario (campaigns, etc.) |
@@ -8258,7 +8304,6 @@ Content prepared to be played — a situation with its cast, places, and possibl
8258
8304
  | `cast` | `Address[]` | individuals associated with this scenario |
8259
8305
  | `factions` | `Address[]` | Affiliations associated with this scenario |
8260
8306
  | `follows` | `Address[]` | Prerequisite scenarios that should be completed before beginning this scenario |
8261
- | `status` | `draft \| playtested \| published` | Playability status of this scenario |
8262
8307
  | `party.size` | `solo \| small \| standard \| large \| host` | Suggested party size (solo=1, small=2-3, standard=4-6, large=6-7, host=7+) |
8263
8308
  | `party.archetypes` | `Archetype[]` | Archetypes of characters suitable for completion |
8264
8309
 
@@ -8338,7 +8383,7 @@ Content prepared to be played — a situation with its cast, places, and possibl
8338
8383
  | `sounds.marks` | `{ mark, letters, kind, limit }[]` | The marks written on the letters, each row's keys as a language note's `sounds.marks` |
8339
8384
  | `sounds.marks[].mark` | `string`, required | The mark's name, as `acute` |
8340
8385
  | `sounds.marks[].letters` | `string[]`, required | The letters that carry it |
8341
- | `sounds.marks[].kind` | `stress`, `length` or `apart`, required | What it says of its letter |
8386
+ | `sounds.marks[].kind` | `stress`, `length`, `apart` or `quality`, required | What it says of its letter |
8342
8387
  | `sounds.marks[].limit` | `one` or `any`, required | How many a word may carry |
8343
8388
 
8344
8389
  A setting guide introduces one culture's view of the setting, so it names that