@heroiclands/package-build 23.3.1 → 23.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 23.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - `package-build lint` requires a being's `complexion` to be a list of complexion words, `[]` where no condition is established, and refuses a single word.
8
+ - `extra_features` admits only short plain descriptions of visible physical marks; colours, skin conditions, gear, voice, manner, looks, handedness, sterility, apparent age and punctuation are errors.
9
+ - `package-build npc new` writes the complexion it draws as a list.
10
+
11
+ ## 23.3.2
12
+
13
+ ### Patch Changes
14
+
15
+ - Cultures can inherit their language and appearance from parent cultures when generating characters.
16
+
3
17
  ## 23.3.1
4
18
 
5
19
  ### Patch Changes
@@ -141,6 +141,7 @@ import { profileFindings } from "../engine/appearance-profile.mjs";
141
141
  import { namesFindings } from "../engine/language-names.mjs";
142
142
  import { parseAddress, renderAddress } from "../engine/address.mjs";
143
143
  import { cultureKey, generateNpc, npcDefaults, npcSpecProblems } from "../engine/npc-generate.mjs";
144
+ import { cultureFieldSource, cultureHierarchyProblems } from "../engine/culture-parents.mjs";
144
145
 
145
146
  /**
146
147
  * The packs `unpack` extracts.
@@ -2613,11 +2614,12 @@ function newNpc(config, argv) {
2613
2614
  throw new Error(`${argv.out} already exists, and \`npc new\` never overwrites a note`);
2614
2615
 
2615
2616
  const root = config.paths.content;
2617
+ const corpusProblems = [];
2616
2618
  const records = indexRecordsFor({
2617
2619
  contentBase: root,
2618
2620
  config,
2619
2621
  skipDirectories: config.skipDirectories,
2620
- problems: [],
2622
+ problems: corpusProblems,
2621
2623
  });
2622
2624
  const index = buildLinkIndex(root, {
2623
2625
  config,
@@ -2643,6 +2645,12 @@ function newNpc(config, argv) {
2643
2645
  `\`data.culture\` ${JSON.stringify(written)} is not a culture: it names a ` +
2644
2646
  `${culture.type ?? hit.type ?? "note"} of subType ${culture.subType ?? "none"}`,
2645
2647
  );
2648
+ const hierarchyProblems = cultureHierarchyProblems(records, key);
2649
+ for (const problem of hierarchyProblems) emitDiagnostic(problem);
2650
+ if (hierarchyProblems.some((problem) => problem.severity === "error")) {
2651
+ process.exitCode = 1;
2652
+ return;
2653
+ }
2646
2654
  const cultureNote = located(hit);
2647
2655
  const profile = culture.data?.appearanceProfile;
2648
2656
  if (profile === undefined || profile === null)
@@ -2652,8 +2660,13 @@ function newNpc(config, argv) {
2652
2660
  `${key} states no \`data.appearanceProfile\`, so \`npc new\` cannot draw its ` +
2653
2661
  "people's look",
2654
2662
  );
2663
+ const profileSource = cultureFieldSource(records, key, "appearanceProfile") ?? hit;
2655
2664
  const profileErrors = profileFindings(
2656
- { file: cultureNote.file, raw: hit.raw, fm: culture },
2665
+ {
2666
+ file: located(profileSource).file,
2667
+ raw: profileSource.raw,
2668
+ fm: profileSource.fm ?? profileSource,
2669
+ },
2657
2670
  BEING_LOOK_VOCABULARY,
2658
2671
  );
2659
2672
  if (profileErrors.length) {
@@ -2669,6 +2682,8 @@ function newNpc(config, argv) {
2669
2682
  `${key} states no \`data.language\`, so \`npc new\` has no name lists to draw ` +
2670
2683
  "from; name the language skill note its people speak",
2671
2684
  );
2685
+ const languageSource = cultureFieldSource(records, key, "language") ?? hit;
2686
+ const languageSourceNote = located(languageSource);
2672
2687
  const tuple = parseAddress(tongue, {
2673
2688
  package: index.contentPackage,
2674
2689
  system: "note",
@@ -2680,14 +2695,14 @@ function newNpc(config, argv) {
2680
2695
  const languageHit = languageKey ? index.addressHit(languageKey) : undefined;
2681
2696
  if (!languageHit)
2682
2697
  throw noteError(
2683
- cultureNote,
2698
+ languageSourceNote,
2684
2699
  ["data", "language"],
2685
2700
  `${key}'s \`data.language\` names no note in this package or its dependencies`,
2686
2701
  );
2687
2702
  const language = languageHit.fm ?? languageHit;
2688
2703
  if (String(language.type ?? languageHit.type) !== "skill" || language.subType !== "language")
2689
2704
  throw noteError(
2690
- cultureNote,
2705
+ languageSourceNote,
2691
2706
  ["data", "language"],
2692
2707
  `${key}'s \`data.language\` ${languageKey} is not a language: it names a ` +
2693
2708
  `${language.type ?? languageHit.type ?? "note"} of subType ` +
@@ -26,7 +26,7 @@ A sheltered place where ships load and unload.
26
26
 
27
27
  `shortcode` and `type` identify a HeroicLands note; `name.full` gives readers its name. The YAML ends at the second `---`. Everything after it is the shared Markdown body. Run `npm run lint` to check the note, then `npm run build:site` to build its web page. `npm run serve:site` serves the site locally; `npm run build:book` builds the book. A Foundry package's `npm run build:db` also compiles its configured packs. The generated project supplies these scripts and their configuration.
28
28
 
29
- For optional writing suggestions, run `npx package-build prose lint assets/content/Places/Harbor.md`. This reports difficult sentences and simpler word choices with line and column numbers. It does not change the note or run as part of `npm run lint`. The [prose analysis command](../commands.md#package-build-prose-lint-path) explains the settings and how to read each suggestion.
29
+ For optional writing suggestions, run `npx package-build prose lint assets/content/Places/Harbor.md`. This reports difficult sentences and simpler word choices with line and column numbers. It does not change the note or run as part of `npm run lint`. The [prose analysis command](../commands.md#package-build-prose-lint-path-and-prose-score-path) explains the settings and how to read each suggestion.
30
30
 
31
31
  ## What the note becomes
32
32
 
@@ -35,7 +35,7 @@ The [note-type reference](../reference/note-types.md) lists every field accepted
35
35
 
36
36
  A being's `data.archetypes` names the roles it can fill in an adventure. Choose every specific role that fits, such as `entertainer` for a performer or `guildsperson` for someone whose professional training or connections matter. Use `[commoner]` when no specific archetype fits. `commoner` cannot share the list with another value. The [archetype reference](../reference/format-details.md#actors) defines the available roles.
37
37
 
38
- A being's look is stated in a closed vocabulary. `data.gender` and `data.frame` take one word each, as do `data.appearance.eye_color`, `hair_color` and `skin_color`; `data.appearance.complexion` takes one word or a list of them. A value outside a field's list — a variant spelling such as `gray`, a word from another field's list, or an empty string — is an error naming the words the field takes. Leave a field out when the fact is unrecorded. `data.appearance.extra_features` is a free-text list for the marks no word covers. The [look reference](../reference/format-details.md#a-beings-look) gives every word and what it means.
38
+ A being's look is stated in a closed vocabulary. `data.gender` and `data.frame` take one word each, as do `data.appearance.eye_color`, `hair_color` and `skin_color`; `data.appearance.complexion` takes a list of words, `[]` where no condition is established, and is required once a being states an appearance. A value outside a field's list — a variant spelling such as `gray`, a word from another field's list, or an empty string — is an error naming the words the field takes. Leave a field out when the fact is unrecorded. `data.appearance.extra_features` lists the visible physical marks no word covers, each a short plain description of one, such as `a burn scar on the left forearm`. The [look reference](../reference/format-details.md#a-beings-look) gives every word and what it means.
39
39
 
40
40
  A `sohl:`, `hm3:` or `dnd5e:` block holds values for that Foundry system. Its `system:` child contains values sent to the system's DataModel. The system block can override shared routing and art where the declared field permits it. `data.pack` and `data.packFolder` are shared routes; a system-specific route belongs in that system block. The [detailed mapping reference](../reference/format-details.md#mappings-every-type-shares) and [system-block explanation](../reference/format-details.md#frontmatter-has-three-regions) cover precedence and emitted fields.
41
41
 
package/docs/commands.md CHANGED
@@ -1898,7 +1898,10 @@ checks frontmatter against the declared vocabulary and each system's `sohl:`
1898
1898
  / `hm3:` block, the package's charset (so a book can pick a font that covers
1899
1899
  it), that an icon a note writes is one the registry declares, and that
1900
1900
  markup inside a note's body does not smuggle in a character the charset
1901
- check cannot see. `--no-references` turns off the check that a frontmatter
1901
+ check cannot see. A being's appearance is held to
1902
+ [a being's look](reference/format-details.md#a-beings-look): `complexion` a
1903
+ list, and `extra_features` only short plain descriptions of physical marks.
1904
+ `--no-references` turns off the check that a frontmatter
1902
1905
  shortcode reference lands, including references in schema-declared system
1903
1906
  fields, for a tree whose cross-package references it cannot see. Address
1904
1907
  references in system fields resolve against this package and its declared
@@ -2828,6 +2831,14 @@ culture missing either, with an error at the culture note:
2828
2831
  `data.names` is an error at the language note. See
2829
2832
  [name lists](reference/format-details.md#name-lists).
2830
2833
 
2834
+ The content-index JSONL resolves a culture's `data.language` and
2835
+ `data.appearanceProfile` before this command reads them. A child culture may
2836
+ inherit each omitted field from its `data.parents`, while a stated value keeps
2837
+ precedence. The command consumes the resolved values and keeps the selected
2838
+ child culture on the generated note. A parent cycle or conflicting inherited
2839
+ sources are errors; complete fields on the selected culture do not excuse a
2840
+ cycle. See [culture parents](reference/format-details.md#culture-parents-and-inherited-fields).
2841
+
2831
2842
  A profile or name lists with problems are refused with a finding at each.
2832
2843
 
2833
2844
  A spec that states `name.full` keeps it, and the name must fit the language's
@@ -2915,7 +2926,7 @@ data:
2915
2926
  eye_color: brown
2916
2927
  hair_color: black
2917
2928
  skin_color: brown
2918
- complexion: clear
2929
+ complexion: [clear]
2919
2930
  extra_features: []
2920
2931
  ---
2921
2932
 
@@ -13,15 +13,15 @@ Every typed note requires a nonempty body, including drafts, folders and homepag
13
13
 
14
14
  Start with [your first note](authoring/first-note.md). It explains the smallest valid note and shows the JournalEntry, web page, book entry, and content-index record made from it. Then use the chapters below in the order your note needs them.
15
15
 
16
- | Need | Read |
17
- | --------------------------------------------------------- | ----------------------------------------------------------- |
18
- | Frontmatter, `data`, system blocks, and infoboxes | [Frontmatter](authoring/frontmatter.md) |
19
- | Addresses, links, anchors, fences, and inline expressions | [Links and markup](authoring/links-and-markup.md) |
20
- | Images, icons, audio, provenance, and sidecars | [Assets](authoring/assets.md) |
21
- | Canonical dates, calendars, eras, and conversions | [Dates and calendars](authoring/dates-and-calendars.md) |
22
- | All accepted note types, subtypes, and fields | [Note type reference](reference/note-types.md) |
23
- | Exact format details and system mappings | [Detailed reference](reference/format-details.md) |
24
- | Optional readability and wording suggestions | [Prose analysis](commands.md#package-build-prose-lint-path) |
16
+ | Need | Read |
17
+ | --------------------------------------------------------- | -------------------------------------------------------------------------------- |
18
+ | Frontmatter, `data`, system blocks, and infoboxes | [Frontmatter](authoring/frontmatter.md) |
19
+ | Addresses, links, anchors, fences, and inline expressions | [Links and markup](authoring/links-and-markup.md) |
20
+ | Images, icons, audio, provenance, and sidecars | [Assets](authoring/assets.md) |
21
+ | Canonical dates, calendars, eras, and conversions | [Dates and calendars](authoring/dates-and-calendars.md) |
22
+ | All accepted note types, subtypes, and fields | [Note type reference](reference/note-types.md) |
23
+ | Exact format details and system mappings | [Detailed reference](reference/format-details.md) |
24
+ | Optional readability and wording suggestions | [Prose analysis](commands.md#package-build-prose-lint-path-and-prose-score-path) |
25
25
 
26
26
  ## The shared note
27
27
 
@@ -92,10 +92,17 @@ Place notes may declare a governing affiliation with `data.government`, or expli
92
92
 
93
93
  A region may list its settlements in a table under `## Settlements` — name, market, people and holder, one row each — tagged with the holding model it is written in. A row links the settlement's note where one exists, and `package-build lint` holds the two to agreement. See [a region's settlements table](reference/format-details.md#a-regions-settlements-table).
94
94
 
95
- A being states its gender, frame and appearance in words from one closed vocabulary: `data.gender`, `data.frame`, and `data.appearance.eye_color`, `hair_color`, `skin_color` and `complexion` each take only the words listed for them, and any other value is an error at the value. `data.appearance.extra_features` stays free text. See [a being's look](reference/format-details.md#a-beings-look).
95
+ A being states its gender, frame and appearance in words from one closed vocabulary: `data.gender`, `data.frame`, and `data.appearance.eye_color`, `hair_color`, `skin_color` and `complexion` each take only the words listed for them, and any other value is an error at the value. `complexion` is a list, required once a being states an appearance. `data.appearance.extra_features` lists the being's visible physical marks, each a short plain description of one, and an entry stating a colour, a skin condition or anything that is not a mark is an error. See [a being's look](reference/format-details.md#a-beings-look).
96
96
 
97
97
  A culture note may state what its people look like in `data.appearanceProfile`: weighted words for frame, eyes, hair, skin and complexion, and height ranges by gender, all from the same vocabulary. A culture names its tongue in `data.language`, and that language skill note may state `data.names`: the patterns its speakers' names are built by and the lists each part is drawn from. `package-build npc new` writes a complete npc note from a spec of a culture and a gender, drawing the name from those lists and everything else the spec leaves out from the profile. See [appearance profiles](reference/format-details.md#appearance-profiles), [name lists](reference/format-details.md#name-lists) and [the command](commands.md#package-build-npc-new).
98
98
 
99
+ A culture may name its larger cultures in `data.parents`. Each entry names a
100
+ culture lore note. Omitted `data.language` and `data.appearanceProfile` fields
101
+ inherit independently through those parents; the content-index JSONL contains
102
+ the resolved values, which `npc new` reads directly. A stated field wins over
103
+ its inherited value. See [culture parents](reference/format-details.md#culture-parents-and-inherited-fields)
104
+ for a complete example, parent ordering and findings.
105
+
99
106
  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).
100
107
 
101
108
  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).
@@ -31,7 +31,7 @@ document ID appears under another address, and **withdrawn** when that ID
31
31
  appears under none. Findings are warnings by default. `--strict` turns them
32
32
  into errors for a release gate. The comparison reads shipped Item packs, so it
33
33
  reports the surface consumers actually resolve rather than inferring changes
34
- from filenames. Use the [Address reference](../content-format.md#addresses)
34
+ from filenames. Use the [Address reference](../reference/format-details.md#addresses)
35
35
  when choosing a durable shortcode.
36
36
 
37
37
  ## Build the release files
@@ -1916,7 +1916,7 @@ largest print slot needs. Both exempt a vector asset, which carries no pixel
1916
1916
  dimensions and no aspect that survives being drawn at a nominal size.
1917
1917
 
1918
1918
  `role: map` also selects a captioned image's counter — see
1919
- [figures and numbered references](../authoring/links-and-markup.md#figures-and-numbered-references).
1919
+ [captions and numbered references](../authoring/links-and-markup.md#captions-and-numbered-references).
1920
1920
  A fence whose picture draws an address with that role counts as `Map` rather
1921
1921
  than `Figure`, on every surface that numbers it, because the role is the one
1922
1922
  fact the fence's own markup cannot state.
@@ -2715,6 +2715,104 @@ table would otherwise need: the authored `ORDER BY` decides the section order to
2715
2715
  **Beware `packFolder`.** It is a note's _pack_ folder, not its directory — the
2716
2716
  directory is `file.folder`.
2717
2717
 
2718
+ ##### Culture parents and inherited fields
2719
+
2720
+ A `lore` note of `subType: culture` may list the larger cultures it belongs to
2721
+ in `data.parents`. Each entry is an Address naming another culture lore note;
2722
+ a missing target, a target of another type or subtype, and a parent cycle are
2723
+ errors at the offending list entry. No other lore subtype accepts the key.
2724
+
2725
+ A culture inherits `data.language` and `data.appearanceProfile` independently
2726
+ when it leaves either field out. A stated value wins for that field. The
2727
+ profile is inherited as one complete object; its weights and ranges are not
2728
+ merged with another profile. Parents are searched in listed order, depth-first:
2729
+ the first parent's chain is searched before the next parent's chain. Sources
2730
+ at the same depth that disagree on a field produce a finding. Nothing is
2731
+ inherited through a broken parent address.
2732
+
2733
+ The emitted content-index JSONL carries the resolved fields in the child's
2734
+ `data`, exactly as if the child had stated them. `package-build npc new` and
2735
+ other consumers read those resolved values; they do not walk culture parents.
2736
+ The child's `data.culture` remains its own Address. A being, place or
2737
+ affiliation naming the parent in `data.culture` belongs to that named culture;
2738
+ it does not match the parent's children.
2739
+
2740
+ These complete notes give Riverfolk their parent culture's tongue and profile:
2741
+
2742
+ ```markdown
2743
+ ---
2744
+ shortcode: reedfolk
2745
+ name: { full: Reedfolk }
2746
+ type: lore
2747
+ subType: culture
2748
+ data:
2749
+ language: skill-reedtongue
2750
+ appearanceProfile:
2751
+ frame: { medium: 1 }
2752
+ eye_color: { brown: 1 }
2753
+ hair_color: { black: 1 }
2754
+ skin_color: { tawny: 1 }
2755
+ complexion: { clear: 1 }
2756
+ height:
2757
+ female: { min: 1.5m, max: 1.7m }
2758
+ male: { min: 1.6m, max: 1.8m }
2759
+ ---
2760
+
2761
+ Reedfolk share the reed tongue and live along the marsh's waterways. Their
2762
+ settlements trade by boat, hold seasonal gatherings and pass their stories
2763
+ between families at those gatherings.
2764
+ ```
2765
+
2766
+ ```markdown
2767
+ ---
2768
+ shortcode: riverfolk
2769
+ name: { full: Riverfolk }
2770
+ type: lore
2771
+ subType: culture
2772
+ data:
2773
+ parents: [lore-reedfolk]
2774
+ ---
2775
+
2776
+ Riverfolk belong to the Reedfolk culture and share its tongue. They live
2777
+ beside the river's deep channels, where their boats carry grain, timber and
2778
+ woven reeds between settlements.
2779
+ ```
2780
+
2781
+ The parent names this language note, whose lists supply generated names:
2782
+
2783
+ ```markdown
2784
+ ---
2785
+ shortcode: reedtongue
2786
+ name: { full: Reed Tongue }
2787
+ type: skill
2788
+ subType: language
2789
+ data:
2790
+ pack: skills
2791
+ names:
2792
+ pattern: "{given} {kin}"
2793
+ given:
2794
+ female: [Nara]
2795
+ male: [Taro]
2796
+ kin: [Mori]
2797
+ ---
2798
+
2799
+ The reed tongue is spoken beside the marsh's waterways. Its speakers use a
2800
+ given name followed by a family name, and carry those names between river
2801
+ settlements with their trade.
2802
+ ```
2803
+
2804
+ In a package named `reedflats`, the Riverfolk index record carries the Reedfolk
2805
+ profile and `data.language` as a published Address object:
2806
+
2807
+ ```json
2808
+ { "address": "reedflats-note-skill-reedtongue", "anchor": null, "anchorKind": null }
2809
+ ```
2810
+
2811
+ Adding `language: skill-rivertongue` to Riverfolk's `data` selects that language
2812
+ instead while retaining the inherited profile, provided the named language
2813
+ note exists. Stating an `appearanceProfile` there replaces the inherited
2814
+ profile while leaving the inherited tongue in place.
2815
+
2718
2816
  ##### Culture in the index
2719
2817
 
2720
2818
  `data.culture` in the index is the note's **resolved** culture, which is its
@@ -3518,8 +3616,8 @@ See [a being's subtype describes the subject](#a-beings-subtype-describes-the-su
3518
3616
  | `appearance.eye_color` | a word | Eye colour — see [a being's look](#a-beings-look) |
3519
3617
  | `appearance.hair_color` | a word | Hair colour — see [a being's look](#a-beings-look) |
3520
3618
  | `appearance.skin_color` | a word | Skin colour — see [a being's look](#a-beings-look) |
3521
- | `appearance.complexion` | `word \| word[]` | The skin's condition, one word or several — see [a being's look](#a-beings-look) |
3522
- | `appearance.extra_features` | `string[]` | Anything else a stranger would notice, in free text |
3619
+ | `appearance.complexion` | `word[]`, required with `appearance` | The skin's condition, a list of words — see [a being's look](#a-beings-look) |
3620
+ | `appearance.extra_features` | `string[]` | The visible physical marks no word covers — see [a being's look](#a-beings-look) |
3523
3621
 
3524
3622
  Author `data.height` as a string of decimal metres (`1.91m` or `1.91 m`) or
3525
3623
  whole feet with optional inches (`6'`, `6'3"`, or `6' 3"`). Inches must be
@@ -3550,15 +3648,45 @@ vocabulary**, so every package describes a face in the same terms and a
3550
3648
  misspelt or invented word is caught. `data.gender`, `data.frame`,
3551
3649
  `data.appearance.eye_color`, `data.appearance.hair_color` and
3552
3650
  `data.appearance.skin_color` each take one word from their own list.
3553
- `data.appearance.complexion` takes one word or a list of them, since a face
3554
- carries more than one condition at once; a single word means a list of one.
3555
3651
  Any other value — a word from another field's list, a variant spelling such as
3556
3652
  `gray` for `grey`, or an empty string — is an error at the value, naming the
3557
3653
  words the field takes. Leave a field out when the fact is unrecorded.
3558
3654
 
3559
- `data.appearance.extra_features` is not closed. It is a list of free-text
3560
- marks — a scar and where it came from, a missing finger, a limp — for what no
3561
- word in the vocabulary covers.
3655
+ **`data.appearance.complexion` is a list, and a being that states an appearance
3656
+ must state it.** A face carries more than one condition at once, so the field
3657
+ is always a list of complexion words: `[weathered, freckled]`, `[clear]` for
3658
+ skin with nothing remarkable about it, or `[]` where no condition is
3659
+ established. A single word written outside a list, such as `complexion: clear`,
3660
+ is an error that asks for the list, and so is an `appearance` map with no
3661
+ `complexion` or a `complexion` of `null`.
3662
+
3663
+ **`data.appearance.extra_features` lists the visible physical marks no word
3664
+ covers** — a scar and where it is, a missing finger, a tattoo, a limp. Each
3665
+ entry is a short plain description of one mark. The closed fields above carry
3666
+ everything else, so an entry is an error at that entry when it:
3667
+
3668
+ - states a hair, eye or skin colour or a skin condition, which belong in
3669
+ `hair_color`, `eye_color`, `skin_color` and `complexion` — `auburn hair`,
3670
+ `red eyes`, `milky white eyes`, `brown and black hair`, `pale skin`,
3671
+ `weathered complexion`, `freckled`;
3672
+ - names gear, clothing or a weapon — `a mail shirt kept oiled`,
3673
+ `carries a heavy battleaxe`;
3674
+ - describes a voice or speech — `speaks with a lisp`;
3675
+ - describes behaviour, manner or temperament — `fidgets constantly`,
3676
+ `an infectious grin`, `hyperactive`;
3677
+ - gives an impression of looks or presence — `startlingly beautiful`,
3678
+ `a commanding presence`, and `a beautiful tattoo of a crane` too, mark or not;
3679
+ - states handedness, sterility or an apparent age — `left handed`, `sterile`,
3680
+ `a boyish face that makes him look younger`;
3681
+ - carries any punctuation, apart from a hyphen or an apostrophe between two
3682
+ letters. `ink-stained fingers` and `a lion's mane` are marks; `Scar: left
3683
+ cheek` and `a scar on the cheek, from a duel` are not.
3684
+
3685
+ A mark that names the skin it sits on is still a mark: `a burn scar on the left
3686
+ forearm` and `a brand scarred into the skin above the heart` are features. A
3687
+ tattoo, scar or brand may show gear or a gesture, as in `a tattoo of a knight's
3688
+ helmet`, and stands; the voice, impression, handedness, sterility and age rules
3689
+ hold even for a mark.
3562
3690
 
3563
3691
  ```yaml
3564
3692
  data:
@@ -3571,6 +3699,7 @@ data:
3571
3699
  complexion: [weathered, freckled]
3572
3700
  extra_features:
3573
3701
  - a burn scar across the back of the left hand
3702
+ - ink-stained fingers
3574
3703
  ```
3575
3704
 
3576
3705
  The words, and what each means:
@@ -3737,8 +3866,9 @@ missing key, a missing `female` or `male` range, a height that does not read,
3737
3866
  a range whose `min` is above its `max`, and a `height` key that is not a
3738
3867
  gender.
3739
3868
 
3740
- `package-build npc new` refuses a culture that states no profile, naming the
3741
- culture note.
3869
+ `package-build npc new` refuses a culture with no resolved profile, naming the
3870
+ culture note. A culture may obtain the complete profile from its
3871
+ [parents](#culture-parents-and-inherited-fields).
3742
3872
 
3743
3873
  #### Name lists
3744
3874
 
@@ -5006,13 +5136,18 @@ data:
5006
5136
  The saga lists under **In song and story** on `place-ironfells`, which holds the
5007
5137
  sack, on `place-east` and on `lore-fords`.
5008
5138
 
5009
- Calendar fields are written only on a `calendar` note, and `form`, `subjects` and `language`
5010
- only on a `literature` note; written on any other lore subType, each is an error at its own key.
5139
+ Calendar fields are written only on a `calendar` note, `form` and `subjects`
5140
+ only on a `literature` note, `language` only on `literature` and `culture`, and
5141
+ `parents` only on `culture`. Written on any other lore subtype, each is an
5142
+ error at its own key.
5011
5143
  `culture` may be written on any lore subType. A culture note is itself the culture, so it
5012
5144
  leaves the field out or names itself, by its shortcode or by any longer form of its Address;
5013
5145
  naming any other note is an error at the value. A `lore` note that states no culture has
5014
- none, since a `lore` note has no `data.parents` to inherit through; a culture note reads as
5015
- its own Address in the index. See [Culture in the index](#culture-in-the-index).
5146
+ none; a culture note reads as its own Address in the index, including when it
5147
+ names parent cultures. Culture parents supply language and appearance fields,
5148
+ not a different cultural identity. See
5149
+ [Culture parents](#culture-parents-and-inherited-fields) and
5150
+ [Culture in the index](#culture-in-the-index).
5016
5151
 
5017
5152
  | `data` property | Values | Description |
5018
5153
  | ------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -5046,6 +5181,7 @@ its own Address in the index. See [Culture in the index](#culture-in-the-index).
5046
5181
  | `culture` | `Address` | The people this lore belongs to — for a `literature` note, the people whose work it is — naming a lore note with `subType: culture`; not on a `culture` note |
5047
5182
  | `form` | `string` | `literature` only: the kind of work in its people's own terms — `epic`, `saga`, `praise-song`, `elegy`. Free text |
5048
5183
  | `subjects` | `Address[]` | `literature` only: the beings, places, gods and other notes the work concerns, and any event, as `note#id` |
5184
+ | `parents` | `Address[]` | `culture` only: the larger cultures this one belongs to; each target is a culture lore note — see [culture parents](#culture-parents-and-inherited-fields) |
5049
5185
  | `language` | `Address` | `literature` and `culture` only: the tongue a work is composed in, or a culture's people speak, naming a skill note with `subType: language`; `package-build npc new` draws a culture's names from that language's [name lists](#name-lists) |
5050
5186
  | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
5051
5187
  | `appearanceProfile` | `{ frame, eye_color, hair_color, skin_color, complexion, height }` | `culture` only: what its people look like, for `package-build npc new` — see [appearance profiles](#appearance-profiles) |
@@ -53,33 +53,33 @@ These fields are accepted by every note type. A field's value can still be irrel
53
53
 
54
54
  **Subtypes:** `npc`, `character`, `creature`.
55
55
 
56
- | Field | Shape | Meaning |
57
- | -------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
58
- | `data.icon` | an Address | The document's profile art — an `icon` address, resolved into `img`. |
59
- | `data.tokenIcon` | an Address | What a token on the canvas wears — an `icon` address; unset, it follows `icon`. |
60
- | `data.templatePriority` | number | Template priority; unset means the note is not a template. |
61
- | `data.archetypes` | list of strings | Archetypal behaviours the being fits. |
62
- | `data.occupation` | string | What the being does for a living. |
63
- | `data.lore` | list of Addresses | Lore concerning this being — the law it lives under, the customs it is subject to, the traditions it was raised in. |
64
- | `data.culture` | an Address | The being's primary culture, as a culture lore note. |
65
- | `data.homes` | list of Addresses | Places the being calls home. |
66
- | `data.affiliations` | a standing map keyed by Address | Bodies the being belongs to, keyed by Address, each entry holding the standing it holds there — `rank`, required, a level on that body's own ladder, and `office`, optional, a post that body names. |
67
- | `data.socialTies` | a map keyed by Address | Defining ties directed from this being to others: `patron` (Supports the subject from greater power, wealth, or status.); `friend` (Supports the subject out of goodwill.); `dependent` (Relies on the subject's support or protection.); `acquaintance` (Knows the subject without a strong disposition.); `rival` (Opposes the subject without implacable hostility.); `nemesis` (Opposes the subject personally and implacably.) |
68
- | `data.gender` | string | One of `female`, `male`, `nonbinary`, `none` or `other`. `none` says the being has no gender; an absent field says its gender is unrecorded. See [a being's look](format-details.md#a-beings-look) for what each word means. |
69
- | `data.species` | an Address | The being's species, as a lore note. |
70
- | `data.born` | string | When the being was born — a date, or `unknown` where the birth is unrecorded. Absent, the being was never born. |
71
- | `data.calendar` | an Address | Calendar that displays this being's dates. |
72
- | `data.died` | string | When the being died — a date, or `unknown` where the death is unrecorded. Absent, the being is alive. |
73
- | `data.age` | string | Age in years, stated only to override what `born` says — `34`, or `~34` for an estimate. Unstated beside a dated `born` it is computed; unstated beside an unknown or absent `born` the age is unknown. |
74
- | `data.ageYears` | number | Written by the compiler beside an `age` estimate — the `~` stripped, the magnitude alone. A note never authors this. |
75
- | `data.height` | metres or feet and inches | Height in metres or feet and inches. |
76
- | `data.weight` | kilograms or pounds | Body weight in kilograms or pounds. |
77
- | `data.frame` | string | Relative frame — one of `scant`, `light`, `medium`, `heavy` or `massive`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
78
- | `data.appearance.eye_color` | string | Eye colour: one of `amber`, `black`, `blue`, `brown`, `dark_brown`, `grey`, `green`, `hazel` or `violet`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
79
- | `data.appearance.hair_color` | string | Hair colour: one of `auburn`, `black`, `blonde`, `brown`, `chestnut`, `dark_blonde`, `dark_brown`, `grey`, `greying_black`, `greying_brown`, `red`, `silver`, `strawberry_blond` or `white`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
80
- | `data.appearance.skin_color` | string | Skin colour: one of `pale`, `fair`, `light`, `medium`, `olive`, `tawny`, `golden`, `tanned`, `swarthy`, `brown`, `dark`, `dark_brown` or `ebony`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
81
- | `data.appearance.complexion` | string or list | The skin's condition: `ashen`, `blotchy`, `chapped`, `clear`, `freckled`, `jaundiced`, `leathery`, `oily`, `pasty`, `pimpled`, `pockmarked`, `rough`, `ruddy`, `sallow`, `scabrous`, `scarred`, `smooth`, `sun_kissed`, `sunburnt`, `translucent`, `vitiligo`, `wan`, `weathered` or `wrinkled`, one word or a list of several, because a face carries more than one at once. A single word means a list of one. See [a being's look](format-details.md#a-beings-look) for what each word means. |
82
- | `data.appearance.extra_features` | list of strings | Anything else a stranger would notice, in free text. |
56
+ | Field | Shape | Meaning |
57
+ | -------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
+ | `data.icon` | an Address | The document's profile art — an `icon` address, resolved into `img`. |
59
+ | `data.tokenIcon` | an Address | What a token on the canvas wears — an `icon` address; unset, it follows `icon`. |
60
+ | `data.templatePriority` | number | Template priority; unset means the note is not a template. |
61
+ | `data.archetypes` | list of strings | Archetypal behaviours the being fits. |
62
+ | `data.occupation` | string | What the being does for a living. |
63
+ | `data.lore` | list of Addresses | Lore concerning this being — the law it lives under, the customs it is subject to, the traditions it was raised in. |
64
+ | `data.culture` | an Address | The being's primary culture, as a culture lore note. |
65
+ | `data.homes` | list of Addresses | Places the being calls home. |
66
+ | `data.affiliations` | a standing map keyed by Address | Bodies the being belongs to, keyed by Address, each entry holding the standing it holds there — `rank`, required, a level on that body's own ladder, and `office`, optional, a post that body names. |
67
+ | `data.socialTies` | a map keyed by Address | Defining ties directed from this being to others: `patron` (Supports the subject from greater power, wealth, or status.); `friend` (Supports the subject out of goodwill.); `dependent` (Relies on the subject's support or protection.); `acquaintance` (Knows the subject without a strong disposition.); `rival` (Opposes the subject without implacable hostility.); `nemesis` (Opposes the subject personally and implacably.) |
68
+ | `data.gender` | string | One of `female`, `male`, `nonbinary`, `none` or `other`. `none` says the being has no gender; an absent field says its gender is unrecorded. See [a being's look](format-details.md#a-beings-look) for what each word means. |
69
+ | `data.species` | an Address | The being's species, as a lore note. |
70
+ | `data.born` | string | When the being was born — a date, or `unknown` where the birth is unrecorded. Absent, the being was never born. |
71
+ | `data.calendar` | an Address | Calendar that displays this being's dates. |
72
+ | `data.died` | string | When the being died — a date, or `unknown` where the death is unrecorded. Absent, the being is alive. |
73
+ | `data.age` | string | Age in years, stated only to override what `born` says — `34`, or `~34` for an estimate. Unstated beside a dated `born` it is computed; unstated beside an unknown or absent `born` the age is unknown. |
74
+ | `data.ageYears` | number | Written by the compiler beside an `age` estimate — the `~` stripped, the magnitude alone. A note never authors this. |
75
+ | `data.height` | metres or feet and inches | Height in metres or feet and inches. |
76
+ | `data.weight` | kilograms or pounds | Body weight in kilograms or pounds. |
77
+ | `data.frame` | string | Relative frame — one of `scant`, `light`, `medium`, `heavy` or `massive`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
78
+ | `data.appearance.eye_color` | string | Eye colour: one of `amber`, `black`, `blue`, `brown`, `dark_brown`, `grey`, `green`, `hazel` or `violet`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
79
+ | `data.appearance.hair_color` | string | Hair colour: one of `auburn`, `black`, `blonde`, `brown`, `chestnut`, `dark_blonde`, `dark_brown`, `grey`, `greying_black`, `greying_brown`, `red`, `silver`, `strawberry_blond` or `white`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
80
+ | `data.appearance.skin_color` | string | Skin colour: one of `pale`, `fair`, `light`, `medium`, `olive`, `tawny`, `golden`, `tanned`, `swarthy`, `brown`, `dark`, `dark_brown` or `ebony`. See [a being's look](format-details.md#a-beings-look) for what each word means. |
81
+ | `data.appearance.complexion` | a list of words, such as `[weathered]` or `[]` | The skin's condition, as a list of `ashen`, `blotchy`, `chapped`, `clear`, `freckled`, `jaundiced`, `leathery`, `oily`, `pasty`, `pimpled`, `pockmarked`, `rough`, `ruddy`, `sallow`, `scabrous`, `scarred`, `smooth`, `sun_kissed`, `sunburnt`, `translucent`, `vitiligo`, `wan`, `weathered` or `wrinkled`: `[weathered, freckled]`, `[clear]` for unremarked skin, or `[]` where no condition is established. Required once a being states an appearance; a single word outside a list is an error. See [a being's look](format-details.md#a-beings-look) for what each word means. |
82
+ | `data.appearance.extra_features` | list of strings | The visible physical marks no word covers, each a short plain description of one: `a burn scar on the left forearm`, `a missing finger`. An entry stating a hair, eye or skin colour or a skin condition, gear or clothing, a voice, behaviour or manner, an impression, handedness, sterility or an apparent age is an error, as is any punctuation but a hyphen or apostrophe inside a word. See [a being's look](format-details.md#a-beings-look). |
83
83
 
84
84
  **Inner keys** — a key a field's value does not declare is an error at its own line and column.
85
85
 
@@ -533,6 +533,7 @@ Lore records in-world knowledge. A `culture` describes a people; a `custom` desc
533
533
  | `data.namedDays` | list of `{ name, abbreviation?, day }` | Names assigned to particular days of the year. |
534
534
  | `data.eras` | list of `{ shortcode, name, marker?, abbreviation?, proclaimedBy?, start, label? }` | The year-counts kept in this calendar. A marker names one era and uses these months. |
535
535
  | `data.formats` | map of named Calendaria format strings | Named patterns for reading and writing this calendar's dates. |
536
+ | `data.parents` | list of Addresses | Culture only: parent culture lore notes, searched in listed depth-first order for omitted language and appearance profile fields. |
536
537
  | `data.culture` | an Address | The people this lore belongs to — for a work of literature, the people whose work it is — as a culture lore note. A culture note is its own culture, and states the field only as itself. |
537
538
  | `data.form` | string | The kind of work in its people's own terms — an epic, a saga, a praise-song, an elegy. Free text. |
538
539
  | `data.subjects` | list of Addresses | The beings, places, gods and other notes the work concerns, and each event it concerns, named as `place-ironfells#sack`. |
@@ -127,7 +127,7 @@ of two ways, and the difference is who resolved it: a content page carries
127
127
  `data.banner`, which the build resolved from the asset Address the note
128
128
  declared into the address its asset host serves; a hand-authored page carries
129
129
  a top-level `banner:`, which the theme resolves against `params.cdnBaseURL`
130
- (see [`params.cdnBaseURL`](../configuration.md#what-a-consumer-must-supply)).
130
+ (see [`params.cdnBaseURL`](../configuration.md#the-generated-hugo-configuration)).
131
131
 
132
132
  **Resolution order.**
133
133
 
@@ -303,7 +303,7 @@ fails this theme's own check until the slot table gives it a maximum.
303
303
  wrapping. On narrow screens, figures remain in document order without text
304
304
  wrapping. An image's label is alternative text, carried on the image itself
305
305
  for a reader who cannot see it, and is not drawn beneath the picture. See
306
- [figures and numbered references](../authoring/links-and-markup.md#figures-and-numbered-references)
306
+ [captions and numbered references](../authoring/links-and-markup.md#captions-and-numbered-references)
307
307
  for how an author writes `size`, `float` and a role.
308
308
 
309
309
  ## A numbered figure
@@ -479,8 +479,9 @@ The home page declares no `infoboxes:`, `related:`, `date:` or `tags:`, so
479
479
  those pieces of a page stay silent.
480
480
 
481
481
  **No `type` at all** — a site whose home page is a set of entry points into
482
- everything it publishes — renders the featured grid documented under
483
- [`params.home`](../configuration.md#what-a-consumer-must-supply).
482
+ everything it publishes — renders the featured grid from `params.home`.
483
+ A package-build configuration supplies those values through
484
+ [`site.hugo`](../configuration.md#site).
484
485
 
485
486
  ### A section landing
486
487