@heroiclands/package-build 22.9.2 → 22.11.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 (97) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CONTENT.md +54 -0
  3. package/bin/content-build.mjs +3 -1
  4. package/docs/api.md +11 -6
  5. package/docs/commands.md +1 -1
  6. package/docs/content-format.md +411 -201
  7. package/engine/actor-compiler.mjs +20 -7
  8. package/engine/address-fields.mjs +426 -0
  9. package/engine/address-values.mjs +80 -0
  10. package/engine/address.mjs +769 -0
  11. package/engine/art-fields.mjs +112 -97
  12. package/engine/art-slots.mjs +81 -0
  13. package/engine/base-compiler.mjs +61 -7
  14. package/engine/bundle-notes.mjs +5 -2
  15. package/engine/bundles.mjs +17 -10
  16. package/engine/content-address.mjs +23 -186
  17. package/engine/content-embeds.mjs +18 -4
  18. package/engine/content-format-check.mjs +50 -22
  19. package/engine/content-index.mjs +85 -43
  20. package/engine/content-links.mjs +105 -104
  21. package/engine/content-lint.mjs +11 -1
  22. package/engine/content-tables.mjs +9 -1
  23. package/engine/field-spec.mjs +21 -4
  24. package/engine/folder-notes.mjs +44 -15
  25. package/engine/foundry-entries.mjs +3 -0
  26. package/engine/frontmatter-lint.mjs +150 -47
  27. package/engine/frontmatter.mjs +55 -10
  28. package/engine/generate.mjs +7 -1
  29. package/engine/helpers.mjs +37 -9
  30. package/engine/holdings.mjs +41 -12
  31. package/engine/index-records.mjs +2 -0
  32. package/engine/infobox-render.mjs +2 -1
  33. package/engine/infobox.mjs +26 -6
  34. package/engine/item-compiler.mjs +5 -0
  35. package/engine/item-docs.mjs +7 -44
  36. package/engine/map-dot.mjs +2 -0
  37. package/engine/map-notes.mjs +7 -2
  38. package/engine/map-places.mjs +42 -10
  39. package/engine/metadata-index.mjs +60 -5
  40. package/engine/note-addresses.mjs +325 -0
  41. package/engine/note-vocabulary.mjs +140 -28
  42. package/engine/pdf-build.mjs +1 -1
  43. package/engine/pdf-toc.mjs +17 -2
  44. package/engine/place-relations.mjs +160 -50
  45. package/engine/populations.mjs +17 -9
  46. package/engine/reference-targets.mjs +54 -0
  47. package/engine/scenes.mjs +5 -2
  48. package/engine/shortcode-references.mjs +68 -0
  49. package/engine/site-build.mjs +39 -15
  50. package/engine/site-index.mjs +57 -75
  51. package/engine/sql-tables.mjs +43 -9
  52. package/engine/system-block.mjs +21 -0
  53. package/engine/system-vocabulary.mjs +46 -0
  54. package/engine/web-wikilinks.mjs +11 -9
  55. package/engine/wikilink-syntax.mjs +21 -0
  56. package/engine/wikilinks.mjs +92 -306
  57. package/hm3/actors.mjs +8 -24
  58. package/hm3/item-fields.mjs +15 -5
  59. package/package.json +1 -1
  60. package/sohl/infobox.mjs +111 -17
  61. package/sohl/item-fields.mjs +131 -41
  62. package/types/engine/address-fields.d.mts +95 -0
  63. package/types/engine/address-values.d.mts +34 -0
  64. package/types/engine/address.d.mts +452 -0
  65. package/types/engine/art-fields.d.mts +59 -53
  66. package/types/engine/art-slots.d.mts +40 -0
  67. package/types/engine/base-compiler.d.mts +24 -2
  68. package/types/engine/bundle-notes.d.mts +2 -2
  69. package/types/engine/content-address.d.mts +3 -139
  70. package/types/engine/content-embeds.d.mts +1 -1
  71. package/types/engine/content-format-check.d.mts +25 -0
  72. package/types/engine/content-index.d.mts +2 -2
  73. package/types/engine/content-links.d.mts +6 -5
  74. package/types/engine/field-spec.d.mts +46 -9
  75. package/types/engine/folder-notes.d.mts +11 -10
  76. package/types/engine/frontmatter-lint.d.mts +7 -97
  77. package/types/engine/frontmatter.d.mts +16 -1
  78. package/types/engine/helpers.d.mts +2 -2
  79. package/types/engine/infobox.d.mts +6 -0
  80. package/types/engine/item-docs.d.mts +1 -35
  81. package/types/engine/map-notes.d.mts +2 -2
  82. package/types/engine/note-addresses.d.mts +41 -0
  83. package/types/engine/note-vocabulary.d.mts +20 -3
  84. package/types/engine/place-relations.d.mts +12 -1
  85. package/types/engine/populations.d.mts +17 -2
  86. package/types/engine/reference-targets.d.mts +9 -0
  87. package/types/engine/shortcode-references.d.mts +14 -0
  88. package/types/engine/site-build.d.mts +3 -3
  89. package/types/engine/site-index.d.mts +9 -30
  90. package/types/engine/sql-tables.d.mts +2 -2
  91. package/types/engine/system-block.d.mts +17 -0
  92. package/types/engine/system-vocabulary.d.mts +16 -0
  93. package/types/engine/wikilink-syntax.d.mts +7 -0
  94. package/types/engine/wikilinks.d.mts +6 -109
  95. package/types/hm3/item-fields.d.mts +5 -0
  96. package/types/sohl/infobox.d.mts +6 -5
  97. package/types/sohl/item-fields.d.mts +8 -0
@@ -94,6 +94,13 @@ column regardless.
94
94
  6. **Order is the toolchain's.** A medium renders boxes, sections and rows in
95
95
  the order given.
96
96
 
97
+ **Embedded models link to documentation in an infobox.** The model Address
98
+ continues to identify the native Item used for compilation. Its displayed link
99
+ uses that same package, type and Shortcode under `none`, which names the
100
+ corresponding documentation journal. A model with no published documentation
101
+ keeps its known name as plain text; the infobox does not substitute an Actor or
102
+ Item link.
103
+
97
104
  #### What the note box holds
98
105
 
99
106
  **The note box's fields are the type's own `data:` vocabulary, in its declared
@@ -446,17 +453,11 @@ it _there_, and a system that disagrees is not in error. A weapon weighs what
446
453
  This is the same rule as `hm3.type` overriding a derived document type, applied
447
454
  to fields: derive from the shared source, and let the system state the exception.
448
455
 
449
- **A shared source and the key a system block still carries are two
450
- declarations.** The mapping tables name the shared source — `data.species` — and
451
- the corpus writes the same fact inside the block it has always written it in —
452
- `hm3.species`. Those are two positions for one field, and both are read while
453
- the corpus moves, with the block winning. A field says so by naming each: its
454
- shared source, and the legacy in-block key it is being swept off. Reading it
455
- from the legacy position is _reported_, so the sweep has a progress signal, and
456
- the note compiles to the identical document either way — the same read-both,
457
- report-one shape every other retirement in this format uses. As one
458
- declaration the two would let a field name only one of them, and a row this
459
- table states would be reachable only by a note that had already moved.
456
+ **A shared source and an in-block key are separate declarations.**
457
+ `data.gender` is the shared source for HM3 gender; `hm3.gender` is its
458
+ in-block spelling, and `hm3.system.gender` is the native destination.
459
+ The native destination wins, followed by the in-block value and the shared
460
+ source. Field declarations name both source and destination explicitly.
460
461
 
461
462
  **A field whose spelling means something else at the note level has no shared
462
463
  source.** The fallback assumes the two vocabularies agree about what a name
@@ -465,9 +466,8 @@ publishes under, while an `affiliation` item's `system.title` is the style of
465
466
  address an office carries. Where they diverge, the field declares what the
466
467
  top-level key means instead, and the top level stops being read for it — leaving
467
468
  `<system>.system.<field>` and the legacy in-block position, which describe the
468
- document rather than the note. `title` is the one field this applies to; `subType`
469
- is the other declared item field spelled like a note-level key, and there the two
470
- levels mean the same thing by design.
469
+ document rather than the note. Native species is independent of the shared lore Address in `data.species`.
470
+ `subType` has the same meaning at both levels.
471
471
 
472
472
  **Some of a system's fields are runtime state, and a note may not write any of
473
473
  them.** A data model declares everything a document stores, and part of that is
@@ -485,13 +485,30 @@ the field rather than a list of names, so it covers every such field a system
485
485
  adds later. The per-type tables below never list one — they are the vocabulary
486
486
  an author writes — and each type that has any names them under its table.
487
487
 
488
- **A `WikiLink` becomes a shortcode where the target field expects one.** SoHL
489
- stores cross-references as shortcode strings, which is what the `Code` suffix
490
- marks: `data.assocSkill` is a link to a skill note, and `system.assocSkillCode`
491
- holds that note's shortcode. The resolution happens at build time, and a link
492
- that resolves to nothing is an error naming the note — never a blank field. Where
493
- a target field has no `Code` suffix, the link is stored as the reference the
494
- field expects.
488
+ **An `Address` becomes a `Shortcode` where the target field expects one.** SoHL
489
+ resolves a stored cross-reference among the items one actor holds, and packages do
490
+ not exist there, so the field holds a `Shortcode`. Four `affiliation` fields are
491
+ authored as Addresses and stored that way: `data.seat` becomes `system.seat`,
492
+ `data.parents` becomes `system.parents`, `data.domains` becomes `system.domain`,
493
+ and each authored key of `data.relations` becomes a key of `system.relations`. The
494
+ reduction happens at build time, and two things are errors naming the note: an
495
+ Address whose type the field does not accept, and two Addresses that reduce to one
496
+ shortcode, which the field cannot hold both of.
497
+
498
+ **A SoHL skill Address becomes a native Item UUID.** An affiliation's
499
+ `sohl.system.commonSkills` names skills in its own package or a declared
500
+ dependency. Its `system.commonSkills` contains the exact published Item UUIDs,
501
+ including each foreign module's own pack name. Native skill lookup uses those
502
+ UUIDs; the journal, website and book use documentation links for presentation.
503
+
504
+ **A `Code` suffix marks a different thing.** `system.assocSkillCode`,
505
+ `system.assocAffiliationCode` and `system.parentSkillCode` hold a `Shortcode` at
506
+ both ends — each is authored as one, under `<system>.system`, and emitted verbatim
507
+ — so no Address is reduced into them and none of them has a shared source. A value
508
+ carrying a separator is refused there by the shortcode charset rather than read as
509
+ an Address. `data.assocSkill`, `data.assocAffiliation` and `data.parentSkill` are
510
+ properties of the **page**: each is an Address naming the note an infobox row
511
+ links to, and none reaches a compiled document.
495
512
 
496
513
  ### The note vocabulary, and how it maps
497
514
 
@@ -575,7 +592,7 @@ destination, so the resolved value is simply dropped — the note validates, the
575
592
  tree compiles, and the value goes nowhere.
576
593
 
577
594
  **What a note writes is an address; what a document carries is a path.**
578
- `data.icon` is a `WikiLink` defaulting to type `icon`, so a being writes
595
+ `data.icon` is an `Address` defaulting to type `icon`, so a being writes
579
596
  `icon: anvil` and never a file name. The compiler resolves that address to the
580
597
  file the owning package ships and writes the resulting path into `img`. The two
581
598
  sides of this row are therefore in two different currencies, which is why the
@@ -599,17 +616,17 @@ check.
599
616
  #### The four art slots
600
617
 
601
618
  A note declares the art its document needs, and every other image in it is
602
- inline. All four slots are ordinary `WikiLink` fields declaring a default type,
619
+ inline. All four slots are ordinary `Address` fields declaring a default type,
603
620
  exactly as `seat` declares `place` — a bare shortcode takes its type from the
604
621
  declaration, and qualification climbs the same short-form ladder every other
605
622
  link uses.
606
623
 
607
- | field | default type | resolves into | on |
608
- | ----------- | ------------ | ---------------------------- | ----------------------------------------- |
609
- | `icon` | `icon` | `document.img` | every Actor/Item type, and embedded items |
610
- | `tokenIcon` | `icon` | `prototypeToken.texture.src` | Actor types |
611
- | `bgImage` | `image` | `background.src` | map types |
612
- | `banner` | `image` | the site hero image | any note; not a Foundry field |
624
+ | field | default type | accepts | resolves into | on |
625
+ | ----------- | ------------ | --------------- | ---------------------------- | ----------------------------------------- |
626
+ | `icon` | `icon` | `icon`, `image` | `document.img` | every Actor/Item type, and embedded items |
627
+ | `tokenIcon` | `icon` | `icon`, `image` | `prototypeToken.texture.src` | Actor types |
628
+ | `bgImage` | `image` | `icon`, `image` | `background.src` | map types |
629
+ | `banner` | `image` | `icon`, `image` | the site hero image | any note; not a Foundry field |
613
630
 
614
631
  All four are authored under `data:`:
615
632
 
@@ -619,16 +636,37 @@ data:
619
636
  banner: packagebuild-none-image-skillbnr
620
637
  ```
621
638
 
639
+ **Every slot accepts a second type beyond the one it defaults to**, and the two
640
+ defaults pair with the same accepted set: `icon` and `tokenIcon` default to
641
+ `icon`, `bgImage` and `banner` default to `image`, and all four accept `icon`
642
+ **and** `image` regardless of which one they default to. A bare shortcode still
643
+ takes the slot's default — `icon: anvil` above names an `icon`, not an `image`
644
+ — and it is a **qualified** value, one that states its own type, that the
645
+ accepted set governs. `sohl-kethira-basic` writes an `icon` slot this way
646
+ twenty times, once per faith tradition, because a deity's profile art is a full
647
+ illustration rather than a game icon:
648
+
649
+ ```yaml
650
+ data:
651
+ icon: image-kpagrik
652
+ ```
653
+
654
+ That value is accepted because `image` is in the slot's accepted set, not
655
+ because it matches the default — `icon` still defaults to `icon`. `audio` is
656
+ refused at all four slots: a sound is not art, and a value naming one — `icon:
657
+ audio-thunder`, say — is an error naming the set the slot accepts, never a
658
+ silent fall back to the note's default art.
659
+
622
660
  **Two of them are legal on _every_ note**, whatever its type. `data:` is a
623
661
  closed container and the per-type vocabularies below are the only lists there
624
662
  are, so a key legal everywhere is stated once here rather than repeated in
625
663
  twenty-five tables where the one that was mistyped would be the one nobody
626
664
  noticed:
627
665
 
628
- | shared `data` property | Values | Description |
629
- | ---------------------- | ---------- | -------------------------------------------------------------------- |
630
- | `icon` | `WikiLink` | The document's profile art — an `icon` address, resolved into `img`. |
631
- | `banner` | `WikiLink` | The page's hero image — an `image` address. |
666
+ | shared `data` property | Values | Description |
667
+ | ---------------------- | --------- | -------------------------------------------------------------------- |
668
+ | `icon` | `Address` | The document's profile art — an `icon` address, resolved into `img`. |
669
+ | `banner` | `Address` | The page's hero image — an `image` address. |
632
670
 
633
671
  `icon` is legal everywhere because most types compile into a document that
634
672
  carries one; where a type's passes emit none, the lint says so and the value is
@@ -660,7 +698,7 @@ Actor types (`being`, `vehicle`) therefore add one row:
660
698
  An actor's two pieces of art are two different questions. `data.icon` is the
661
699
  profile art — what a directory listing shows beside the name and what the sheet
662
700
  header carries. `data.tokenIcon` is what a token on the canvas wears. Both are
663
- wikilinks and both resolve the same way; only the destination differs. An Item
701
+ addresses and both resolve the same way; only the destination differs. An Item
664
702
  has one piece of art, so this row applies to actor types alone.
665
703
 
666
704
  **A `data:` source is still read at the top level, for now.** `data:` did
@@ -798,7 +836,7 @@ Foundry destination: searching a built `packs-json` tree for it turns up
798
836
  nothing. It reaches the generated page and the book's section plates, and
799
837
  nothing else.
800
838
 
801
- It is a `WikiLink` all the same, defaulting to type `image`, so a section note
839
+ It is an `Address` all the same, defaulting to type `image`, so a section note
802
840
  writes `banner: skillbnr` and a note borrowing another package's plate writes
803
841
  `banner: packagebuild-none-image-skillbnr`. The resolved record carries the
804
842
  owning package, and each surface joins the suffix onto its own root — the site
@@ -848,7 +886,7 @@ documents in different packs and different folders:
848
886
  ```yaml
849
887
  sohl:
850
888
  pack: characteristics
851
- packFolder: miscgear
889
+ packFolder: none-folder-miscgear
852
890
  ```
853
891
 
854
892
  **`pack: none` compiles the note into no document.** The note is walked,
@@ -1199,10 +1237,30 @@ is where the question _which notes carry no `kbcat`?_ is answered.
1199
1237
  `sql` fence is the ordinary case of _Content tables_ below, and the same
1200
1238
  free-form value decides the headings there.
1201
1239
 
1202
- ### WikiLinks
1240
+ ### Addresses
1203
1241
 
1204
- Many fields in the tables below take a `WikiLink`, and a link is written
1205
- `[[target]]` or `[[target|label]]`. The target is an **address**.
1242
+ Three types name one thing from another in a content tree, and they are not
1243
+ lengths of one another:
1244
+
1245
+ | type | written | where it is met |
1246
+ | ----------- | -------------------------------------------- | ----------------------------------------------------------- |
1247
+ | `Address` | `<package>-<system>-<note_type>-<shortcode>` | a frontmatter field, a `model:`, an art slot, a link target |
1248
+ | `Shortcode` | `<shortcode>` | a note's own identity, and a key a runtime resolves by |
1249
+ | `Wikilink` | `[[<Address>[#<anchor>]\|<text>]]` | body prose |
1250
+
1251
+ An **`Address`** is the tuple below, and it is what every field typed `Address`
1252
+ in the tables that follow holds. It is equally what a `model:` names, what an art
1253
+ slot points at and what a wikilink resolves through — so an address is the thing
1254
+ being named, and the brackets around one in prose are a separate matter. Three
1255
+ rules govern it, and the first three subsections below state them in turn: the
1256
+ tuple is read by position, any suffix of it may be written, and each omitted
1257
+ segment is supplied by a default the position declares.
1258
+
1259
+ A **`Shortcode`** is one segment, and nothing expands it — see _A shortcode is
1260
+ one segment, and nothing expands it_ below.
1261
+
1262
+ A **`Wikilink`** is the prose form, which contains an Address — see _Wikilinks_
1263
+ below.
1206
1264
 
1207
1265
  #### The canonical address
1208
1266
 
@@ -1237,6 +1295,26 @@ the canonical address, so naming another package means naming its system too.
1237
1295
  `kethira-place-tashal` is three segments, which reads as system `kethira`, and
1238
1296
  fails.
1239
1297
 
1298
+ #### Authored references and generated references
1299
+
1300
+ Authored notes may use any permitted suffix. Builds leave those source bytes
1301
+ unchanged. Generated content indexes, SQL records, site frontmatter and generated
1302
+ Wikilinks contain full `package-system-type-shortcode` Addresses. For example,
1303
+ `data.parents: [north]` in the `thalorna` package emits
1304
+ `data.parents: [thalorna-none-place-north]`.
1305
+
1306
+ SQL queries compare Address-valued fields to full Addresses. A note's `shortcode`
1307
+ is a distinct identity component, so it is not equal to an Address stored in
1308
+ `data.parents`, `data.domains` or a governance rank's `lore`. Use the target's
1309
+ complete identity for that comparison, including its package, system and type.
1310
+ A stub has no published page address; its note identity still contains those
1311
+ components and its Shortcode.
1312
+
1313
+ The toolchain holds parsed Address properties as complete tuples internally.
1314
+ Address-keyed relation maps hold typed targets and standing values. Only generated
1315
+ output serializes these properties to Address strings. Runtime Shortcodes,
1316
+ Foundry UUIDs, file paths and URLs keep their own representations.
1317
+
1240
1318
  #### An omitted segment defaults from where the link is written
1241
1319
 
1242
1320
  It is **not** a wildcard and resolution is not a search. Every short form expands
@@ -1264,15 +1342,11 @@ Only a type whose own document carries a system is redirected this way. A
1264
1342
  documents are core ones already at `none`, so `macro-autoattack` names the Macro
1265
1343
  and `docmacro-autoattack` its journal — two live addresses.
1266
1344
 
1267
- **An address is lowercase throughout**. Every segment — package,
1268
- system, type and shortcode alike — is `^[a-z0-9]+$`, so a capital anywhere in an
1269
- address is an error naming the lowercase form.
1270
-
1271
- The shortcode is the one that would otherwise be case-sensitive and routinely
1272
- mixed (`Clb`, `LtShoe`, `HsTunic`) while the address built from it was lowercased
1273
- wholesale, so the authored name and its address disagreed. Two names differing
1274
- only in case are two names nobody can tell apart, and they collapsed onto one
1275
- address, one `_id` and one URL with nothing to report it.
1345
+ **A complete Address is lowercase throughout.** Every segment — package,
1346
+ system, type and shortcode alike — contains only lowercase ASCII letters and
1347
+ digits (`^[a-z0-9]+$`). Declared Address fields reject uppercase segments.
1348
+ A model reference accepts mixed-case Shortcodes and resolves their lowercase
1349
+ Address identity; this does not change a native embedded item's Shortcode.
1276
1350
 
1277
1351
  **Parsing is positional counting from the right, and nothing else.** Every
1278
1352
  segment is lowercase alphanumeric — shortcodes, **types** and **subTypes** are
@@ -1312,44 +1386,42 @@ before the vault migrated do not silently die. A slash is _unconditionally_ an
1312
1386
  address separator — pipe or no pipe — so an unknown type before one is an error
1313
1387
  rather than something to guess at.
1314
1388
 
1315
- #### Every link is an address, and every link carries a label
1316
-
1317
- There is one namespace, and the pipe is required:
1318
-
1319
- | written | resolved as | displays |
1320
- | -------------------- | ----------- | -------------------------- |
1321
- | `[[WikiLink\|]]` | an address | the target note's own name |
1322
- | `[[WikiLink\|Text]]` | an address | `Text` |
1323
- | `[[Name]]` | nothing | a finding |
1324
-
1325
- **A link written without a label addresses nothing**, and the correction
1326
- is always the same: write `[[type-shortcode|Text]]`.
1327
-
1328
- The bare form does not name an **alias** — a note's own display name, or one of
1329
- the names it listed in `aliases:` — looked up within the citing note's type. It
1330
- was measured before it was retired, and the namespace was empty in practice:
1331
- across 8,305 wikilinks in three content trees, **not one** bare link resolved to
1332
- a note. What the index behind it did do was fold every note's `name.full` into
1333
- itself, so two notes of one type could not share a display name — a rules page
1334
- and a user guide page both called "Gear" were a build failure whose every
1335
- available fix moved a published URL.
1336
-
1337
- The top-level `aliases:` that fed it is **retired** and refused. The nested
1338
- `name.aliases:` is **not**: it is reserved for a use that does not exist yet, so
1339
- it is permitted and read by nothing — no index, no resolver, no lint rule, no
1340
- derived address. A note carrying one behaves exactly as one without it.
1341
-
1342
- Requiring the label is also what makes positional parsing safe. Note names
1343
- contain hyphens — `Grukar-ahk` is a name, not a `Grukar` of type `ahk` — so a
1344
- target that does not parse as an address is reported as one that does not, rather
1345
- than split at an arbitrary place or quietly looked up somewhere else.
1346
-
1347
- The **empty** label is not a way of writing no label. It says _address this
1348
- target, and show whatever it calls itself_ — so a note renamed later takes its new
1349
- name at every citation with no link edited. `[[x|]]` is labelled; `[[x]]` is not.
1389
+ #### A shortcode is one segment, and nothing expands it
1390
+
1391
+ A **`Shortcode`** is a single segment of the same charset an address segment is
1392
+ held to, and it is a type of its own rather than the shortest `Address`. It
1393
+ names a thing's identity, or it is the key something resolves by where the other
1394
+ three segments have nothing to say:
1395
+
1396
+ | where a `Shortcode` is written | what it names |
1397
+ | -------------------------------------------- | ----------------------------------------------------------------------------------- |
1398
+ | a note's own `shortcode:` | the note's identity — the name every address of it is built from |
1399
+ | a custom embedded entry's `system.shortcode` | that entry's identity within the actor that holds it |
1400
+ | an emitted `system.relations` key | the other body this one has a standing towards |
1401
+ | a mystery's `skillAptitudes` keys | the skill an aptitude weighs, where the key is not a `subType:` selector |
1402
+ | a `Code`-suffixed system field | what the resolved address is stored as — see _The note vocabulary, and how it maps_ |
1403
+
1404
+ **The test is whether the position can take the full four-tuple.** An `Address`
1405
+ accepts it by definition, and the positions above cannot: a `system.relations`
1406
+ key is read at runtime as `relations[shortcode]`, among the items one actor
1407
+ carries, where packages do not exist. So nothing defaults a `Shortcode` out to
1408
+ four segments, and a value persisted there as an address names no standing at
1409
+ all.
1350
1410
 
1351
- The link part may still be an anchor: `[[#slug|Text]]` addresses a section of the
1352
- page it is written on. It is the label that is required, not a target.
1411
+ **The two halves of `relations` are two types.** The key an author writes is an
1412
+ `Address` — a body in another package is named in full, and one in this package
1413
+ by its shortcode alone — and the key the compiled Item carries is the
1414
+ `Shortcode` that address names. The builder performs the reduction, exactly as
1415
+ it does for a `Code`-suffixed field, and two authored keys reducing to one
1416
+ emitted key is an error naming both addresses: a shortcode is unique within one
1417
+ package's address space, so two packages may each declare one, and the compiled
1418
+ field holds one of the two.
1419
+
1420
+ **A bare shortcode in an `Address` field is not a `Shortcode`.** It is an
1421
+ Address written at its shortest, with the package, the system and the type all
1422
+ supplied by the position — the same value as the full tuple, spelled shorter.
1423
+ Which of the two a field holds is the field's own declaration, stated in the
1424
+ `Values` column of the tables below and nowhere else.
1353
1425
 
1354
1426
  #### Every address resolves, and every build says so the same way
1355
1427
 
@@ -1388,9 +1460,9 @@ precisely because an author meets whichever build ran first. Three resolvers rea
1388
1460
  one authored link; they must not describe the same mistake in three ways, and
1389
1461
  they must never disagree about whether it is a mistake at all.
1390
1462
 
1391
- #### In frontmatter, a link is a bare address
1463
+ #### In frontmatter, the address is written bare
1392
1464
 
1393
- A `WikiLink` **field** takes the address with no brackets:
1465
+ An **`Address` field** takes the address with no brackets:
1394
1466
 
1395
1467
  ```yaml
1396
1468
  data:
@@ -1399,22 +1471,35 @@ data:
1399
1471
  seat: tashal
1400
1472
  ```
1401
1473
 
1402
- not `[[hexhodai]]`. The field is declared as a `WikiLink`, so the schema already
1403
- knows the value is an address and reads it as one; brackets would be punctuation
1404
- the reader has to strip before it can do anything.
1474
+ not `[[hexhodai]]`. The field is declared as an `Address`, so the schema knows
1475
+ what the value is and reads it as one; brackets are a wikilink's punctuation,
1476
+ which the reader would have to strip before it could do anything.
1405
1477
 
1406
1478
  A frontmatter value is parsed by the address grammar above, so a single-segment
1407
- value such as `hexhodai` is a _shortcode_. Frontmatter is structure rather than
1479
+ value such as `hexhodai` names the target by its shortcode alone, with every
1480
+ other segment supplied by the position. Frontmatter is structure rather than
1408
1481
  prose: a field value is a reference something else will compile against, and it
1409
1482
  should say exactly what it points at.
1410
1483
 
1411
- **The field supplies the type.** Every `WikiLink` field declares the note type it
1412
- targets — `seat` a `place`, `parents` an `affiliation`, `stations` a `lore` — so
1484
+ **A field can supply the type.** A field naming one kind of note declares its
1485
+ default — `seat` a `place`, `parents` an `affiliation`, `stations` a `lore` — so
1413
1486
  the type segment defaults from the declaration and a bare shortcode is the
1414
1487
  ordinary case, not an abbreviation of one. That is why the examples above read
1415
1488
  `tashal` rather than `place-tashal`: the shorter form carries the same
1416
1489
  information, because the field already said what kind of thing it points at.
1417
1490
 
1491
+ The frontmatter lint checks scalar Addresses, entries in Address lists, and
1492
+ Address map keys at the entry's own location. Segments contain lowercase ASCII
1493
+ letters and digits; wikilink brackets, labels, anchors, and spaces do not belong
1494
+ in these fields. Optional null values and empty lists or maps express no
1495
+ reference. Grammar and accepted types are checked independently of whether a
1496
+ target exists.
1497
+
1498
+ An affiliation's `economy` accepts only `affiliation` and `lore` Addresses.
1499
+ Each entry states its type: `affiliation-merchants` or `lore-tanvrcrncy`.
1500
+ There is no default type; a bare shortcode is an error.
1501
+ Bundle `contents` likewise requires a type for each document.
1502
+
1418
1503
  **The declaration constrains the type; it does not shorten the address.** A field
1419
1504
  value may be written at any length, and every length is equally correct so long
1420
1505
  as the type it names is the one the field declares. All four of these are valid
@@ -1423,14 +1508,32 @@ in a `seat` field:
1423
1508
  ```yaml
1424
1509
  seat: tashal
1425
1510
  seat: place-tashal
1426
- seat: kethira-place-tashal
1511
+ seat: none-place-tashal
1427
1512
  seat: kethira-none-place-tashal
1428
1513
  ```
1429
1514
 
1515
+ Each is a **suffix** of the canonical address, which is why the third one names
1516
+ the system and the fourth cannot omit it — see [Shorter forms](#shorter-forms).
1517
+
1430
1518
  An `affiliation-` prefix there is an **error naming the field and both types** —
1431
1519
  never a silent widening of what the field accepts. Where a field permits more
1432
- than one type, a bare shortcode must resolve to exactly one of them, and an
1433
- ambiguity is an error naming the candidates rather than a first match.
1520
+ than one type with no default among them, a bare shortcode is refused
1521
+ outright — an error naming the field and the types it accepts, never a
1522
+ resolution across them.
1523
+
1524
+ **A position's default and its accepted set are two different declarations,
1525
+ and most positions are not ambiguous at all.** The paragraph above is about a
1526
+ position that permits more than one type with no default among them; `seat` is
1527
+ the plainer case, permitting exactly one. Between those sits a third shape,
1528
+ and it is the common one: a position that nominates one of its accepted types
1529
+ as the default while accepting others besides. There a bare shortcode is never
1530
+ a candidate to weigh — it takes the default outright, every time — and the
1531
+ accepted set is what a value that states its own type is checked against
1532
+ instead: naming any member of the set is as valid as naming the default
1533
+ itself, and naming a type outside it is an error against the set, never
1534
+ against the default alone. [The four art slots](#the-four-art-slots) are
1535
+ declared exactly this way — each one accepts a second type beyond the one a
1536
+ bare shortcode takes.
1434
1537
 
1435
1538
  **Only the type segment defaults.** Package and system are not the field's to
1436
1539
  supply — it has no opinion about which package holds the target — so reaching
@@ -1452,6 +1555,67 @@ Brackets belong in prose, where a link sits inside a sentence and needs marking
1452
1555
  off from the words around it. A frontmatter value has nothing to be marked off
1453
1556
  from.
1454
1557
 
1558
+ ### Wikilinks
1559
+
1560
+ A **`Wikilink`** is how body prose names something:
1561
+
1562
+ ```text
1563
+ [[<Address>[#<anchor>]|<text>]]
1564
+ ```
1565
+
1566
+ The brackets, the `#<anchor>` and the `|<text>` label are the Wikilink's own.
1567
+ What sits inside them is an `Address` like any other — read by the same grammar,
1568
+ written at any of its lengths, and taking each omitted segment from where the
1569
+ link is written, which for body prose is this package and system `none`. So
1570
+ nothing about resolution belongs to the bracketed form: a wikilink and a
1571
+ frontmatter field ask the same question of the same value, and the findings above
1572
+ are the answers either of them gets.
1573
+
1574
+ The anchor is the Wikilink's, not the Address's. It names a section of the target
1575
+ by the slug that section's heading declares — `[[place-tashal#market|the
1576
+ market]]` — and one naming no section there is the `unknown-anchor` finding.
1577
+ Written alone, `[[#slug|Text]]` addresses a section of the page the link sits on,
1578
+ which is the one form carrying no address at all.
1579
+
1580
+ #### Every wikilink carries a label
1581
+
1582
+ There is one namespace, and the pipe is required:
1583
+
1584
+ | written | resolved as | displays |
1585
+ | ------------------- | ----------- | -------------------------- |
1586
+ | `[[Address\|]]` | an address | the target note's own name |
1587
+ | `[[Address\|Text]]` | an address | `Text` |
1588
+ | `[[Name]]` | nothing | a finding |
1589
+
1590
+ **A link written without a label addresses nothing**, and the correction
1591
+ is always the same: write `[[type-shortcode|Text]]`.
1592
+
1593
+ The bare form does not name an **alias** — a note's own display name, or one of
1594
+ the names it listed in `aliases:` — looked up within the citing note's type. It
1595
+ was measured before it was retired, and the namespace was empty in practice:
1596
+ across 8,305 wikilinks in three content trees, **not one** bare link resolved to
1597
+ a note. What the index behind it did do was fold every note's `name.full` into
1598
+ itself, so two notes of one type could not share a display name — a rules page
1599
+ and a user guide page both called "Gear" were a build failure whose every
1600
+ available fix moved a published URL.
1601
+
1602
+ The top-level `aliases:` that fed it is **retired** and refused. The nested
1603
+ `name.aliases:` is **not**: it is reserved for a use that does not exist yet, so
1604
+ it is permitted and read by nothing — no index, no resolver, no lint rule, no
1605
+ derived address. A note carrying one behaves exactly as one without it.
1606
+
1607
+ Requiring the label is also what makes positional parsing safe. Note names
1608
+ contain hyphens — `Grukar-ahk` is a name, not a `Grukar` of type `ahk` — so a
1609
+ target that does not parse as an address is reported as one that does not, rather
1610
+ than split at an arbitrary place or quietly looked up somewhere else.
1611
+
1612
+ The **empty** label is not a way of writing no label. It says _address this
1613
+ target, and show whatever it calls itself_ — so a note renamed later takes its new
1614
+ name at every citation with no link edited. `[[x|]]` is labelled; `[[x]]` is not.
1615
+
1616
+ It is the label that is required, not a target: the anchor-only form above
1617
+ carries its pipe like every other link.
1618
+
1455
1619
  ### Assets are types
1456
1620
 
1457
1621
  `icon`, `image` and `audio` are types in the type vocabulary, and an address
@@ -1668,12 +1832,12 @@ type the way a field declaration does — `image`. The short-form ladder, the
1668
1832
  package and system defaults, the lowercase rule, the ambiguity reporting and the
1669
1833
  findings vocabulary all apply unchanged.
1670
1834
 
1671
- | written | means |
1672
- | ------------------------------ | ---------------------------------------- |
1673
- | `![[anvil\|]]` | decorative — the common case |
1674
- | `![[anvil\|An anvil]]` | alt text where it carries meaning |
1675
- | `![[anvil]]` | unlabelled, and a finding like any other |
1676
- | `![[sohl-image-anvil\|Anvil]]` | qualified, reaching another package |
1835
+ | written | means |
1836
+ | ----------------------------------- | ---------------------------------------- |
1837
+ | `![[anvil\|]]` | decorative — the common case |
1838
+ | `![[anvil\|An anvil]]` | alt text where it carries meaning |
1839
+ | `![[anvil]]` | unlabelled, and a finding like any other |
1840
+ | `![[sohl-none-image-anvil\|Anvil]]` | qualified, reaching another package |
1677
1841
 
1678
1842
  The parser distinguishes a missing label from an empty one, so _deliberately
1679
1843
  decorative_ and _not written_ differ without an exemption.
@@ -1806,7 +1970,7 @@ so adding a type means answering the question rather than inheriting a default.
1806
1970
 
1807
1971
  #### A stub is still referenceable as data
1808
1972
 
1809
- `borders.to` and `routes.to` name a place by shortcode, and those are **index
1973
+ `borders.to` and `routes.to` name a place by `Address`, and those are **index
1810
1974
  relations rather than page links**: a border with a stub on the far side is a
1811
1975
  valid statement about the world and resolves. So does `parents` for the
1812
1976
  containment tree and `domains` for tenure — a stub appears in its region's
@@ -2291,16 +2455,16 @@ Generates a living (or undead, or spirit) being.
2291
2455
 
2292
2456
  | `data` property | Values | Description |
2293
2457
  | --------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |
2294
- | `tokenIcon` | `WikiLink` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2458
+ | `tokenIcon` | `Address` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2295
2459
  | `templatePriority` | `number` | Template priority, _null_ = not a template |
2296
2460
  | `archetypes` | `Archetype[]` | What sort of character this is. **Always an array** — `[]` where none apply; `null` is an error. |
2297
2461
  | `occupation` | `string` | Name of the character's occupation |
2298
- | `stations` | `WikiLink[]` | Name of the stations the character belongs to |
2299
- | `lore` | `WikiLink[]` | Lore concerning this being — the people it is of, the standing it holds, the law it lives under |
2300
- | `homes` | `WikiLink[]` | Place the being calls home |
2301
- | `affiliations` | `WikiLink[]` | Affilliations (e.g., arcane/divine traditions, polities, etc) |
2462
+ | `stations` | `Address[]` | Name of the stations the character belongs to |
2463
+ | `lore` | `Address[]` | Lore concerning this being — the people it is of, the standing it holds, the law it lives under |
2464
+ | `homes` | `Address[]` | Place the being calls home |
2465
+ | `affiliations` | `Address[]` | Affilliations (e.g., arcane/divine traditions, polities, etc) |
2302
2466
  | `gender` | `male \| female \| other` | Gender of the character |
2303
- | `species` | `WikiLink` | Being's species (lore) |
2467
+ | `species` | `Address` | Being's species (lore) |
2304
2468
  | `born` | `YYYY/MM/DD \| unknown` | When the being was born; absent, it was never born |
2305
2469
  | `died` | `YYYY/MM/DD \| unknown` | When the being died; absent, it is alive |
2306
2470
  | `age` | `34 \| ~34` | Age in years, stated only to override what `born` says; `~` marks an estimate |
@@ -2539,10 +2703,15 @@ A SoHL "being" document will be created, as will an "HM3" document.
2539
2703
 
2540
2704
  | shared source | → sohl | → hm3 |
2541
2705
  | ----------------- | ------ | ------------------- |
2542
- | `data.species` | NA | `system.species` |
2706
+ | `data.species` | NA | NA |
2543
2707
  | `data.gender` | NA | `system.gender` |
2544
2708
  | `data.occupation` | NA | `system.occupation` |
2545
2709
 
2710
+ `data.species` is a lore Address describing the being's species. HM3's
2711
+ `hm3.system.species` is independent native text; it does not inherit from
2712
+ `data.species`. The in-block `hm3.species` spelling is also accepted, with
2713
+ `hm3.system.species` taking precedence.
2714
+
2546
2715
  ### type: homepage
2547
2716
 
2548
2717
  The package's front page, published at `https://www.heroiclands.org/<package>/`.
@@ -2570,10 +2739,10 @@ a card block — the homepage is a page with a body, rendered as one.
2570
2739
 
2571
2740
  Represents a conveyance able to hold goods and people moving from one place to another.
2572
2741
 
2573
- | `data` property | Values | Description |
2574
- | ------------------ | ---------- | ------------------------------------------------------------------------ |
2575
- | `tokenIcon` | `WikiLink` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2576
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
2742
+ | `data` property | Values | Description |
2743
+ | ------------------ | --------- | ------------------------------------------------------------------------ |
2744
+ | `tokenIcon` | `Address` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2745
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
2577
2746
 
2578
2747
  If `sohl` is present, this becomes a `vehicle` actor.
2579
2748
 
@@ -2650,12 +2819,12 @@ two is the commonest way to get a non-monarchy wrong.
2650
2819
  **Rank**
2651
2820
  Definition of a level within the organization (e.g., Priest, Layperson, Member, Gang Leader, Master, etc.)
2652
2821
 
2653
- | Property | Values | Description |
2654
- | ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------ |
2655
- | `level` | number | The ranking within the affiliation, increasing values starting with 1, 0 indicates intentional exclusion (expulsion/excommunication) |
2656
- | `title` | string | Title associated with the Rank |
2657
- | `description` | string | Description of the Rank |
2658
- | `lore` | `WikiLink` | The standing this rank _is_ — a `lore` note of subType `law`, shared with every other body that confers the same thing |
2822
+ | Property | Values | Description |
2823
+ | ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
2824
+ | `level` | number | The ranking within the affiliation, increasing values starting with 1, 0 indicates intentional exclusion (expulsion/excommunication) |
2825
+ | `title` | string | Title associated with the Rank |
2826
+ | `description` | string | Description of the Rank |
2827
+ | `lore` | `Address` | The standing this rank _is_ — a `lore` note of subType `law`, shared with every other body that confers the same thing |
2659
2828
 
2660
2829
  **A rank's `lore` is shared; its `title` is not.** A Normen kingdom calls it
2661
2830
  `Thrall` and a Vylarian province calls it `Slave`, and they mean one standing:
@@ -2671,24 +2840,23 @@ rank names the standing, and the standing says.
2671
2840
 
2672
2841
  **Standing**: aligned, unaligned, rival, nemesis
2673
2842
 
2674
- | `data` property | Values | Description |
2675
- | -------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------- |
2676
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
2677
- | `demonym` | `string` | What a member of this affiliation is called (a Vylarian) |
2678
- | `epithet` | `string` | The by-name it is known by — a god's, an order's, a company's |
2679
- | `symbol` | `string` | Its emblem in words: a feather atop a golden scale, a chisel carving a star |
2680
- | `governance.model` | `GovernanceModel` | Type of government structure, if applicable |
2681
- | `governance.summary` | `string` | summary of the governance situation |
2682
- | `governance.ranks` | `Rank[]` | The ranks available to members of the affiliation |
2683
- | `governance.offices` | `Map<name, description>` | Official offices in the affiliation |
2684
- | `commonSkills` | `WikiLink[]` | Common skills among members (languages, etc.) |
2685
- | `seat` | `WikiLink` | Where the affiliation's authority sits |
2686
- | `domains` | `WikiLink[]` | Places over which this affiliation holds sway |
2687
- | `population` | `number` | Number of people in the affiliation (precision 2 significant digits). |
2688
- | `economy` | `WikiLink[]` | Any wikilink referring to the economic activity of the affiliation (produced goods, currency systems, etc) |
2689
- | `lore` | `WikiLink[]` | Lore concerning it — the peoples it draws on, the god a faith venerates, its law, its calendar |
2690
- | `parents` | `WikiLink[]` | Affiliations that this affiliation is subordinate to |
2691
- | `relations` | `Map<WikiLink, Standing>` | Relations with other affiliations |
2843
+ | `data` property | Values | Description |
2844
+ | -------------------- | ------------------------ | ---------------------------------------------------------------------------------------------- |
2845
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
2846
+ | `demonym` | `string` | What a member of this affiliation is called (a Vylarian) |
2847
+ | `epithet` | `string` | The by-name it is known by — a god's, an order's, a company's |
2848
+ | `symbol` | `string` | Its emblem in words: a feather atop a golden scale, a chisel carving a star |
2849
+ | `governance.model` | `GovernanceModel` | Type of government structure, if applicable |
2850
+ | `governance.summary` | `string` | summary of the governance situation |
2851
+ | `governance.ranks` | `Rank[]` | The ranks available to members of the affiliation |
2852
+ | `governance.offices` | `Map<name, description>` | Official offices in the affiliation |
2853
+ | `seat` | `Address` | Where the affiliation's authority sits |
2854
+ | `domains` | `Address[]` | Places over which this affiliation holds sway |
2855
+ | `population` | `number` | Number of people in the affiliation (precision 2 significant digits). |
2856
+ | `economy` | `Address[]` | Economic life: `affiliation` or `lore` Addresses, with an explicit type and no default |
2857
+ | `lore` | `Address[]` | Lore concerning it — the peoples it draws on, the god a faith venerates, its law, its calendar |
2858
+ | `parents` | `Address[]` | Affiliations that this affiliation is subordinate to |
2859
+ | `relations` | `Map<Address, Standing>` | Standing with other affiliations, keyed by the other body's Address |
2692
2860
 
2693
2861
  **A faith tradition is not its god.** An `affiliation` of subType
2694
2862
  `faithtradition` is a _religion_ — a practice, with an ordained priesthood, a
@@ -2763,6 +2931,27 @@ If `sohl` is present, this becomes an `affiliation` item.
2763
2931
  | `data.parents` | `system.parents` | NA |
2764
2932
  | `data.relations` | `system.relations` | NA |
2765
2933
 
2934
+ An affiliation writes SoHL common skills under `sohl.system.commonSkills` as a
2935
+ list of skill Addresses. An abbreviated value names a skill in the authoring
2936
+ package; a skill in the SoHL system package uses its complete Address:
2937
+
2938
+ ```yaml
2939
+ sohl:
2940
+ system:
2941
+ commonSkills:
2942
+ - herb
2943
+ - sohl-sohl-skill-law
2944
+ ```
2945
+
2946
+ The compiled SoHL Item stores the exact published Item UUIDs in
2947
+ `system.commonSkills`. An empty or absent list emits `[]`. A foreign target must
2948
+ come from a declared `relationships.systems` or `relationships.requires` content
2949
+ index. Each UUID uses the module and pack named by that target's published index;
2950
+ a missing skill Item or Item UUID is an error at the authored Address. The
2951
+ content index retains complete Addresses. Journal, web and book infoboxes link
2952
+ to a skill's published documentation journal when one exists and otherwise show
2953
+ its name as plain text.
2954
+
2766
2955
  `governance` reaches no system field. Ranks and offices are the note's and the
2767
2956
  web page's — SoHL's affiliation item has nowhere to put them, and inventing a
2768
2957
  mapping for a field no schema declares is the drift these tables exist to catch.
@@ -3004,27 +3193,29 @@ if a `hm3` property is present, an HM3 item of type "miscgear" will be created.
3004
3193
 
3005
3194
  **SkillAptitude**: either a single skill
3006
3195
 
3007
- | `data` property | Values | Description |
3008
- | ------------------ | --------------------------------------- | -------------------------------------------------------------- |
3009
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
3010
- | `assocSkill` | `WikiLink` | Associated skill |
3011
- | `assocAffiliation` | `WikiLink` | Associated affiliation |
3012
- | `skillAptitudes` | `WikiLink` or `subType:<skill-subtype>` | Bonuses/penalties to skills (or types of skills) |
3013
- | `level` | `number` | Magnitude of the mystery |
3014
- | `charges.value` | `number` | Current number of charges available, _null_ = charges not used |
3015
- | `charges.max` | `number` | Maximum number of charges, _null_ = no maximum |
3196
+ | `data` property | Values | Description |
3197
+ | ------------------ | ---------------------------------------------------------------------------------- | -------------------------------------------------------------- |
3198
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
3199
+ | `assocSkill` | `Address` | Associated skill |
3200
+ | `assocAffiliation` | `Address` | Associated affiliation |
3201
+ | `skillAptitudes` | `Map<Shortcode, number>`, the key a skill's shortcode or `subType:<skill-subtype>` | Bonuses and penalties, per skill or per kind of skill |
3202
+ | `level` | `number` | Magnitude of the mystery |
3203
+ | `charges.value` | `number` | Current number of charges available, _null_ = charges not used |
3204
+ | `charges.max` | `number` | Maximum number of charges, _null_ = no maximum |
3016
3205
 
3017
3206
  if a `sohl` property is present, a SoHL item of type "mystery" will be created.
3018
3207
 
3019
- | shared source | → sohl | → hm3 |
3020
- | ----------------------- | ----------------------------- | ----- |
3021
- | `subType` | `system.subType` | NA |
3022
- | `data.assocSkill` | `system.assocSkillCode` | NA |
3023
- | `data.assocAffiliation` | `system.assocAffiliationCode` | NA |
3024
- | `data.skillAptitudes` | `system.skillAptitudes` | NA |
3025
- | `data.level` | `system.levelBase` | NA |
3026
- | `data.charges.value` | `system.charges.value` | NA |
3027
- | `data.charges.max` | `system.charges.max` | NA |
3208
+ `system.assocSkillCode` and `system.assocAffiliationCode` are authored under
3209
+ `sohl.system` as shortcodes and have no shared source; `data.assocSkill` and
3210
+ `data.assocAffiliation` name the same two notes as Addresses, for the page.
3211
+
3212
+ | shared source | → sohl | → hm3 |
3213
+ | --------------------- | ----------------------- | ----- |
3214
+ | `subType` | `system.subType` | NA |
3215
+ | `data.skillAptitudes` | `system.skillAptitudes` | NA |
3216
+ | `data.level` | `system.levelBase` | NA |
3217
+ | `data.charges.value` | `system.charges.value` | NA |
3218
+ | `data.charges.max` | `system.charges.max` | NA |
3028
3219
 
3029
3220
  ### type: mysticalability
3030
3221
 
@@ -3041,29 +3232,31 @@ if a `sohl` property is present, a SoHL item of type "mystery" will be created.
3041
3232
  - alchemy: The preparation of substances imbued with mystical potency.
3042
3233
  - divination: The practice of obtaining hidden knowledge or foreknowledge by mystical means.
3043
3234
 
3044
- | `data` property | Values | Description |
3045
- | ------------------ | ---------- | -------------------------------------------------------------- |
3046
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
3047
- | `assocSkill` | `WikiLink` | Associated skill |
3048
- | `assocAffiliation` | `WikiLink` | Associated affiliation |
3049
- | `masteryLevel` | `number` | Mastery Level |
3050
- | `level` | `number` | Magnitude of the mystery |
3051
- | `charges.value` | `number` | Current number of charges available, _null_ = charges not used |
3052
- | `charges.max` | `number` | Maximum number of charges, _null_ = no maximum |
3235
+ | `data` property | Values | Description |
3236
+ | ------------------ | --------- | -------------------------------------------------------------- |
3237
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
3238
+ | `assocSkill` | `Address` | Associated skill |
3239
+ | `assocAffiliation` | `Address` | Associated affiliation |
3240
+ | `masteryLevel` | `number` | Mastery Level |
3241
+ | `level` | `number` | Magnitude of the mystery |
3242
+ | `charges.value` | `number` | Current number of charges available, _null_ = charges not used |
3243
+ | `charges.max` | `number` | Maximum number of charges, _null_ = no maximum |
3053
3244
 
3054
3245
  if a `sohl` property is present, a SoHL item of type "mysticalability" will be created.
3055
3246
 
3056
3247
  If an `hm3` property is present, an HM3 item is created, and `hm3.type` states which — `psionic`, `spell` or `invocation`. It is **authored, not derived from `subType`**: the ten mystical-ability subtypes do not partition onto HM3's three documents (a `spiritrite`, an `alchemy` and a `divination` each answer to none of them), so a derivation would be a guess with a plausible shape. A note that omits it is an error naming the note.
3057
3248
 
3058
- | shared source | → sohl | → hm3 |
3059
- | ----------------------- | ----------------------------- | ------------- |
3060
- | `subType` | `system.subType` | **see above** |
3061
- | `data.assocSkill` | `system.assocSkillCode` | NA |
3062
- | `data.assocAffiliation` | `system.assocAffiliationCode` | NA |
3063
- | `data.masteryLevel` | `system.masteryLevelBase` | NA |
3064
- | `data.level` | `system.levelBase` | NA |
3065
- | `data.charges.value` | `system.charges.value` | NA |
3066
- | `data.charges.max` | `system.charges.max` | NA |
3249
+ `system.assocSkillCode` and `system.assocAffiliationCode` are authored under
3250
+ `sohl.system` as shortcodes and have no shared source; `data.assocSkill` and
3251
+ `data.assocAffiliation` name the same two notes as Addresses, for the page.
3252
+
3253
+ | shared source | → sohl | → hm3 |
3254
+ | -------------------- | ------------------------- | ------------- |
3255
+ | `subType` | `system.subType` | **see above** |
3256
+ | `data.masteryLevel` | `system.masteryLevelBase` | NA |
3257
+ | `data.level` | `system.levelBase` | NA |
3258
+ | `data.charges.value` | `system.charges.value` | NA |
3259
+ | `data.charges.max` | `system.charges.max` | NA |
3067
3260
 
3068
3261
  ### type: projectilegear
3069
3262
 
@@ -3117,11 +3310,11 @@ HM3 side while remaining distinct on the SoHL side.
3117
3310
  - combat
3118
3311
  - combattechnique
3119
3312
 
3120
- | `data` property | Values | Description |
3121
- | ------------------ | ---------- | ------------------------------------------ |
3122
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
3123
- | `masteryLevel` | `number` | Mastery Level |
3124
- | `parentSkill` | `WikiLink` | Parent skill this skill specializes |
3313
+ | `data` property | Values | Description |
3314
+ | ------------------ | --------- | ------------------------------------------ |
3315
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
3316
+ | `masteryLevel` | `number` | Mastery Level |
3317
+ | `parentSkill` | `Address` | Parent skill this skill specializes |
3125
3318
 
3126
3319
  if a `sohl` property is present, a SoHL item of type "skill" will be created.
3127
3320
 
@@ -3129,11 +3322,14 @@ If an `hm3` property is present, then an HM3 item of type "skill" will be create
3129
3322
 
3130
3323
  Note: `hm3.system.type` (skill types) use the values "Craft", "Physical", "Communication", "Combat", "Magic", and "Ritual". These do not cleanly map to the `subType` values. Because of this, the `hm3.system.type` value must be specified with the appropriate value when defining HM3 skills.
3131
3324
 
3325
+ `system.parentSkillCode` is authored under `sohl.system` as the shortcode of the
3326
+ skill this one specializes, and has no shared source; `data.parentSkill` names that
3327
+ skill as an Address, for the page.
3328
+
3132
3329
  | shared source | → sohl | → hm3 |
3133
3330
  | ------------------- | ------------------------- | --------------------- |
3134
3331
  | `subType` | `system.subType` | See notes above |
3135
3332
  | `data.masteryLevel` | `system.masteryLevelBase` | `system.masteryLevel` |
3136
- | `data.parentSkill` | `system.parentSkillCode` | NA |
3137
3333
 
3138
3334
  ### type: trauma
3139
3335
 
@@ -3344,7 +3540,7 @@ The `data:` fields, of which three are required:
3344
3540
 
3345
3541
  | `data` property | Values | Description |
3346
3542
  | ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
3347
- | `bgImage` | `WikiLink` | **Required.** The map art — an `image` address. Becomes the level's `background.src`, what tokens stand on |
3543
+ | `bgImage` | `Address` | **Required.** The map art — an `image` address. Becomes the level's `background.src`, what tokens stand on |
3348
3544
  | `dimensions` | `[int, int]` | **Required.** `[width, height]` in whole pixels, the art's own size |
3349
3545
  | `pxPerGrid` | `integer` | **Required.** Whole pixels per grid square; must match the art |
3350
3546
  | `navName` | `string` | Short name for Foundry's scene navigation bar |
@@ -3357,7 +3553,7 @@ The `data:` fields, of which three are required:
3357
3553
  | `tiles` | `Tile[]` | List of tiles, each naming its art in `image` |
3358
3554
  | `sounds` | `Sound[]` | List of sounds, each naming its clip in `audio` |
3359
3555
  | `regions` | `SceneRegion[]` | List of scene regions |
3360
- | `place` | `WikiLink` | The place this map depicts. Optional, because an encounter map depicts no named place — but that is the exception, and a map without one is a map of nowhere |
3556
+ | `place` | `Address` | The place this map depicts. Optional, because an encounter map depicts no named place — but that is the exception, and a map without one is a map of nowhere |
3361
3557
  | `notes` | `NoteLocation[]` | grid coordinates of note markers mapped to anchors in this document |
3362
3558
 
3363
3559
  Everything else a Scene holds is **derived**, not authored: padding, grid type,
@@ -3374,7 +3570,7 @@ compilation, and the server-side migration shim is version-gated on
3374
3570
  single Level is synthesised from `bgImage`, `overlay`, `levelName` and
3375
3571
  `backgroundColor`.
3376
3572
 
3377
- > **`bgImage` is a `WikiLink` under `data:`, as every other type's artwork is.**
3573
+ > **`bgImage` is an `Address` under `data:`, as every other type's artwork is.**
3378
3574
  > Art is not system-specific — a Scene is a core Foundry document, and a second
3379
3575
  > system would want the identical art — so the field sits under `data:` rather
3380
3576
  > than inside a system block.
@@ -3455,8 +3651,8 @@ three maps, and none of those maps needs to know it is a keep.
3455
3651
  | `data` property | Values | Description |
3456
3652
  | --------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------- |
3457
3653
  | `demonym` | `string` | What a person from this place is called — a Vylarian |
3458
- | `lore` | `WikiLink[]` | Lore concerning this place — its peoples, its law, its calendar, its history |
3459
- | `parents` | `WikiLink[]` | Enclosing places within which this place is located |
3654
+ | `lore` | `Address[]` | Lore concerning this place — its peoples, its law, its calendar, its history |
3655
+ | `parents` | `Address[]` | Enclosing places within which this place is located |
3460
3656
  | `population` | `number` | Approximate population (precision 2 significant digits) |
3461
3657
  | `market` | `number` | Market class, 1 to 6 — what trade a settlement supports |
3462
3658
  | `borders` | `{ to, bearing }[]` | Places sharing a frontier with this one, and where each lies from here |
@@ -3563,8 +3759,13 @@ data:
3563
3759
  - { to: kethramir, bearing: E, mode: land, days: 30, terrain: [dunes], leagues: 90 }
3564
3760
  ```
3565
3761
 
3566
- - `to` — required; the `shortcode` of a `place` note in this package or in a
3567
- declared dependency's index. A shortcode, not an address.
3762
+ - `to` — required; an `Address` naming the other place. Its type segment defaults
3763
+ to `place` and `place` is the whole of its accepted set, so `vylar`,
3764
+ `place-vylar` and `thalorna-none-place-vylar` name one place and a `to` naming
3765
+ anything else is an error. **A bare shortcode names a place in this package**,
3766
+ which is what the fully qualified form is for: naming a place in a declared
3767
+ dependency's index, so two packages using one shortcode never answer for each
3768
+ other.
3568
3769
  - `bearing` — required; where the neighbour or the destination lies from here.
3569
3770
  - `mode` — required on a route; how the journey is travelled.
3570
3771
  - `days` — required on a route; a marker meaning "about this, under normal
@@ -3591,6 +3792,15 @@ neighbour that states a different bearing, a different `days` or a different
3591
3792
  tell which. Every `to` must resolve to a place, every value must come from its
3592
3793
  closed set, and each finding is located at the entry that states it.
3593
3794
 
3795
+ **A relation that crosses a package boundary is written in full at both ends.**
3796
+ An omitted `<package>` segment is the package being built, in whatever note the
3797
+ value is read from — a note of this package's own, or a record a consumer fetched
3798
+ out of a dependency's index — so a short `to` names a different place in a
3799
+ different build. Only the fully qualified form names one place everywhere, which
3800
+ is why the long form is not optional for a border or a route between packages,
3801
+ and why a short `to` in a fetched record names the package being built rather
3802
+ than the package that wrote it.
3803
+
3594
3804
  | `bearing` value | where the other place lies |
3595
3805
  | --------------- | -------------------------- |
3596
3806
  | `N` | north |
@@ -3719,7 +3929,7 @@ because they were true of something else, and each removal has a home to go to.
3719
3929
 
3720
3930
  **`languages` is a fact about a polity.** A place's languages change when its
3721
3931
  ruler changes, which is what makes them the ruler's property — and
3722
- `affiliation.commonSkills` already holds them. The authored corpus agrees: of 206
3932
+ `sohl.system.commonSkills` holds them. The authored corpus agrees: of 206
3723
3933
  places carrying `languages`, 190 were settlements and 16 were regions, and not one
3724
3934
  was a site, a structure or a feature. A ruin has no language.
3725
3935
 
@@ -3749,14 +3959,14 @@ Content prepared to be played — a situation with its cast, places, and possibl
3749
3959
 
3750
3960
  | `data` property | Values | Description |
3751
3961
  | ------------------ | -------------------------------------------- | ------------------------------------------------------------------------------ |
3752
- | `parents` | `WikiLink[]` | List of parent scenarios of this scenario (campaigns, etc.) |
3753
- | `locations` | `WikiLink[]` | List of locations associated with this scenario |
3754
- | `cast` | `WikiLink[]` | individuals associated with this scenario |
3755
- | `factions` | `WikiLink[]` | Affiliations associated with this scenario |
3756
- | `follows` | `WikiLink[]` | Prerequisite scenarios that should be completed before beginning this scenario |
3962
+ | `parents` | `Address[]` | List of parent scenarios of this scenario (campaigns, etc.) |
3963
+ | `locations` | `Address[]` | List of locations associated with this scenario |
3964
+ | `cast` | `Address[]` | individuals associated with this scenario |
3965
+ | `factions` | `Address[]` | Affiliations associated with this scenario |
3966
+ | `follows` | `Address[]` | Prerequisite scenarios that should be completed before beginning this scenario |
3757
3967
  | `status` | `draft \| playtested \| published` | Playability status of this scenario |
3758
3968
  | `party.size` | `solo \| small \| standard \| large \| host` | Suggested party size (solo=1, small=2-3, standard=4-6, large=6-7, host=7+) |
3759
- | `party.archetypes` | `Archetype` | Archetypes of characters suitable for completion |
3969
+ | `party.archetypes` | `Archetype[]` | Archetypes of characters suitable for completion |
3760
3970
 
3761
3971
  ### type: doc
3762
3972
 
@@ -3859,9 +4069,9 @@ authors none takes Foundry's own `icons/svg/dice-target.svg`.
3859
4069
 
3860
4070
  A bundle of notes to be taken as a single unit — an `Adventure` in Foundry VTT.
3861
4071
 
3862
- | `data` property | Values | Description |
3863
- | --------------- | ------------ | ------------------------------------------------------ |
3864
- | `contents` | `WikiLink[]` | The documents the Adventure holds; `[]` when unstated. |
4072
+ | `data` property | Values | Description |
4073
+ | --------------- | ----------- | ------------------------------------------------------ |
4074
+ | `contents` | `Address[]` | The documents the Adventure holds; `[]` when unstated. |
3865
4075
 
3866
4076
  ```yaml
3867
4077
  ---
@@ -3944,10 +4154,10 @@ data:
3944
4154
  ---
3945
4155
  ```
3946
4156
 
3947
- | `data` property | Values | Description |
3948
- | --------------- | ------------------------------------------ | ------------------------------------------------------------------------------ |
3949
- | `parent` | `WikiLink`, or a map of them keyed by pack | The folder this one sits in — one address, or one per pack. Unset at the root. |
3950
- | `color` | `"#RRGGBB"`, a string | The folder's colour. Unset for Foundry's default. |
4157
+ | `data` property | Values | Description |
4158
+ | --------------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
4159
+ | `parent` | `Address`, or a map of them keyed by pack | The folder this one sits in — one address, or one per pack. Unset at the root. |
4160
+ | `color` | `"#RRGGBB"`, a string | The folder's colour. Unset for Foundry's default. |
3951
4161
 
3952
4162
  A folder is addressed `<package>-none-folder-<shortcode>` — **`none`**, because a
3953
4163
  `Folder` is a core Foundry document like a `JournalEntry` or a `Scene`, not a