@heroiclands/package-build 23.1.0 → 23.2.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 (248) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/bin/content-commands.mjs +68 -149
  3. package/bin/package-build.mjs +56 -10
  4. package/ci/check-banners.mjs +42 -6
  5. package/ci/confirm-npm-version.mjs +135 -0
  6. package/ci/content-format-reference.mjs +14 -5
  7. package/config.mjs +1 -0
  8. package/container.mjs +185 -4
  9. package/content-config.mjs +333 -210
  10. package/deploy.mjs +116 -10
  11. package/docs/api.md +238 -246
  12. package/docs/authoring/assets.md +11 -5
  13. package/docs/authoring/frontmatter.md +1 -1
  14. package/docs/authoring/links-and-markup.md +87 -0
  15. package/docs/commands.md +138 -99
  16. package/docs/configuration.md +277 -125
  17. package/docs/content-format.md +2 -0
  18. package/docs/diagnostics.md +48 -13
  19. package/docs/guides/book.md +341 -21
  20. package/docs/guides/e2e.md +13 -2
  21. package/docs/guides/icons.md +14 -4
  22. package/docs/guides/module-project.md +18 -8
  23. package/docs/guides/release-and-deploy.md +54 -6
  24. package/docs/guides/site.md +5 -2
  25. package/docs/project-setup.md +30 -10
  26. package/docs/reference/format-details.md +373 -170
  27. package/docs/reference/note-types.md +128 -109
  28. package/docs/reference/theme-pages.md +31 -6
  29. package/e2e.mjs +140 -28
  30. package/engine/actor-compiler.mjs +22 -31
  31. package/engine/address-charset.mjs +4 -17
  32. package/engine/address-fields.mjs +1 -1
  33. package/engine/address-render.mjs +12 -1
  34. package/engine/address-values.mjs +6 -0
  35. package/engine/address.mjs +46 -33
  36. package/engine/art-fields.mjs +30 -5
  37. package/engine/art-slots.mjs +10 -1
  38. package/engine/asset-bindings.mjs +12 -1
  39. package/engine/base-compiler.mjs +34 -65
  40. package/engine/being-age.mjs +2 -7
  41. package/engine/being-measurements.mjs +12 -1
  42. package/engine/calendar-artifacts.mjs +6 -0
  43. package/engine/calendar-choice.mjs +12 -1
  44. package/engine/calendar-format.mjs +12 -1
  45. package/engine/calendar-human.mjs +12 -1
  46. package/engine/compile-corpus.mjs +3 -1
  47. package/engine/content-address.mjs +9 -12
  48. package/engine/content-alerts.mjs +13 -1
  49. package/engine/content-blocks.mjs +14 -3
  50. package/engine/content-embeds.mjs +11 -3
  51. package/engine/content-figures.mjs +9 -0
  52. package/engine/content-footnotes.mjs +12 -1
  53. package/engine/content-format-check.mjs +11 -262
  54. package/engine/content-format.yaml +5 -2
  55. package/engine/content-images.mjs +3 -0
  56. package/engine/content-index.mjs +443 -20
  57. package/engine/content-links.mjs +29 -35
  58. package/engine/content-lint.mjs +2 -3
  59. package/engine/content-poetry.mjs +12 -1
  60. package/engine/content-slug.mjs +2 -14
  61. package/engine/content-spans.mjs +13 -1
  62. package/engine/content-tables.mjs +12 -1
  63. package/engine/culture-choice.mjs +210 -13
  64. package/engine/data-keys.mjs +19 -16
  65. package/engine/date-conversion.mjs +12 -1
  66. package/engine/declaration-check.mjs +9 -0
  67. package/engine/derived-fields.mjs +6 -7
  68. package/engine/diagnostics.mjs +34 -4
  69. package/engine/docs-checks.mjs +9 -0
  70. package/engine/document-subtypes.mjs +47 -34
  71. package/engine/event-views.mjs +139 -14
  72. package/engine/exported-scene.mjs +92 -28
  73. package/engine/extension-attributes.mjs +12 -1
  74. package/engine/field-spec.mjs +11 -102
  75. package/engine/folder-notes.mjs +0 -64
  76. package/engine/foreign-catalog.mjs +2 -1
  77. package/engine/format-generated.mjs +9 -0
  78. package/engine/foundry-entries.mjs +10 -28
  79. package/engine/frontmatter-lint.mjs +83 -392
  80. package/engine/frontmatter.mjs +20 -70
  81. package/engine/generate.mjs +10 -15
  82. package/engine/heading-attributes.mjs +12 -1
  83. package/engine/helpers.mjs +41 -102
  84. package/engine/holdings.mjs +1 -1
  85. package/engine/homepage.mjs +3 -10
  86. package/engine/ids.mjs +1 -167
  87. package/engine/index-records.mjs +6 -4
  88. package/engine/index.mjs +0 -3
  89. package/engine/infobox-render.mjs +15 -3
  90. package/engine/infobox.mjs +13 -14
  91. package/engine/item-compiler.mjs +4 -10
  92. package/engine/item-docs.mjs +3 -5
  93. package/engine/item-registry.mjs +2 -10
  94. package/engine/journals.mjs +1 -5
  95. package/engine/literature-notes.mjs +4 -12
  96. package/engine/macros.mjs +1 -2
  97. package/engine/map-graphviz.mjs +12 -1
  98. package/engine/markdown-expressions.mjs +12 -1
  99. package/engine/markdownlint-run.mjs +567 -0
  100. package/engine/metadata-index.mjs +42 -12
  101. package/engine/note-addresses.mjs +143 -35
  102. package/engine/note-claims.mjs +14 -28
  103. package/engine/note-dates.mjs +3 -3
  104. package/engine/note-frontmatter.mjs +6 -0
  105. package/engine/note-ids.mjs +8 -14
  106. package/engine/note-package.mjs +8 -25
  107. package/engine/note-state.mjs +1 -3
  108. package/engine/note-vocabulary.mjs +148 -84
  109. package/engine/number-words.mjs +12 -1
  110. package/engine/pack-config.mjs +15 -25
  111. package/engine/pack-router.mjs +10 -16
  112. package/engine/pathnames.mjs +8 -22
  113. package/engine/pdf-build.mjs +773 -121
  114. package/engine/pdf-coverage.mjs +188 -0
  115. package/engine/pdf-index.mjs +54 -7
  116. package/engine/pdf-pages.mjs +247 -0
  117. package/engine/pdf-render.mjs +114 -4
  118. package/engine/pdf-toc.mjs +29 -32
  119. package/engine/project-init.mjs +46 -6
  120. package/engine/prose-config.mjs +1 -1
  121. package/engine/prose-lint.mjs +52 -88
  122. package/engine/readability-lint.mjs +9 -0
  123. package/engine/reckoning-markers.mjs +12 -1
  124. package/engine/reference-targets.mjs +7 -0
  125. package/engine/runtime-only-fields.mjs +5 -8
  126. package/engine/scene-levels.mjs +2 -2
  127. package/engine/scenes.mjs +1 -1
  128. package/engine/shortcode-references.mjs +7 -0
  129. package/engine/site-build.mjs +30 -16
  130. package/engine/site-index.mjs +10 -16
  131. package/engine/site-preview.mjs +6 -0
  132. package/engine/site-root.mjs +8 -1
  133. package/engine/social-tie-terms.mjs +12 -1
  134. package/engine/social-ties.mjs +3 -0
  135. package/engine/sql-tables.mjs +56 -20
  136. package/engine/standing-terms.mjs +11 -1
  137. package/engine/standings.mjs +11 -27
  138. package/engine/system-block.mjs +39 -141
  139. package/engine/system-vocabulary.mjs +6 -0
  140. package/engine/walk-files.mjs +69 -0
  141. package/engine/web-wikilinks.mjs +23 -3
  142. package/engine/wikilink-syntax.mjs +32 -21
  143. package/engine/wikilinks.mjs +27 -16
  144. package/engine/yaml-lint.mjs +7 -4
  145. package/githooks/attribution.mjs +8 -0
  146. package/githooks/commit-message.mjs +9 -0
  147. package/hm3/actors.mjs +9 -48
  148. package/hm3/default-item-art.mjs +9 -9
  149. package/hm3/item-fields.mjs +2 -9
  150. package/hm3/template-priority.mjs +6 -7
  151. package/hugo-theme/data/banner-kinds.yaml +78 -0
  152. package/hugo-theme/data/banners.yaml +2 -3
  153. package/hugo-theme/layouts/404.html +3 -3
  154. package/hugo-theme/layouts/partials/banner-url.html +31 -7
  155. package/hugo-theme/layouts/partials/hero-banner.html +13 -14
  156. package/hugo-theme/layouts/partials/related.html +1 -1
  157. package/hugo-theme/layouts/partials/warn-missing-banner.html +1 -1
  158. package/hugo-theme/static/css/style.css +1 -1
  159. package/manifest.mjs +11 -4
  160. package/markdownlint-config.mjs +1 -1
  161. package/package.json +6 -3
  162. package/release.mjs +83 -18
  163. package/sohl/actors.mjs +16 -27
  164. package/sohl/default-item-art.mjs +3 -3
  165. package/sohl/document-subtypes.mjs +1 -16
  166. package/sohl/infobox.mjs +2 -3
  167. package/sohl/item-builders.mjs +5 -8
  168. package/sohl/item-fields.mjs +5 -35
  169. package/sohl/items.mjs +0 -4
  170. package/sohl/kb-passes.mjs +3 -3
  171. package/types/container.d.mts +116 -5
  172. package/types/content-config.d.mts +83 -35
  173. package/types/deploy.d.mts +42 -6
  174. package/types/e2e.d.mts +35 -3
  175. package/types/engine/address-charset.d.mts +4 -17
  176. package/types/engine/address.d.mts +17 -8
  177. package/types/engine/art-fields.d.mts +7 -4
  178. package/types/engine/art-slots.d.mts +6 -0
  179. package/types/engine/base-compiler.d.mts +15 -17
  180. package/types/engine/content-address.d.mts +9 -12
  181. package/types/engine/content-embeds.d.mts +4 -2
  182. package/types/engine/content-format-check.d.mts +0 -64
  183. package/types/engine/content-index.d.mts +82 -3
  184. package/types/engine/content-links.d.mts +2 -4
  185. package/types/engine/culture-choice.d.mts +51 -8
  186. package/types/engine/data-keys.d.mts +2 -2
  187. package/types/engine/derived-fields.d.mts +1 -2
  188. package/types/engine/diagnostics.d.mts +27 -4
  189. package/types/engine/document-subtypes.d.mts +12 -31
  190. package/types/engine/event-views.d.mts +14 -2
  191. package/types/engine/exported-scene.d.mts +3 -2
  192. package/types/engine/field-spec.d.mts +13 -122
  193. package/types/engine/folder-notes.d.mts +0 -39
  194. package/types/engine/foreign-catalog.d.mts +2 -1
  195. package/types/engine/foundry-entries.d.mts +4 -5
  196. package/types/engine/frontmatter-lint.d.mts +2 -2
  197. package/types/engine/frontmatter.d.mts +19 -45
  198. package/types/engine/generate.d.mts +1 -1
  199. package/types/engine/helpers.d.mts +25 -42
  200. package/types/engine/homepage.d.mts +3 -10
  201. package/types/engine/ids.d.mts +0 -114
  202. package/types/engine/index-records.d.mts +6 -4
  203. package/types/engine/index.d.mts +0 -1
  204. package/types/engine/infobox-render.d.mts +7 -1
  205. package/types/engine/infobox.d.mts +3 -2
  206. package/types/engine/item-docs.d.mts +1 -1
  207. package/types/engine/item-registry.d.mts +2 -4
  208. package/types/engine/literature-notes.d.mts +1 -1
  209. package/types/engine/markdownlint-run.d.mts +40 -0
  210. package/types/engine/metadata-index.d.mts +9 -1
  211. package/types/engine/note-addresses.d.mts +27 -2
  212. package/types/engine/note-claims.d.mts +3 -5
  213. package/types/engine/note-ids.d.mts +2 -4
  214. package/types/engine/note-package.d.mts +1 -1
  215. package/types/engine/note-state.d.mts +1 -3
  216. package/types/engine/note-vocabulary.d.mts +28 -21
  217. package/types/engine/pack-config.d.mts +2 -6
  218. package/types/engine/pack-router.d.mts +4 -4
  219. package/types/engine/pathnames.d.mts +3 -3
  220. package/types/engine/pdf-build.d.mts +98 -12
  221. package/types/engine/pdf-coverage.d.mts +60 -0
  222. package/types/engine/pdf-index.d.mts +34 -5
  223. package/types/engine/pdf-pages.d.mts +143 -0
  224. package/types/engine/pdf-render.d.mts +45 -1
  225. package/types/engine/pdf-toc.d.mts +12 -14
  226. package/types/engine/prose-config.d.mts +1 -1
  227. package/types/engine/prose-lint.d.mts +11 -10
  228. package/types/engine/runtime-only-fields.d.mts +2 -2
  229. package/types/engine/site-build.d.mts +9 -5
  230. package/types/engine/site-index.d.mts +4 -5
  231. package/types/engine/site-root.d.mts +1 -1
  232. package/types/engine/sql-tables.d.mts +31 -2
  233. package/types/engine/standing-terms.d.mts +2 -1
  234. package/types/engine/standings.d.mts +7 -9
  235. package/types/engine/system-block.d.mts +10 -44
  236. package/types/engine/walk-files.d.mts +15 -0
  237. package/types/engine/wikilink-syntax.d.mts +18 -19
  238. package/types/engine/yaml-lint.d.mts +5 -4
  239. package/types/hm3/actors.d.mts +1 -2
  240. package/types/hm3/item-fields.d.mts +2 -4
  241. package/types/hm3/template-priority.d.mts +6 -7
  242. package/types/manifest.d.mts +3 -2
  243. package/types/release.d.mts +12 -7
  244. package/types/sohl/actors.d.mts +2 -1
  245. package/types/sohl/document-subtypes.d.mts +1 -2
  246. package/types/sohl/item-fields.d.mts +3 -7
  247. package/engine/retired-fields.mjs +0 -631
  248. package/types/engine/retired-fields.d.mts +0 -354
@@ -28,7 +28,7 @@ will find the row that produced it.
28
28
  ### Two ways to write it
29
29
 
30
30
  Every configuration ends at the same `defineConfig`, but reaches it by one of
31
- two routes, and three keys behave differently depending on which:
31
+ two routes, and four keys behave differently depending on which:
32
32
 
33
33
  | | `package-build.config.yaml` (data) | `package-build.config.mjs` (code) |
34
34
  | ---------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
@@ -36,7 +36,6 @@ two routes, and three keys behave differently depending on which:
36
36
  | `rootDir` | Forbidden — always the directory the file sits in | Authored, typically `import.meta.dirname` |
37
37
  | `foundryPackage` | Forbidden — always the adjacent `package.json` `name` | Authored |
38
38
  | `homepage` | Forbidden — always the adjacent `package.json` `homepage` | Authored |
39
- | `author` | Forbidden — always the adjacent `package.json` `author` | Authored |
40
39
  | `itemBuilders` | A **name** (`sohl`, `hm3`) or list of names, resolved against the registries this package ships | The registry object itself — real builder functions, which only code can carry |
41
40
 
42
41
  A file is chosen by its extension: `package-build.config.yaml`, then
@@ -66,7 +65,7 @@ authoring.
66
65
  | [`contentPackage`](#contentpackage) | string | yes | — |
67
66
  | [`foundryPackage`](#foundrypackage) | string | yes (`.mjs` only — derived in YAML); refused in a `documentation` package | — |
68
67
  | [`homepage`](#homepage) | string | no (`.mjs` only — derived in YAML) | `null` |
69
- | [`author`](#author) | string, or `{name, email?, url?}` | no (`.mjs` only — derived in YAML) | `null` |
68
+ | [`authors`](#authors) | list of `{name, email?, url?, discord?, role?}` | no | `[]` |
70
69
  | [`packageKind`](#packagekind) | `"systems"` \| `"modules"` \| `"documentation"` | yes | — |
71
70
  | [`stats`](#stats) | object | yes; refused in a `documentation` package | — |
72
71
  | [`itemBuilders`](#itembuilders) | object, or list of `{system, builders}` (or a name/list of names, in YAML) | no; refused in a `documentation` package | `{}` |
@@ -88,7 +87,7 @@ authoring.
88
87
 
89
88
  Any key outside this list is refused:
90
89
 
91
- > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, author, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
90
+ > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, authors, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
92
91
 
93
92
  (`rootDir` appears in that list because it is a key `defineConfig` itself
94
93
  accepts — an `.mjs` configuration authors it directly. A YAML configuration
@@ -100,12 +99,11 @@ refuses an authored `rootDir` earlier, with its own message — see
100
99
 
101
100
  Seven values in the resolved configuration are never transcribed by an author
102
101
  — they are computed from where the file sits, from the adjacent
103
- `package.json`, from the package kind, or from a name naming a table this
104
- package already ships. Authoring `rootDir`, `foundryPackage`, `homepage`,
105
- `author` or `stats.systemVersion` yourself is an **error**, not an override: a
106
- transcribed copy is free to drift from what it copied, which is exactly how
107
- `stats.systemVersion` once sat at a stale version for four releases while
108
- nothing said so. `assetRoot` has no author-facing spelling to refuse in the
102
+ `package.json`, from the package kind, from `authors`, or from a name naming a
103
+ table this package already ships. Authoring `rootDir`, `foundryPackage`,
104
+ `homepage`, `author` or `stats.systemVersion` yourself is an **error**, not an
105
+ override: a transcribed copy is free to drift from what it copied, with
106
+ nothing to say so. `assetRoot` has no author-facing spelling to refuse in the
109
107
  first place — it is never a key at all, only ever a computed value.
110
108
 
111
109
  #### `rootDir`
@@ -144,7 +142,7 @@ which applies before either loader form gets a chance to derive anything.
144
142
 
145
143
  If the adjacent `package.json` cannot be read, or declares no `name`:
146
144
 
147
- > `package-build: <package.json path> could not be read, and the configuration derives its Foundry package id, system version, homepage and author from it.`
145
+ > `package-build: <package.json path> could not be read, and the configuration derives its Foundry package id, system version and homepage from it.`
148
146
 
149
147
  > ``package-build: <package.json path> declares no `name`, which is what the Foundry package id is derived from.``
150
148
 
@@ -188,22 +186,17 @@ before the generated `baseURL` is written, so a missing or mismatched
188
186
 
189
187
  #### `author`
190
188
 
191
- The package's byline, normalised from either of npm's `author` forms — a
192
- string (`"Name <email> (url)"`, with the email and the URL both optional) or
193
- an object (`{name, email?, url?}`) — to the object form. `null` when the
194
- package declares none.
189
+ The package's byline: the first entry of [`authors`](#authors), in npm's
190
+ `author` shape — `{name, email?, url?}`, without `discord` or `role`. The site
191
+ build writes its `name` as `params.author`. `null` when `authors` is empty.
195
192
 
196
- - In a **YAML** configuration, writing `author:` is refused; the loader reads
197
- it from the adjacent `package.json` `author` instead:
193
+ `author` is never written, in either configuration form. Writing it is refused
194
+ as an unknown key, at the key's own line, because `authors` is the one list a
195
+ package names its people in:
198
196
 
199
- > ``package-build: <config file> declares `author`, which a data configuration may not: it is `package.json`'s own `author`. Remove the key.``
197
+ > ``package-build config: `author` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, authors, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog).``
200
198
 
201
- - In an **`.mjs`** configuration, `author` is an ordinary optional key, in
202
- either form:
203
-
204
- > ``package-build config: `author` must be `"Name"`, `"Name <email>"`, `"Name (url)"` or `"Name <email> (url)"` — npm's own `author` forms.``
205
-
206
- > ``package-build config: `author` must be a string or an object with `name`, `email` and `url`.``
199
+ `package.json`'s own `author` is npm's and is not read.
207
200
 
208
201
  #### `assetRoot`
209
202
 
@@ -349,10 +342,58 @@ package id:
349
342
  See [Derived values](#derived-values) — forbidden in a YAML configuration,
350
343
  optional (a non-empty string) in an `.mjs` one.
351
344
 
352
- ### `author`
345
+ ### `authors`
353
346
 
354
- See [Derived values](#derived-values) — forbidden in a YAML configuration,
355
- optional (either of npm's forms) in an `.mjs` one.
347
+ **Type:** list of mappings · **Optional** · default `[]`.
348
+
349
+ The people who made the package, in the order they are credited. It is the one
350
+ place a package names them:
351
+
352
+ - the generated Foundry manifest's `authors` is this list with each entry's
353
+ `role` dropped, because Foundry's author schema has no role — so
354
+ `packageBuild.manifest.authors` is refused (see
355
+ [`packageBuild.manifest`](#packagebuildmanifest));
356
+ - the site's byline, `params.author`, is the first entry's `name` (see the
357
+ derived [`author`](#author));
358
+ - a book's title page lists every entry, one per line, as `Name` or
359
+ `Name, Role`.
360
+
361
+ ```yaml
362
+ authors:
363
+ - name: Tom Rodriguez
364
+ email: toasty@heroiclands.org
365
+ url: https://www.heroiclands.org/
366
+ role: author
367
+ - name: A. Painter
368
+ discord: apainter
369
+ role: artist
370
+ - name: A. Painter
371
+ role: cartographer
372
+ ```
373
+
374
+ | Key | Type | Required | Meaning |
375
+ | ------------------- | ------ | -------- | ----------------------------------------------------------- |
376
+ | `authors[].name` | string | yes | As printed and as the manifest carries it. |
377
+ | `authors[].email` | string | no | Carried to the manifest; the first entry's is the byline's. |
378
+ | `authors[].url` | string | no | Carried to the manifest; the first entry's is the byline's. |
379
+ | `authors[].discord` | string | no | A Discord handle, carried to the manifest. |
380
+ | `authors[].role` | string | no | One role from the closed list below. |
381
+
382
+ `role` is one of `author`, `designer`, `developer`, `editor`, `proofreader`,
383
+ `artist`, `cartographer`, `layout`, `playtester` and `translator`, printed in
384
+ title case after the name. An entry carries one role; a person with two is
385
+ listed twice, as above. Any other value is refused at its key:
386
+
387
+ > ``package-build config: `authors[0].role` must be one of: author, designer, developer, editor, proofreader, artist, cartographer, layout, playtester, translator.``
388
+
389
+ An entry with no `name`, a key outside the table, or a list that is not a list
390
+ is refused at its key:
391
+
392
+ > ``package-build config: `authors[0].name` must be a non-empty string.``
393
+
394
+ > ``package-build config: `authors[0].handle` is not a recognized option (expected one of: name, email, url, discord, role).``
395
+
396
+ > ``package-build config: `authors` must be a list of people, each a mapping with at least a `name`.``
356
397
 
357
398
  ### `packageKind`
358
399
 
@@ -508,17 +549,17 @@ name form resolves before reaching here.
508
549
  **Type:** object · **Optional** · every key defaults to the conventional
509
550
  HeroicLands layout, resolved against `rootDir`:
510
551
 
511
- | Key | Default | What it is |
512
- | ----------------------- | ------------------------ | ----------------------------------------------------------------------------------------------- |
513
- | `paths.content` | `assets/content` | The content tree root. |
514
- | `paths.assets` | `assets` | The asset roots' parent, holding `icons/`, `images/` and `audio/`. |
515
- | `paths.contentIndex` | `build/content-index` | Where `content-index` writes this package's note index. Derived and disposable. |
516
- | `paths.packJson` | `build/packs-json` | Build-only per-entry JSON intermediate. |
517
- | `paths.stage` | `build/stage/packs` | Compiled LevelDB packs. |
518
- | `paths.unpack` | `build/tmp/packs` | Where `unpack` extracts JSON back to. |
519
- | `paths.foreignCache` | `build/cache/foreign` | Where a dependency declaring `itemCatalog: true` or `assetArchive: true` is unpacked. |
520
- | `paths.metadataCache` | `build/cache/metadata` | Where a dependency's published content index is fetched to, for every declared dependency. |
521
- | `paths.navigationCache` | `build/cache/navigation` | Where the site navigation heroiclands.org publishes is fetched to, for the generated Hugo menu. |
552
+ | Key | Default | What it is |
553
+ | ----------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
554
+ | `paths.content` | `assets/content` | The content tree root. |
555
+ | `paths.assets` | `assets` | The asset roots' parent, holding `icons/`, `images/` and `audio/`. |
556
+ | `paths.contentIndex` | `build/content-index` | Where `content-index` writes this package's note index. Derived and disposable. |
557
+ | `paths.packJson` | `build/packs-json` | Build-only per-entry JSON intermediate. |
558
+ | `paths.stage` | `build/stage/packs` | Compiled LevelDB packs. |
559
+ | `paths.unpack` | `build/tmp/packs` | Where `unpack` extracts JSON back to. |
560
+ | `paths.foreignCache` | `build/cache/foreign` | Where a dependency declaring `itemCatalog: true` or `assetArchive: true` is unpacked. |
561
+ | `paths.metadataCache` | `build/cache/metadata` | Where a published content index is fetched to, for every declared dependency and every relationship declaring `itemCatalog: true`. |
562
+ | `paths.navigationCache` | `build/cache/navigation` | Where the site navigation heroiclands.org publishes is fetched to, for the generated Hugo menu. |
522
563
 
523
564
  Every configured path must be **relative** — an absolute one would escape
524
565
  the repository the config anchors:
@@ -711,11 +752,6 @@ toolchain compiles:
711
752
 
712
753
  > ``package-build config: `packs[<index>].type` must be one of: Actor, Adventure, Item, JournalEntry, Macro, Scene.``
713
754
 
714
- `folders` is retired — a folder is a note (`type: folder`) now, materialised
715
- by the pack whose documents reference it through `packFolder`:
716
-
717
- > ``package-build config: `packs[<index>].folders` is retired — delete it. A folder is a note (`type: folder`) now, and a pack materialises the folders its documents reference through `packFolder`, so there is no per-pack hierarchy file to name.``
718
-
719
755
  A **companion** — a pack written by its parent pack's own compile pass — may
720
756
  not declare `default` (no note is ever routed into one), and may not nest
721
757
  further companions of its own:
@@ -836,39 +872,17 @@ over the content index, and linked from the homepage like any other page.
836
872
 
837
873
  So a page of documentation is a note — `type: doc`, addressed by its
838
874
  shortcode, and `pack: none` where it compiles into no Foundry document — and
839
- there is no second mechanism for mounting a directory of markdown. A
840
- configuration that names one is refused with a message saying where the page
841
- goes instead:
842
-
843
- > ``package-build config: `site.trees` is retired — a page is a note in the content tree. Give each page `type: doc`, a `shortcode` and `pack: none`, file it under `assets/content/`, and link it from the homepage or from a `doc` note that indexes it.``
844
-
845
- > ``package-build config: `site.readmeSections` is retired — a page is a note in the content tree. Give each page `type: doc`, a `shortcode` and `pack: none`, file it under `assets/content/`, and link it from the homepage or from a `doc` note that indexes it.``
846
-
847
- A configuration that asks the build to generate an index between the
848
- homepage and the pages is refused the same way, by name and with one message
849
- — `site.sections` (and the `listType` / `listSubType` an entry carried),
850
- `site.landing`, `site.backfillSections` and `site.list` alike, since how a
851
- listing renders is a content table's to say:
852
-
853
- > ``package-build config: `site.sections` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
854
-
855
- > ``package-build config: `site.landing` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
856
-
857
- > ``package-build config: `site.backfillSections` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
858
-
859
- > ``package-build config: `site.list` is retired — a site is its homepage and its pages, and any index between them is a `doc` note: write one with `type: doc`, a `shortcode` and `pack: none`, carrying a content table over the notes it lists, and link it from the homepage. Nothing is generated between the homepage and the pages, so delete the key.``
875
+ there is no second mechanism for mounting a directory of markdown.
860
876
 
861
877
  Where the Hugo tree is written is not a choice. `package-build site` writes
862
878
  the whole Hugo source tree under `build/hugo/` — the generated `hugo.toml`,
863
879
  the content mount at `build/hugo/content/`, Hugo's own cache — as a sibling
864
880
  of the deployment root `build/site/`, so nothing Hugo reads lands in what is
865
- published. A `site.out` is refused by name:
866
-
867
- > ``package-build config: `site.out` is retired — the site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`, and the location is not configurable. Remove the key.``
881
+ published.
868
882
 
869
883
  > ``package-build config: `site` must be a mapping.``
870
884
 
871
- > ``package-build config: `site.<key>` is not a recognized option (expected one of: base, assets, description, packages, pass, passOptions, notfound, hugo, search, maps).``
885
+ > ``package-build config: `site.<key>` is not a recognized option (expected one of: base, title, assets, description, packages, pass, passOptions, notfound, hugo, search, maps).``
872
886
 
873
887
  `site.assets` is the host every package's imagery is served from, and it is
874
888
  the one address in this file that is not this repository's own. A note names
@@ -1038,7 +1052,7 @@ in it has one source, and that source is where it is edited:
1038
1052
  | `taxonomies` | the toolchain — never written, because `taxonomy` and `term` are disabled kinds |
1039
1053
  | `outputs` | the toolchain — never written, because every listing kind is disabled |
1040
1054
  | `params.description` | `site.description`, which is required |
1041
- | `params.author` | `package.json` `author`, its `name`; absent when the package declares none |
1055
+ | `params.author` | the first entry of `authors`, its `name`; absent when `authors` is empty |
1042
1056
  | `params.cdnBaseURL` | `site.assets`, which is required |
1043
1057
  | `params.brand` | the organisation's brand links — `logo`, `licenseURL`, `discordURL` — in `engine/site-config.mjs` |
1044
1058
  | `params.notfound` | `site.notfound`; absent when undeclared |
@@ -1077,50 +1091,191 @@ Every other key is `site.hugo`'s to add.
1077
1091
 
1078
1092
  **Type:** object · **Optional** · default `null` (no book is built).
1079
1093
 
1080
- The content tree published as a book — a **selection**, not a rendering of
1081
- everything: `pdf.document` names the tree that says which notes the volume
1082
- carries and in what order, parsed by `engine/pdf-toc.mjs`'s
1083
- `parseDocumentTree` and not validated here. Whether a book is built at all
1084
- determined by the authored content tree: a tree containing only a homepage has
1085
- no content pages for a book.
1094
+ The content tree published as books — each a **selection**, not a rendering of
1095
+ everything. `pdf.books` lists the books, one entry per PDF; each book's tree
1096
+ says which notes that volume carries and in what order, and is parsed by
1097
+ `engine/pdf-toc.mjs`'s `parseDocumentTree` when the book is built. Whether any
1098
+ book is built at all is determined by the authored content tree: a tree
1099
+ containing only a homepage has no content pages for a book.
1086
1100
 
1087
1101
  Each selected place with a border or route receives a dedicated full-page
1088
1102
  vector itinerary after its entry. A selected map note prints each of its
1089
1103
  exported Scene's level backgrounds on its own page. The npm-installed Graphviz
1090
1104
  runtime draws itineraries.
1091
1105
 
1106
+ A package with one book declares a list of one:
1107
+
1092
1108
  ```yaml
1093
1109
  pdf:
1094
- title: The Hârn Ensemble
1095
- document: book.yaml
1096
- fonts:
1097
- serif: Libertinus Serif
1098
- mono: DejaVu Sans Mono
1110
+ out: build/pdf
1111
+ books:
1112
+ - id: core
1113
+ title: The Hârn Ensemble
1114
+ document: book.yaml
1115
+ out: harnensemble
1099
1116
  ```
1100
1117
 
1101
- | Key | Type | Required | Default |
1102
- | --------------- | --------------------------- | -------- | ---------------------- |
1103
- | `pdf.title` | string | yes | — |
1104
- | `pdf.document` | string (path) | yes | — |
1105
- | `pdf.subtitle` | string | no | `""` |
1106
- | `pdf.out` | string | no | `""` |
1107
- | `pdf.front` | string[] (markdown files) | no | `[]` |
1108
- | `pdf.fonts` | object | no | all empty |
1109
- | `pdf.iconFonts` | object (family → font file) | no | `{}` |
1110
- | `pdf.binary` | string | no | `""` — found on `PATH` |
1111
-
1112
- `pdf.title` and `pdf.document` are required **together** — a document with
1113
- no title produces a file whose name and cover say nothing about what a
1114
- reader downloaded, and a title with no document has nothing to print:
1118
+ Two books, one tree kept in a file and one written inline, sharing their faces
1119
+ and the copyright and licence statements:
1115
1120
 
1116
- > ``package-build config: `pdf.title` must be a non-empty string.``
1121
+ ```yaml
1122
+ pdf:
1123
+ out: build/pdf
1124
+ fonts:
1125
+ serif: Libertinus Serif
1126
+ copyright: © 2026 Tom Rodriguez
1127
+ license: |
1128
+ Text is licensed under CC BY-SA 4.0.
1129
+ Code is licensed under GPL-3.0-or-later.
1130
+ books:
1131
+ - id: core
1132
+ title: The World of Thalorna
1133
+ subtitle: A Song of Heroic Lands Sourcebook
1134
+ coverImage: assets/images/covers/core.webp
1135
+ document: book.yaml
1136
+ out: thalorna
1137
+ - id: vedyara
1138
+ title: Vedyara
1139
+ subtitle: A Gazetteer of Thalorna
1140
+ front:
1141
+ - assets/book/vedyara-preface.md
1142
+ contents:
1143
+ - sectionName: Vedyara
1144
+ contents:
1145
+ - filter: "type = 'place' AND data.culture = 'thalorna-note-lore-vedyariclt'"
1146
+ ```
1117
1147
 
1118
- > ``package-build config: `pdf.document` must be a non-empty string.``
1148
+ `package-build pdf` builds both: `build/pdf/thalorna-<version>.pdf` and
1149
+ `build/pdf/thalorna-vedyara-<version>.pdf`. `--book vedyara` builds the second
1150
+ alone.
1151
+
1152
+ The keys of `pdf` itself are shared by every book:
1153
+
1154
+ | Key | Type | Required | Default |
1155
+ | ------------------ | ----------------------------- | -------- | ---------------------- |
1156
+ | `pdf.books` | list of books | yes | — |
1157
+ | `pdf.out` | string (directory) | no | `""` — `build/dist` |
1158
+ | `pdf.fonts` | object | no | all empty |
1159
+ | `pdf.iconFonts` | object (family → font file) | no | `{}` |
1160
+ | `pdf.binary` | string | no | `""` — found on `PATH` |
1161
+ | `pdf.unprinted` | string (`WHERE` clause) | no | `""` |
1162
+ | `pdf.coverage` | `warning` \| `error` \| `off` | no | `warning` |
1163
+ | `pdf.indexSeeAlso` | boolean | no | `false` |
1164
+ | `pdf.copyright` | string (one line) | no | `""` |
1165
+ | `pdf.license` | string (one or more lines) | no | `""` |
1166
+
1167
+ - `pdf.out` is the **directory** every book is written into; `package-build pdf
1168
+ --out` overrides it.
1169
+ - `pdf.unprinted` is a `WHERE` clause over `notes` naming the notes that belong
1170
+ in no book, such as `"type IN ('folder', 'homepage')"`. Coverage skips them.
1171
+ A clause that does not run is an error at `pdf.unprinted`, and coverage is
1172
+ not reported for that run.
1173
+ - `pdf.coverage` says how a printable note that no book selects is reported:
1174
+ `warning` (the default), `error`, or `off` to say nothing. A printable note
1175
+ is a note that is neither a stub nor tagged `gm`, and that `pdf.unprinted`
1176
+ does not name. Each finding sits at the note's `type:` line:
1177
+
1178
+ > ``assets/content/Guides/Fieldcraft.md:2:1: warning: no book selects this note; add it to a book's query or to `pdf.unprinted```
1179
+
1180
+ Coverage is computed over every book in `pdf.books`, including when
1181
+ `package-build pdf --book` builds only one.
1182
+
1183
+ - `pdf.indexSeeAlso` adds a line to a book's index for each note that another
1184
+ book prints and this book mentions in authored prose, written
1185
+ `Name: _Book title_, p. N` with the page the note's entry starts on in its
1186
+ home book. It is `false` by default, and the index then lists only the notes
1187
+ the book is home to. See [The index](guides/book.md#the-index).
1188
+ - A note several books select prints in each of them. Its **home book** is the
1189
+ first book in `pdf.books` that selects it; a note a book selects twice is
1190
+ still one book's, and prints at each position. After the books are built the
1191
+ command prints, per book, its entries, its distinct notes and how many of
1192
+ those carry no `data.culture`, then the totals, then one line per note
1193
+ several books select, naming its home.
1194
+ - `pdf.copyright` is the copyright statement, one line, such as
1195
+ `© 2026 Tom Rodriguez`. `pdf.license` is the licence statement and may run to
1196
+ several lines, such as a Creative Commons notice or a trademark notice the
1197
+ material's terms require. Every book carries the same two; neither has a
1198
+ per-book form, and neither do the [`authors`](#authors), which every book
1199
+ lists.
1200
+
1201
+ Each entry of `pdf.books` is one PDF:
1202
+
1203
+ | Key | Type | Required | Default |
1204
+ | ------------------------ | ------------------------- | -------------- | ----------------------- |
1205
+ | `pdf.books[].id` | string | yes | — |
1206
+ | `pdf.books[].title` | string | yes | — |
1207
+ | `pdf.books[].subtitle` | string | no | `""` |
1208
+ | `pdf.books[].coverImage` | string (path) | no | `""` — no cover |
1209
+ | `pdf.books[].front` | string[] (markdown files) | no | `[]` |
1210
+ | `pdf.books[].out` | string (file stem) | no | `<contentPackage>-<id>` |
1211
+ | `pdf.books[].contents` | list (the tree, inline) | one of the two | — |
1212
+ | `pdf.books[].document` | string (path to the tree) | one of the two | — |
1213
+
1214
+ - `id` is lowercase letters, digits and hyphens, starting with a letter or
1215
+ digit. It is what `--book <id>` names and the tail of the default file stem.
1216
+ Each `id` is used once.
1217
+ - `contents` is the tree written inline — the same `sectionName` /
1218
+ `contents` / `filter` / `file` shape a tree file holds under its own
1219
+ `contents:` key — and `document` names the file holding it. A book declares
1220
+ exactly one of the two. A finding about an inline tree names
1221
+ `package-build.config.yaml` at the line of the offending key.
1222
+ - `out` is the book's **file stem**: the PDF is `<out>-<version>.pdf` (or
1223
+ `<out>.pdf` with no version) inside `pdf.out`. It is one path segment —
1224
+ letters, digits, `.`, `_` and `-`. Two books resolving to one stem, whether
1225
+ declared or defaulted, are refused, since the second would overwrite the
1226
+ first. A package with one book keeps the file name `<package>-<version>.pdf`
1227
+ by declaring `out: <package>`.
1228
+ - `coverImage` names the picture that opens the book as page 1: it fills the
1229
+ page, cropped to fit, with the title and subtitle centred in white over a
1230
+ translucent dark band across the upper third. The cover carries no footer or
1231
+ page number. A book without `coverImage` opens on its title page, which
1232
+ lists the title, subtitle and version, then the [`authors`](#authors) one
1233
+ per line as `Name` or `Name, Role`, and sets `pdf.copyright` and
1234
+ `pdf.license` at its foot.
1235
+ - A section whose queries select no note is left out of the book without a
1236
+ finding. A book whose queries select no note at all is an error at its `id`
1237
+ and is not built:
1238
+
1239
+ > ``package-build.config.yaml:9:15: error: book `sinale` selects no note``
1240
+
1241
+ - `coverImage`, `document` and each `front` file resolve against the directory
1242
+ the configuration sits in. Whether each exists is checked when the book is
1243
+ built — by `package-build pdf`, by a release, and by `package-build init
1244
+ --check` — and never when the configuration loads, so `lint`, `site` and
1245
+ every other command run without a cover held outside the tree. A missing
1246
+ file is an error at its key, and that book is not built:
1247
+
1248
+ > ``package-build.config.yaml:14:11: error: book `core`: the cover image `assets/images/covers/core.webp` does not exist, so the book is not built``
1249
+
1250
+ Every refusal names its key, and a YAML configuration's refusal starts with the
1251
+ file, line and column the key is written at:
1252
+
1253
+ > ``package-build config: `pdf.books` is required — list the books this package publishes, one entry each.``
1254
+
1255
+ > ``package-build config: `pdf.books` must be a non-empty list of books.``
1256
+
1257
+ > ``package-build config: `pdf.books[1].id` is `core`, which names a book more than once.``
1258
+
1259
+ > ``package-build config: `pdf.books[0].id` is `Core Book`, which is not lowercase letters, digits and hyphens starting with a letter or digit (^[a-z0-9][a-z0-9-]*$) — it is written as `--book <id>` and ends the book's default file name.``
1260
+
1261
+ > ``package-build config: `pdf.books[0].title` must be a non-empty string.``
1262
+
1263
+ > ``package-build config: `pdf.books[0]` must declare exactly one of `contents` and `document` — the tree written inline, or the file holding it.``
1264
+
1265
+ > ``package-build config: `pdf.books[0].out` is `dist/core`, which is not a file stem — letters, digits, `.`, `_` and `-`, with no directory; the directory is `pdf.out`.``
1266
+
1267
+ > ``package-build config: `pdf.books[1].out` gives the file stem `same`, which `pdf.books[0]` already uses — the second book would overwrite the first.``
1268
+
1269
+ > ``package-build config: `pdf.books[0].front` must be a list of markdown files.``
1270
+
1271
+ > ``package-build config: `pdf.books[0].<key>` is not a recognized option (expected one of: id, title, subtitle, coverImage, front, out, contents, document).``
1272
+
1273
+ > ``package-build config: `pdf.coverage` must be one of: warning, error, off.``
1274
+
1275
+ > ``package-build config: `pdf.copyright` must be one line — the licence statement is the one that runs on.``
1119
1276
 
1120
1277
  > ``package-build config: `pdf` must be a mapping.``
1121
1278
 
1122
- > ``package-build config: `pdf.front` must be a list of markdown files.``
1123
-
1124
1279
  `pdf.fonts` names font **families**, not files — the renderer asks the font
1125
1280
  stack for a family by name:
1126
1281
 
@@ -1156,7 +1311,10 @@ books.
1156
1311
 
1157
1312
  Any other key on `pdf` is refused:
1158
1313
 
1159
- > ``package-build config: `pdf.<key>` is not a recognized option (expected one of: title, subtitle, document, out, front, fonts, iconFonts, binary).``
1314
+ > ``package-build config: `pdf.<key>` is not a recognized option (expected one of: books, out, fonts, iconFonts, binary, unprinted, coverage, copyright, license).``
1315
+
1316
+ A book's own keys — `title`, `subtitle`, `document` and `front` — are refused
1317
+ on `pdf` itself by this message, at the key's own line.
1160
1318
 
1161
1319
  ### `compatibility`
1162
1320
 
@@ -1256,7 +1414,9 @@ Omit it where the two are the same word, which they are for every system:
1256
1414
  `itemCatalog` opts into extracting the named package's Item packs so the
1257
1415
  actors pass can resolve embedded items this repository does not hold — off
1258
1416
  by default, since depending on a package is not the same as needing its
1259
- item catalogue at build time. It requires a `manifest`:
1417
+ item catalogue at build time. A relationship of any kind that declares it
1418
+ also has its content index fetched and read, so the lint resolves the same
1419
+ embedded-item addresses the compile does. It requires a `manifest`:
1260
1420
 
1261
1421
  > ``package-build config: `relationships.<kind>[<index>].itemCatalog` must be true or false.``
1262
1422
 
@@ -1459,12 +1619,6 @@ manifest's package homepage also stays at that root. A page staged under
1459
1619
 
1460
1620
  > ``package-build config: `publish.address.prefix` must not begin with a slash.``
1461
1621
 
1462
- `publish.address.landing` is **retired** — it named which note addressed a
1463
- whole section rather than a page within one, and there are no sections to
1464
- address:
1465
-
1466
- > ``package-build config: `publish.address.landing` is a retired option — delete it. It named which note addressed a whole section rather than a page within one, and there are no sections to address: a section is a Hugo content directory the note format does not carry, so no note lands one and every page is addressed `<type>-<shortcode>`. Nothing replaces it.``
1467
-
1468
1622
  Any other key under `publish.address` is refused:
1469
1623
 
1470
1624
  > ``package-build config: `publish.address.<key>` is not a recognized option (expected one of: prefix).``
@@ -1689,18 +1843,19 @@ survives into the manifest under its own name. `package.json.description` is
1689
1843
  npm package metadata. It does not supply the Foundry description or the site
1690
1844
  description (see [`site.description`](#site)); each surface has its own copy.
1691
1845
 
1692
- | Forbidden key | Derived from |
1693
- | ------------------------------------- | ---------------------------------------------------------------- |
1694
- | `packageBuild.manifest.id` | `foundryPackage`, itself derived from `package.json` `name` |
1695
- | `packageBuild.manifest.version` | `package.json` `version` |
1696
- | `packageBuild.manifest.description` | `packageBuild.manifest.descriptionHtml` |
1697
- | `packageBuild.manifest.url` | `package.json` `repository` |
1698
- | `packageBuild.manifest.bugs` | `package.json` `repository` |
1699
- | `packageBuild.manifest.manifest` | `package.json` `repository` and the release tag |
1700
- | `packageBuild.manifest.download` | `package.json` `repository` and the release tag |
1701
- | `packageBuild.manifest.compatibility` | the top level of `package-build.config.yaml` |
1702
- | `packageBuild.manifest.relationships` | the top level of `package-build.config.yaml` |
1703
- | `packageBuild.manifest.packs` | the `packs` list at the top level of `package-build.config.yaml` |
1846
+ | Forbidden key | Derived from |
1847
+ | ------------------------------------- | --------------------------------------------------------------------------------------------- |
1848
+ | `packageBuild.manifest.id` | `foundryPackage`, itself derived from `package.json` `name` |
1849
+ | `packageBuild.manifest.version` | `package.json` `version` |
1850
+ | `packageBuild.manifest.authors` | the `authors` list at the top level of package-build.config.yaml, without each entry's `role` |
1851
+ | `packageBuild.manifest.description` | `packageBuild.manifest.descriptionHtml` |
1852
+ | `packageBuild.manifest.url` | `package.json` `repository` |
1853
+ | `packageBuild.manifest.bugs` | `package.json` `repository` |
1854
+ | `packageBuild.manifest.manifest` | `package.json` `repository` and the release tag |
1855
+ | `packageBuild.manifest.download` | `package.json` `repository` and the release tag |
1856
+ | `packageBuild.manifest.compatibility` | the top level of `package-build.config.yaml` |
1857
+ | `packageBuild.manifest.relationships` | the top level of `package-build.config.yaml` |
1858
+ | `packageBuild.manifest.packs` | the `packs` list at the top level of `package-build.config.yaml` |
1704
1859
 
1705
1860
  > ``package-build config: `packageBuild.manifest.version` is derived from package.json `version` and must not be declared — it would be overwritten, and the two would disagree with nothing to say so.``
1706
1861
 
@@ -1919,21 +2074,18 @@ the source directory:
1919
2074
 
1920
2075
  ---
1921
2076
 
1922
- ## Every retired or forbidden key, in one place
2077
+ ## Every forbidden key, in one place
1923
2078
 
1924
2079
  | Key | Why |
1925
2080
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
1926
- | `publish.address.landing` | Retired — named a whole-section landing, and there are no sections to address. |
1927
- | `packs[].folders` | Retired — a folder is a note (`type: folder`), materialised by the pack whose documents reference it. |
1928
2081
  | `rootDir` | Forbidden in a YAML configuration — always the file's own directory. |
1929
2082
  | `foundryPackage` | Forbidden in a YAML configuration — always the adjacent `package.json` `name`. |
1930
- | `homepage`, `author` | Forbidden in a YAML configuration — always the adjacent `package.json`'s own `homepage` and `author`. |
2083
+ | `homepage` | Forbidden in a YAML configuration — always the adjacent `package.json`'s own `homepage`. |
2084
+ | `author` | Refused as an unknown key — the byline is the first entry of [`authors`](#authors). |
2085
+ | `packageBuild.manifest.authors` | Forbidden — derived from [`authors`](#authors), without each entry's `role`. |
1931
2086
  | `stats.systemId` | Forbidden in every configuration — derived from `packageKind`, `requiresSystem` or a lone declared system. |
1932
2087
  | `stats.systemVersion` | Forbidden in every configuration — derived from `package.json` (a system) or `systems:` / `relationships.systems` (a module). |
1933
2088
  | `packageBuild.manifest.id`, `.version`, `.description`, `.url`, `.bugs`, `.manifest`, `.download`, `.compatibility`, `.relationships`, `.packs` | Forbidden — each is derived from `package.json` or the top level of `package-build.config.yaml`; see [`packageBuild.manifest`](#packagebuildmanifest). |
1934
- | `site.out` | Retired — the site build writes its content mount at `build/hugo/content`, beside the generated `hugo.toml`. |
1935
- | `site.trees`, `site.readmeSections` | Retired — a page is a note in the content tree. |
1936
- | `site.sections`, `site.landing`, `site.backfillSections`, `site.list` | Retired — a site is its homepage and its pages, and any index between them is a `doc` note. |
1937
2089
  | `site.hugo.baseURL`, `.title`, `.locale`, `.publishDir`, `.contentDir`, `.themesDir`, `.theme`, `.disableKinds`, `.taxonomies`, `.outputs`, `.params.description`, `.params.author`, `.params.cdnBaseURL`, `.params.brand`, `.params.notfound`, `.markup.goldmark.renderer.unsafe`, `.menu` | Forbidden — each is written by the site build from a source it names; see [the generated Hugo configuration](#the-generated-hugo-configuration). |
1938
2090
  | `publish.site` (any value) | Refused — publication follows the authored content tree. |
1939
2091
  | `packs`, `itemBuilders`, `docs`, `compatibility`, `relationships`, `systems`, `requiresSystem`, `stats`, `foundryPackage` | Forbidden in a `documentation` package — each describes a Foundry package this kind is not; see the key's own section for its located refusal message. |
@@ -90,4 +90,6 @@ The [first-note guide](authoring/first-note.md) shows the build commands and how
90
90
 
91
91
  Place notes may declare a governing affiliation with `data.government`, or explicit `null` for complete anarchy. Positive population with an omitted government produces an advisory. See [place governments](authoring/frontmatter.md#place-governments).
92
92
 
93
+ Places, affiliations, lore and beings may name their culture in `data.culture`, and a `doc` of `subType: settingguide` must. A place or affiliation that names none inherits the culture of its parents. See [culture in the index](reference/format-details.md#culture-in-the-index).
94
+
93
95
  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).