@heroiclands/package-build 22.9.1 → 22.10.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 (100) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/CONTENT.md +33 -0
  3. package/bin/content-build.mjs +3 -1
  4. package/docs/api.md +10 -6
  5. package/docs/content-format.md +384 -200
  6. package/docs/project-setup.md +23 -0
  7. package/engine/actor-compiler.mjs +20 -7
  8. package/engine/address-fields.mjs +404 -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 +59 -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 +20 -4
  24. package/engine/folder-notes.mjs +44 -15
  25. package/engine/foundry-entries.mjs +3 -0
  26. package/engine/frontmatter-lint.mjs +144 -43
  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 +310 -0
  41. package/engine/note-vocabulary.mjs +145 -26
  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/githooks/attribution.mjs +82 -0
  58. package/githooks/commit-message.mjs +26 -0
  59. package/githooks/commit-msg +3 -35
  60. package/hm3/actors.mjs +8 -24
  61. package/hm3/item-fields.mjs +15 -5
  62. package/package.json +1 -1
  63. package/sohl/infobox.mjs +80 -17
  64. package/sohl/item-fields.mjs +114 -41
  65. package/types/engine/address-fields.d.mts +90 -0
  66. package/types/engine/address-values.d.mts +34 -0
  67. package/types/engine/address.d.mts +452 -0
  68. package/types/engine/art-fields.d.mts +59 -53
  69. package/types/engine/art-slots.d.mts +40 -0
  70. package/types/engine/base-compiler.d.mts +24 -2
  71. package/types/engine/bundle-notes.d.mts +2 -2
  72. package/types/engine/content-address.d.mts +3 -139
  73. package/types/engine/content-embeds.d.mts +1 -1
  74. package/types/engine/content-format-check.d.mts +25 -0
  75. package/types/engine/content-index.d.mts +2 -2
  76. package/types/engine/content-links.d.mts +6 -5
  77. package/types/engine/field-spec.d.mts +44 -9
  78. package/types/engine/folder-notes.d.mts +11 -10
  79. package/types/engine/frontmatter-lint.d.mts +7 -97
  80. package/types/engine/frontmatter.d.mts +16 -1
  81. package/types/engine/helpers.d.mts +2 -2
  82. package/types/engine/infobox.d.mts +6 -0
  83. package/types/engine/item-docs.d.mts +1 -35
  84. package/types/engine/map-notes.d.mts +2 -2
  85. package/types/engine/note-addresses.d.mts +41 -0
  86. package/types/engine/note-vocabulary.d.mts +20 -3
  87. package/types/engine/place-relations.d.mts +12 -1
  88. package/types/engine/populations.d.mts +17 -2
  89. package/types/engine/reference-targets.d.mts +9 -0
  90. package/types/engine/shortcode-references.d.mts +14 -0
  91. package/types/engine/site-build.d.mts +3 -3
  92. package/types/engine/site-index.d.mts +9 -30
  93. package/types/engine/sql-tables.d.mts +2 -2
  94. package/types/engine/system-block.d.mts +17 -0
  95. package/types/engine/system-vocabulary.d.mts +16 -0
  96. package/types/engine/wikilink-syntax.d.mts +7 -0
  97. package/types/engine/wikilinks.d.mts +6 -109
  98. package/types/hm3/item-fields.d.mts +5 -0
  99. package/types/sohl/infobox.d.mts +4 -5
  100. 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,24 @@ 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 `Code` suffix marks a different thing.** `system.assocSkillCode`,
499
+ `system.assocAffiliationCode` and `system.parentSkillCode` hold a `Shortcode` at
500
+ both ends — each is authored as one, under `<system>.system`, and emitted verbatim
501
+ — so no Address is reduced into them and none of them has a shared source. A value
502
+ carrying a separator is refused there by the shortcode charset rather than read as
503
+ an Address. `data.assocSkill`, `data.assocAffiliation` and `data.parentSkill` are
504
+ properties of the **page**: each is an Address naming the note an infobox row
505
+ links to, and none reaches a compiled document.
495
506
 
496
507
  ### The note vocabulary, and how it maps
497
508
 
@@ -575,7 +586,7 @@ destination, so the resolved value is simply dropped — the note validates, the
575
586
  tree compiles, and the value goes nowhere.
576
587
 
577
588
  **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
589
+ `data.icon` is an `Address` defaulting to type `icon`, so a being writes
579
590
  `icon: anvil` and never a file name. The compiler resolves that address to the
580
591
  file the owning package ships and writes the resulting path into `img`. The two
581
592
  sides of this row are therefore in two different currencies, which is why the
@@ -599,17 +610,17 @@ check.
599
610
  #### The four art slots
600
611
 
601
612
  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,
613
+ inline. All four slots are ordinary `Address` fields declaring a default type,
603
614
  exactly as `seat` declares `place` — a bare shortcode takes its type from the
604
615
  declaration, and qualification climbs the same short-form ladder every other
605
616
  link uses.
606
617
 
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 |
618
+ | field | default type | accepts | resolves into | on |
619
+ | ----------- | ------------ | --------------- | ---------------------------- | ----------------------------------------- |
620
+ | `icon` | `icon` | `icon`, `image` | `document.img` | every Actor/Item type, and embedded items |
621
+ | `tokenIcon` | `icon` | `icon`, `image` | `prototypeToken.texture.src` | Actor types |
622
+ | `bgImage` | `image` | `icon`, `image` | `background.src` | map types |
623
+ | `banner` | `image` | `icon`, `image` | the site hero image | any note; not a Foundry field |
613
624
 
614
625
  All four are authored under `data:`:
615
626
 
@@ -619,16 +630,37 @@ data:
619
630
  banner: packagebuild-none-image-skillbnr
620
631
  ```
621
632
 
633
+ **Every slot accepts a second type beyond the one it defaults to**, and the two
634
+ defaults pair with the same accepted set: `icon` and `tokenIcon` default to
635
+ `icon`, `bgImage` and `banner` default to `image`, and all four accept `icon`
636
+ **and** `image` regardless of which one they default to. A bare shortcode still
637
+ takes the slot's default — `icon: anvil` above names an `icon`, not an `image`
638
+ — and it is a **qualified** value, one that states its own type, that the
639
+ accepted set governs. `sohl-kethira-basic` writes an `icon` slot this way
640
+ twenty times, once per faith tradition, because a deity's profile art is a full
641
+ illustration rather than a game icon:
642
+
643
+ ```yaml
644
+ data:
645
+ icon: image-kpagrik
646
+ ```
647
+
648
+ That value is accepted because `image` is in the slot's accepted set, not
649
+ because it matches the default — `icon` still defaults to `icon`. `audio` is
650
+ refused at all four slots: a sound is not art, and a value naming one — `icon:
651
+ audio-thunder`, say — is an error naming the set the slot accepts, never a
652
+ silent fall back to the note's default art.
653
+
622
654
  **Two of them are legal on _every_ note**, whatever its type. `data:` is a
623
655
  closed container and the per-type vocabularies below are the only lists there
624
656
  are, so a key legal everywhere is stated once here rather than repeated in
625
657
  twenty-five tables where the one that was mistyped would be the one nobody
626
658
  noticed:
627
659
 
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. |
660
+ | shared `data` property | Values | Description |
661
+ | ---------------------- | --------- | -------------------------------------------------------------------- |
662
+ | `icon` | `Address` | The document's profile art — an `icon` address, resolved into `img`. |
663
+ | `banner` | `Address` | The page's hero image — an `image` address. |
632
664
 
633
665
  `icon` is legal everywhere because most types compile into a document that
634
666
  carries one; where a type's passes emit none, the lint says so and the value is
@@ -660,7 +692,7 @@ Actor types (`being`, `vehicle`) therefore add one row:
660
692
  An actor's two pieces of art are two different questions. `data.icon` is the
661
693
  profile art — what a directory listing shows beside the name and what the sheet
662
694
  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
695
+ addresses and both resolve the same way; only the destination differs. An Item
664
696
  has one piece of art, so this row applies to actor types alone.
665
697
 
666
698
  **A `data:` source is still read at the top level, for now.** `data:` did
@@ -798,7 +830,7 @@ Foundry destination: searching a built `packs-json` tree for it turns up
798
830
  nothing. It reaches the generated page and the book's section plates, and
799
831
  nothing else.
800
832
 
801
- It is a `WikiLink` all the same, defaulting to type `image`, so a section note
833
+ It is an `Address` all the same, defaulting to type `image`, so a section note
802
834
  writes `banner: skillbnr` and a note borrowing another package's plate writes
803
835
  `banner: packagebuild-none-image-skillbnr`. The resolved record carries the
804
836
  owning package, and each surface joins the suffix onto its own root — the site
@@ -848,7 +880,7 @@ documents in different packs and different folders:
848
880
  ```yaml
849
881
  sohl:
850
882
  pack: characteristics
851
- packFolder: miscgear
883
+ packFolder: none-folder-miscgear
852
884
  ```
853
885
 
854
886
  **`pack: none` compiles the note into no document.** The note is walked,
@@ -1199,10 +1231,30 @@ is where the question _which notes carry no `kbcat`?_ is answered.
1199
1231
  `sql` fence is the ordinary case of _Content tables_ below, and the same
1200
1232
  free-form value decides the headings there.
1201
1233
 
1202
- ### WikiLinks
1234
+ ### Addresses
1235
+
1236
+ Three types name one thing from another in a content tree, and they are not
1237
+ lengths of one another:
1238
+
1239
+ | type | written | where it is met |
1240
+ | ----------- | -------------------------------------------- | ----------------------------------------------------------- |
1241
+ | `Address` | `<package>-<system>-<note_type>-<shortcode>` | a frontmatter field, a `model:`, an art slot, a link target |
1242
+ | `Shortcode` | `<shortcode>` | a note's own identity, and a key a runtime resolves by |
1243
+ | `Wikilink` | `[[<Address>[#<anchor>]\|<text>]]` | body prose |
1203
1244
 
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**.
1245
+ An **`Address`** is the tuple below, and it is what every field typed `Address`
1246
+ in the tables that follow holds. It is equally what a `model:` names, what an art
1247
+ slot points at and what a wikilink resolves through — so an address is the thing
1248
+ being named, and the brackets around one in prose are a separate matter. Three
1249
+ rules govern it, and the first three subsections below state them in turn: the
1250
+ tuple is read by position, any suffix of it may be written, and each omitted
1251
+ segment is supplied by a default the position declares.
1252
+
1253
+ A **`Shortcode`** is one segment, and nothing expands it — see _A shortcode is
1254
+ one segment, and nothing expands it_ below.
1255
+
1256
+ A **`Wikilink`** is the prose form, which contains an Address — see _Wikilinks_
1257
+ below.
1206
1258
 
1207
1259
  #### The canonical address
1208
1260
 
@@ -1237,6 +1289,26 @@ the canonical address, so naming another package means naming its system too.
1237
1289
  `kethira-place-tashal` is three segments, which reads as system `kethira`, and
1238
1290
  fails.
1239
1291
 
1292
+ #### Authored references and generated references
1293
+
1294
+ Authored notes may use any permitted suffix. Builds leave those source bytes
1295
+ unchanged. Generated content indexes, SQL records, site frontmatter and generated
1296
+ Wikilinks contain full `package-system-type-shortcode` Addresses. For example,
1297
+ `data.parents: [north]` in the `thalorna` package emits
1298
+ `data.parents: [thalorna-none-place-north]`.
1299
+
1300
+ SQL queries compare Address-valued fields to full Addresses. A note's `shortcode`
1301
+ is a distinct identity component, so it is not equal to an Address stored in
1302
+ `data.parents`, `data.domains` or a governance rank's `lore`. Use the target's
1303
+ complete identity for that comparison, including its package, system and type.
1304
+ A stub has no published page address; its note identity still contains those
1305
+ components and its Shortcode.
1306
+
1307
+ The toolchain holds parsed Address properties as complete tuples internally.
1308
+ Address-keyed relation maps hold typed targets and standing values. Only generated
1309
+ output serializes these properties to Address strings. Runtime Shortcodes,
1310
+ Foundry UUIDs, file paths and URLs keep their own representations.
1311
+
1240
1312
  #### An omitted segment defaults from where the link is written
1241
1313
 
1242
1314
  It is **not** a wildcard and resolution is not a search. Every short form expands
@@ -1264,15 +1336,11 @@ Only a type whose own document carries a system is redirected this way. A
1264
1336
  documents are core ones already at `none`, so `macro-autoattack` names the Macro
1265
1337
  and `docmacro-autoattack` its journal — two live addresses.
1266
1338
 
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.
1339
+ **A complete Address is lowercase throughout.** Every segment — package,
1340
+ system, type and shortcode alike — contains only lowercase ASCII letters and
1341
+ digits (`^[a-z0-9]+$`). Declared Address fields reject uppercase segments.
1342
+ A model reference accepts mixed-case Shortcodes and resolves their lowercase
1343
+ Address identity; this does not change a native embedded item's Shortcode.
1276
1344
 
1277
1345
  **Parsing is positional counting from the right, and nothing else.** Every
1278
1346
  segment is lowercase alphanumeric — shortcodes, **types** and **subTypes** are
@@ -1312,44 +1380,42 @@ before the vault migrated do not silently die. A slash is _unconditionally_ an
1312
1380
  address separator — pipe or no pipe — so an unknown type before one is an error
1313
1381
  rather than something to guess at.
1314
1382
 
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.
1383
+ #### A shortcode is one segment, and nothing expands it
1384
+
1385
+ A **`Shortcode`** is a single segment of the same charset an address segment is
1386
+ held to, and it is a type of its own rather than the shortest `Address`. It
1387
+ names a thing's identity, or it is the key something resolves by where the other
1388
+ three segments have nothing to say:
1389
+
1390
+ | where a `Shortcode` is written | what it names |
1391
+ | -------------------------------------------- | ----------------------------------------------------------------------------------- |
1392
+ | a note's own `shortcode:` | the note's identity — the name every address of it is built from |
1393
+ | a custom embedded entry's `system.shortcode` | that entry's identity within the actor that holds it |
1394
+ | an emitted `system.relations` key | the other body this one has a standing towards |
1395
+ | a mystery's `skillAptitudes` keys | the skill an aptitude weighs, where the key is not a `subType:` selector |
1396
+ | a `Code`-suffixed system field | what the resolved address is stored as — see _The note vocabulary, and how it maps_ |
1397
+
1398
+ **The test is whether the position can take the full four-tuple.** An `Address`
1399
+ accepts it by definition, and the positions above cannot: a `system.relations`
1400
+ key is read at runtime as `relations[shortcode]`, among the items one actor
1401
+ carries, where packages do not exist. So nothing defaults a `Shortcode` out to
1402
+ four segments, and a value persisted there as an address names no standing at
1403
+ all.
1350
1404
 
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.
1405
+ **The two halves of `relations` are two types.** The key an author writes is an
1406
+ `Address` — a body in another package is named in full, and one in this package
1407
+ by its shortcode alone — and the key the compiled Item carries is the
1408
+ `Shortcode` that address names. The builder performs the reduction, exactly as
1409
+ it does for a `Code`-suffixed field, and two authored keys reducing to one
1410
+ emitted key is an error naming both addresses: a shortcode is unique within one
1411
+ package's address space, so two packages may each declare one, and the compiled
1412
+ field holds one of the two.
1413
+
1414
+ **A bare shortcode in an `Address` field is not a `Shortcode`.** It is an
1415
+ Address written at its shortest, with the package, the system and the type all
1416
+ supplied by the position — the same value as the full tuple, spelled shorter.
1417
+ Which of the two a field holds is the field's own declaration, stated in the
1418
+ `Values` column of the tables below and nowhere else.
1353
1419
 
1354
1420
  #### Every address resolves, and every build says so the same way
1355
1421
 
@@ -1388,9 +1454,9 @@ precisely because an author meets whichever build ran first. Three resolvers rea
1388
1454
  one authored link; they must not describe the same mistake in three ways, and
1389
1455
  they must never disagree about whether it is a mistake at all.
1390
1456
 
1391
- #### In frontmatter, a link is a bare address
1457
+ #### In frontmatter, the address is written bare
1392
1458
 
1393
- A `WikiLink` **field** takes the address with no brackets:
1459
+ An **`Address` field** takes the address with no brackets:
1394
1460
 
1395
1461
  ```yaml
1396
1462
  data:
@@ -1399,22 +1465,35 @@ data:
1399
1465
  seat: tashal
1400
1466
  ```
1401
1467
 
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.
1468
+ not `[[hexhodai]]`. The field is declared as an `Address`, so the schema knows
1469
+ what the value is and reads it as one; brackets are a wikilink's punctuation,
1470
+ which the reader would have to strip before it could do anything.
1405
1471
 
1406
1472
  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
1473
+ value such as `hexhodai` names the target by its shortcode alone, with every
1474
+ other segment supplied by the position. Frontmatter is structure rather than
1408
1475
  prose: a field value is a reference something else will compile against, and it
1409
1476
  should say exactly what it points at.
1410
1477
 
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
1478
+ **A field can supply the type.** A field naming one kind of note declares its
1479
+ default — `seat` a `place`, `parents` an `affiliation`, `stations` a `lore` — so
1413
1480
  the type segment defaults from the declaration and a bare shortcode is the
1414
1481
  ordinary case, not an abbreviation of one. That is why the examples above read
1415
1482
  `tashal` rather than `place-tashal`: the shorter form carries the same
1416
1483
  information, because the field already said what kind of thing it points at.
1417
1484
 
1485
+ The frontmatter lint checks scalar Addresses, entries in Address lists, and
1486
+ Address map keys at the entry's own location. Segments contain lowercase ASCII
1487
+ letters and digits; wikilink brackets, labels, anchors, and spaces do not belong
1488
+ in these fields. Optional null values and empty lists or maps express no
1489
+ reference. Grammar and accepted types are checked independently of whether a
1490
+ target exists.
1491
+
1492
+ An affiliation's `economy` accepts only `affiliation` and `lore` Addresses.
1493
+ Each entry states its type: `affiliation-merchants` or `lore-tanvrcrncy`.
1494
+ There is no default type; a bare shortcode is an error.
1495
+ Bundle `contents` likewise requires a type for each document.
1496
+
1418
1497
  **The declaration constrains the type; it does not shorten the address.** A field
1419
1498
  value may be written at any length, and every length is equally correct so long
1420
1499
  as the type it names is the one the field declares. All four of these are valid
@@ -1423,14 +1502,32 @@ in a `seat` field:
1423
1502
  ```yaml
1424
1503
  seat: tashal
1425
1504
  seat: place-tashal
1426
- seat: kethira-place-tashal
1505
+ seat: none-place-tashal
1427
1506
  seat: kethira-none-place-tashal
1428
1507
  ```
1429
1508
 
1509
+ Each is a **suffix** of the canonical address, which is why the third one names
1510
+ the system and the fourth cannot omit it — see [Shorter forms](#shorter-forms).
1511
+
1430
1512
  An `affiliation-` prefix there is an **error naming the field and both types** —
1431
1513
  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.
1514
+ than one type with no default among them, a bare shortcode is refused
1515
+ outright — an error naming the field and the types it accepts, never a
1516
+ resolution across them.
1517
+
1518
+ **A position's default and its accepted set are two different declarations,
1519
+ and most positions are not ambiguous at all.** The paragraph above is about a
1520
+ position that permits more than one type with no default among them; `seat` is
1521
+ the plainer case, permitting exactly one. Between those sits a third shape,
1522
+ and it is the common one: a position that nominates one of its accepted types
1523
+ as the default while accepting others besides. There a bare shortcode is never
1524
+ a candidate to weigh — it takes the default outright, every time — and the
1525
+ accepted set is what a value that states its own type is checked against
1526
+ instead: naming any member of the set is as valid as naming the default
1527
+ itself, and naming a type outside it is an error against the set, never
1528
+ against the default alone. [The four art slots](#the-four-art-slots) are
1529
+ declared exactly this way — each one accepts a second type beyond the one a
1530
+ bare shortcode takes.
1434
1531
 
1435
1532
  **Only the type segment defaults.** Package and system are not the field's to
1436
1533
  supply — it has no opinion about which package holds the target — so reaching
@@ -1452,6 +1549,67 @@ Brackets belong in prose, where a link sits inside a sentence and needs marking
1452
1549
  off from the words around it. A frontmatter value has nothing to be marked off
1453
1550
  from.
1454
1551
 
1552
+ ### Wikilinks
1553
+
1554
+ A **`Wikilink`** is how body prose names something:
1555
+
1556
+ ```text
1557
+ [[<Address>[#<anchor>]|<text>]]
1558
+ ```
1559
+
1560
+ The brackets, the `#<anchor>` and the `|<text>` label are the Wikilink's own.
1561
+ What sits inside them is an `Address` like any other — read by the same grammar,
1562
+ written at any of its lengths, and taking each omitted segment from where the
1563
+ link is written, which for body prose is this package and system `none`. So
1564
+ nothing about resolution belongs to the bracketed form: a wikilink and a
1565
+ frontmatter field ask the same question of the same value, and the findings above
1566
+ are the answers either of them gets.
1567
+
1568
+ The anchor is the Wikilink's, not the Address's. It names a section of the target
1569
+ by the slug that section's heading declares — `[[place-tashal#market|the
1570
+ market]]` — and one naming no section there is the `unknown-anchor` finding.
1571
+ Written alone, `[[#slug|Text]]` addresses a section of the page the link sits on,
1572
+ which is the one form carrying no address at all.
1573
+
1574
+ #### Every wikilink carries a label
1575
+
1576
+ There is one namespace, and the pipe is required:
1577
+
1578
+ | written | resolved as | displays |
1579
+ | ------------------- | ----------- | -------------------------- |
1580
+ | `[[Address\|]]` | an address | the target note's own name |
1581
+ | `[[Address\|Text]]` | an address | `Text` |
1582
+ | `[[Name]]` | nothing | a finding |
1583
+
1584
+ **A link written without a label addresses nothing**, and the correction
1585
+ is always the same: write `[[type-shortcode|Text]]`.
1586
+
1587
+ The bare form does not name an **alias** — a note's own display name, or one of
1588
+ the names it listed in `aliases:` — looked up within the citing note's type. It
1589
+ was measured before it was retired, and the namespace was empty in practice:
1590
+ across 8,305 wikilinks in three content trees, **not one** bare link resolved to
1591
+ a note. What the index behind it did do was fold every note's `name.full` into
1592
+ itself, so two notes of one type could not share a display name — a rules page
1593
+ and a user guide page both called "Gear" were a build failure whose every
1594
+ available fix moved a published URL.
1595
+
1596
+ The top-level `aliases:` that fed it is **retired** and refused. The nested
1597
+ `name.aliases:` is **not**: it is reserved for a use that does not exist yet, so
1598
+ it is permitted and read by nothing — no index, no resolver, no lint rule, no
1599
+ derived address. A note carrying one behaves exactly as one without it.
1600
+
1601
+ Requiring the label is also what makes positional parsing safe. Note names
1602
+ contain hyphens — `Grukar-ahk` is a name, not a `Grukar` of type `ahk` — so a
1603
+ target that does not parse as an address is reported as one that does not, rather
1604
+ than split at an arbitrary place or quietly looked up somewhere else.
1605
+
1606
+ The **empty** label is not a way of writing no label. It says _address this
1607
+ target, and show whatever it calls itself_ — so a note renamed later takes its new
1608
+ name at every citation with no link edited. `[[x|]]` is labelled; `[[x]]` is not.
1609
+
1610
+ It is the label that is required, not a target: the anchor-only form above
1611
+ carries its pipe like every other link.
1612
+
1455
1613
  ### Assets are types
1456
1614
 
1457
1615
  `icon`, `image` and `audio` are types in the type vocabulary, and an address
@@ -1668,12 +1826,12 @@ type the way a field declaration does — `image`. The short-form ladder, the
1668
1826
  package and system defaults, the lowercase rule, the ambiguity reporting and the
1669
1827
  findings vocabulary all apply unchanged.
1670
1828
 
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 |
1829
+ | written | means |
1830
+ | ----------------------------------- | ---------------------------------------- |
1831
+ | `![[anvil\|]]` | decorative — the common case |
1832
+ | `![[anvil\|An anvil]]` | alt text where it carries meaning |
1833
+ | `![[anvil]]` | unlabelled, and a finding like any other |
1834
+ | `![[sohl-none-image-anvil\|Anvil]]` | qualified, reaching another package |
1677
1835
 
1678
1836
  The parser distinguishes a missing label from an empty one, so _deliberately
1679
1837
  decorative_ and _not written_ differ without an exemption.
@@ -1806,7 +1964,7 @@ so adding a type means answering the question rather than inheriting a default.
1806
1964
 
1807
1965
  #### A stub is still referenceable as data
1808
1966
 
1809
- `borders.to` and `routes.to` name a place by shortcode, and those are **index
1967
+ `borders.to` and `routes.to` name a place by `Address`, and those are **index
1810
1968
  relations rather than page links**: a border with a stub on the far side is a
1811
1969
  valid statement about the world and resolves. So does `parents` for the
1812
1970
  containment tree and `domains` for tenure — a stub appears in its region's
@@ -2291,16 +2449,16 @@ Generates a living (or undead, or spirit) being.
2291
2449
 
2292
2450
  | `data` property | Values | Description |
2293
2451
  | --------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |
2294
- | `tokenIcon` | `WikiLink` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2452
+ | `tokenIcon` | `Address` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2295
2453
  | `templatePriority` | `number` | Template priority, _null_ = not a template |
2296
2454
  | `archetypes` | `Archetype[]` | What sort of character this is. **Always an array** — `[]` where none apply; `null` is an error. |
2297
2455
  | `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) |
2456
+ | `stations` | `Address[]` | Name of the stations the character belongs to |
2457
+ | `lore` | `Address[]` | Lore concerning this being — the people it is of, the standing it holds, the law it lives under |
2458
+ | `homes` | `Address[]` | Place the being calls home |
2459
+ | `affiliations` | `Address[]` | Affilliations (e.g., arcane/divine traditions, polities, etc) |
2302
2460
  | `gender` | `male \| female \| other` | Gender of the character |
2303
- | `species` | `WikiLink` | Being's species (lore) |
2461
+ | `species` | `Address` | Being's species (lore) |
2304
2462
  | `born` | `YYYY/MM/DD \| unknown` | When the being was born; absent, it was never born |
2305
2463
  | `died` | `YYYY/MM/DD \| unknown` | When the being died; absent, it is alive |
2306
2464
  | `age` | `34 \| ~34` | Age in years, stated only to override what `born` says; `~` marks an estimate |
@@ -2539,10 +2697,15 @@ A SoHL "being" document will be created, as will an "HM3" document.
2539
2697
 
2540
2698
  | shared source | → sohl | → hm3 |
2541
2699
  | ----------------- | ------ | ------------------- |
2542
- | `data.species` | NA | `system.species` |
2700
+ | `data.species` | NA | NA |
2543
2701
  | `data.gender` | NA | `system.gender` |
2544
2702
  | `data.occupation` | NA | `system.occupation` |
2545
2703
 
2704
+ `data.species` is a lore Address describing the being's species. HM3's
2705
+ `hm3.system.species` is independent native text; it does not inherit from
2706
+ `data.species`. The in-block `hm3.species` spelling is also accepted, with
2707
+ `hm3.system.species` taking precedence.
2708
+
2546
2709
  ### type: homepage
2547
2710
 
2548
2711
  The package's front page, published at `https://www.heroiclands.org/<package>/`.
@@ -2570,10 +2733,10 @@ a card block — the homepage is a page with a body, rendered as one.
2570
2733
 
2571
2734
  Represents a conveyance able to hold goods and people moving from one place to another.
2572
2735
 
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 |
2736
+ | `data` property | Values | Description |
2737
+ | ------------------ | --------- | ------------------------------------------------------------------------ |
2738
+ | `tokenIcon` | `Address` | What a token on the canvas wears — an `icon` address; defaults to `icon` |
2739
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
2577
2740
 
2578
2741
  If `sohl` is present, this becomes a `vehicle` actor.
2579
2742
 
@@ -2650,12 +2813,12 @@ two is the commonest way to get a non-monarchy wrong.
2650
2813
  **Rank**
2651
2814
  Definition of a level within the organization (e.g., Priest, Layperson, Member, Gang Leader, Master, etc.)
2652
2815
 
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 |
2816
+ | Property | Values | Description |
2817
+ | ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
2818
+ | `level` | number | The ranking within the affiliation, increasing values starting with 1, 0 indicates intentional exclusion (expulsion/excommunication) |
2819
+ | `title` | string | Title associated with the Rank |
2820
+ | `description` | string | Description of the Rank |
2821
+ | `lore` | `Address` | The standing this rank _is_ — a `lore` note of subType `law`, shared with every other body that confers the same thing |
2659
2822
 
2660
2823
  **A rank's `lore` is shared; its `title` is not.** A Normen kingdom calls it
2661
2824
  `Thrall` and a Vylarian province calls it `Slave`, and they mean one standing:
@@ -2671,24 +2834,24 @@ rank names the standing, and the standing says.
2671
2834
 
2672
2835
  **Standing**: aligned, unaligned, rival, nemesis
2673
2836
 
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 |
2837
+ | `data` property | Values | Description |
2838
+ | -------------------- | ------------------------ | ---------------------------------------------------------------------------------------------- |
2839
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
2840
+ | `demonym` | `string` | What a member of this affiliation is called (a Vylarian) |
2841
+ | `epithet` | `string` | The by-name it is known by — a god's, an order's, a company's |
2842
+ | `symbol` | `string` | Its emblem in words: a feather atop a golden scale, a chisel carving a star |
2843
+ | `governance.model` | `GovernanceModel` | Type of government structure, if applicable |
2844
+ | `governance.summary` | `string` | summary of the governance situation |
2845
+ | `governance.ranks` | `Rank[]` | The ranks available to members of the affiliation |
2846
+ | `governance.offices` | `Map<name, description>` | Official offices in the affiliation |
2847
+ | `commonSkills` | `Address[]` | Common skills among members (languages, etc.) |
2848
+ | `seat` | `Address` | Where the affiliation's authority sits |
2849
+ | `domains` | `Address[]` | Places over which this affiliation holds sway |
2850
+ | `population` | `number` | Number of people in the affiliation (precision 2 significant digits). |
2851
+ | `economy` | `Address[]` | Economic life: `affiliation` or `lore` Addresses, with an explicit type and no default |
2852
+ | `lore` | `Address[]` | Lore concerning it — the peoples it draws on, the god a faith venerates, its law, its calendar |
2853
+ | `parents` | `Address[]` | Affiliations that this affiliation is subordinate to |
2854
+ | `relations` | `Map<Address, Standing>` | Standing with other affiliations, keyed by the other body's Address |
2692
2855
 
2693
2856
  **A faith tradition is not its god.** An `affiliation` of subType
2694
2857
  `faithtradition` is a _religion_ — a practice, with an ordained priesthood, a
@@ -3004,27 +3167,29 @@ if a `hm3` property is present, an HM3 item of type "miscgear" will be created.
3004
3167
 
3005
3168
  **SkillAptitude**: either a single skill
3006
3169
 
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 |
3170
+ | `data` property | Values | Description |
3171
+ | ------------------ | ---------------------------------------------------------------------------------- | -------------------------------------------------------------- |
3172
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
3173
+ | `assocSkill` | `Address` | Associated skill |
3174
+ | `assocAffiliation` | `Address` | Associated affiliation |
3175
+ | `skillAptitudes` | `Map<Shortcode, number>`, the key a skill's shortcode or `subType:<skill-subtype>` | Bonuses and penalties, per skill or per kind of skill |
3176
+ | `level` | `number` | Magnitude of the mystery |
3177
+ | `charges.value` | `number` | Current number of charges available, _null_ = charges not used |
3178
+ | `charges.max` | `number` | Maximum number of charges, _null_ = no maximum |
3016
3179
 
3017
3180
  if a `sohl` property is present, a SoHL item of type "mystery" will be created.
3018
3181
 
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 |
3182
+ `system.assocSkillCode` and `system.assocAffiliationCode` are authored under
3183
+ `sohl.system` as shortcodes and have no shared source; `data.assocSkill` and
3184
+ `data.assocAffiliation` name the same two notes as Addresses, for the page.
3185
+
3186
+ | shared source | → sohl | → hm3 |
3187
+ | --------------------- | ----------------------- | ----- |
3188
+ | `subType` | `system.subType` | NA |
3189
+ | `data.skillAptitudes` | `system.skillAptitudes` | NA |
3190
+ | `data.level` | `system.levelBase` | NA |
3191
+ | `data.charges.value` | `system.charges.value` | NA |
3192
+ | `data.charges.max` | `system.charges.max` | NA |
3028
3193
 
3029
3194
  ### type: mysticalability
3030
3195
 
@@ -3041,29 +3206,31 @@ if a `sohl` property is present, a SoHL item of type "mystery" will be created.
3041
3206
  - alchemy: The preparation of substances imbued with mystical potency.
3042
3207
  - divination: The practice of obtaining hidden knowledge or foreknowledge by mystical means.
3043
3208
 
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 |
3209
+ | `data` property | Values | Description |
3210
+ | ------------------ | --------- | -------------------------------------------------------------- |
3211
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
3212
+ | `assocSkill` | `Address` | Associated skill |
3213
+ | `assocAffiliation` | `Address` | Associated affiliation |
3214
+ | `masteryLevel` | `number` | Mastery Level |
3215
+ | `level` | `number` | Magnitude of the mystery |
3216
+ | `charges.value` | `number` | Current number of charges available, _null_ = charges not used |
3217
+ | `charges.max` | `number` | Maximum number of charges, _null_ = no maximum |
3053
3218
 
3054
3219
  if a `sohl` property is present, a SoHL item of type "mysticalability" will be created.
3055
3220
 
3056
3221
  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
3222
 
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 |
3223
+ `system.assocSkillCode` and `system.assocAffiliationCode` are authored under
3224
+ `sohl.system` as shortcodes and have no shared source; `data.assocSkill` and
3225
+ `data.assocAffiliation` name the same two notes as Addresses, for the page.
3226
+
3227
+ | shared source | → sohl | → hm3 |
3228
+ | -------------------- | ------------------------- | ------------- |
3229
+ | `subType` | `system.subType` | **see above** |
3230
+ | `data.masteryLevel` | `system.masteryLevelBase` | NA |
3231
+ | `data.level` | `system.levelBase` | NA |
3232
+ | `data.charges.value` | `system.charges.value` | NA |
3233
+ | `data.charges.max` | `system.charges.max` | NA |
3067
3234
 
3068
3235
  ### type: projectilegear
3069
3236
 
@@ -3117,11 +3284,11 @@ HM3 side while remaining distinct on the SoHL side.
3117
3284
  - combat
3118
3285
  - combattechnique
3119
3286
 
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 |
3287
+ | `data` property | Values | Description |
3288
+ | ------------------ | --------- | ------------------------------------------ |
3289
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
3290
+ | `masteryLevel` | `number` | Mastery Level |
3291
+ | `parentSkill` | `Address` | Parent skill this skill specializes |
3125
3292
 
3126
3293
  if a `sohl` property is present, a SoHL item of type "skill" will be created.
3127
3294
 
@@ -3129,11 +3296,14 @@ If an `hm3` property is present, then an HM3 item of type "skill" will be create
3129
3296
 
3130
3297
  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
3298
 
3299
+ `system.parentSkillCode` is authored under `sohl.system` as the shortcode of the
3300
+ skill this one specializes, and has no shared source; `data.parentSkill` names that
3301
+ skill as an Address, for the page.
3302
+
3132
3303
  | shared source | → sohl | → hm3 |
3133
3304
  | ------------------- | ------------------------- | --------------------- |
3134
3305
  | `subType` | `system.subType` | See notes above |
3135
3306
  | `data.masteryLevel` | `system.masteryLevelBase` | `system.masteryLevel` |
3136
- | `data.parentSkill` | `system.parentSkillCode` | NA |
3137
3307
 
3138
3308
  ### type: trauma
3139
3309
 
@@ -3344,7 +3514,7 @@ The `data:` fields, of which three are required:
3344
3514
 
3345
3515
  | `data` property | Values | Description |
3346
3516
  | ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
3347
- | `bgImage` | `WikiLink` | **Required.** The map art — an `image` address. Becomes the level's `background.src`, what tokens stand on |
3517
+ | `bgImage` | `Address` | **Required.** The map art — an `image` address. Becomes the level's `background.src`, what tokens stand on |
3348
3518
  | `dimensions` | `[int, int]` | **Required.** `[width, height]` in whole pixels, the art's own size |
3349
3519
  | `pxPerGrid` | `integer` | **Required.** Whole pixels per grid square; must match the art |
3350
3520
  | `navName` | `string` | Short name for Foundry's scene navigation bar |
@@ -3357,7 +3527,7 @@ The `data:` fields, of which three are required:
3357
3527
  | `tiles` | `Tile[]` | List of tiles, each naming its art in `image` |
3358
3528
  | `sounds` | `Sound[]` | List of sounds, each naming its clip in `audio` |
3359
3529
  | `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 |
3530
+ | `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
3531
  | `notes` | `NoteLocation[]` | grid coordinates of note markers mapped to anchors in this document |
3362
3532
 
3363
3533
  Everything else a Scene holds is **derived**, not authored: padding, grid type,
@@ -3374,7 +3544,7 @@ compilation, and the server-side migration shim is version-gated on
3374
3544
  single Level is synthesised from `bgImage`, `overlay`, `levelName` and
3375
3545
  `backgroundColor`.
3376
3546
 
3377
- > **`bgImage` is a `WikiLink` under `data:`, as every other type's artwork is.**
3547
+ > **`bgImage` is an `Address` under `data:`, as every other type's artwork is.**
3378
3548
  > Art is not system-specific — a Scene is a core Foundry document, and a second
3379
3549
  > system would want the identical art — so the field sits under `data:` rather
3380
3550
  > than inside a system block.
@@ -3455,8 +3625,8 @@ three maps, and none of those maps needs to know it is a keep.
3455
3625
  | `data` property | Values | Description |
3456
3626
  | --------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------- |
3457
3627
  | `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 |
3628
+ | `lore` | `Address[]` | Lore concerning this place — its peoples, its law, its calendar, its history |
3629
+ | `parents` | `Address[]` | Enclosing places within which this place is located |
3460
3630
  | `population` | `number` | Approximate population (precision 2 significant digits) |
3461
3631
  | `market` | `number` | Market class, 1 to 6 — what trade a settlement supports |
3462
3632
  | `borders` | `{ to, bearing }[]` | Places sharing a frontier with this one, and where each lies from here |
@@ -3563,8 +3733,13 @@ data:
3563
3733
  - { to: kethramir, bearing: E, mode: land, days: 30, terrain: [dunes], leagues: 90 }
3564
3734
  ```
3565
3735
 
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.
3736
+ - `to` — required; an `Address` naming the other place. Its type segment defaults
3737
+ to `place` and `place` is the whole of its accepted set, so `vylar`,
3738
+ `place-vylar` and `thalorna-none-place-vylar` name one place and a `to` naming
3739
+ anything else is an error. **A bare shortcode names a place in this package**,
3740
+ which is what the fully qualified form is for: naming a place in a declared
3741
+ dependency's index, so two packages using one shortcode never answer for each
3742
+ other.
3568
3743
  - `bearing` — required; where the neighbour or the destination lies from here.
3569
3744
  - `mode` — required on a route; how the journey is travelled.
3570
3745
  - `days` — required on a route; a marker meaning "about this, under normal
@@ -3591,6 +3766,15 @@ neighbour that states a different bearing, a different `days` or a different
3591
3766
  tell which. Every `to` must resolve to a place, every value must come from its
3592
3767
  closed set, and each finding is located at the entry that states it.
3593
3768
 
3769
+ **A relation that crosses a package boundary is written in full at both ends.**
3770
+ An omitted `<package>` segment is the package being built, in whatever note the
3771
+ value is read from — a note of this package's own, or a record a consumer fetched
3772
+ out of a dependency's index — so a short `to` names a different place in a
3773
+ different build. Only the fully qualified form names one place everywhere, which
3774
+ is why the long form is not optional for a border or a route between packages,
3775
+ and why a short `to` in a fetched record names the package being built rather
3776
+ than the package that wrote it.
3777
+
3594
3778
  | `bearing` value | where the other place lies |
3595
3779
  | --------------- | -------------------------- |
3596
3780
  | `N` | north |
@@ -3749,14 +3933,14 @@ Content prepared to be played — a situation with its cast, places, and possibl
3749
3933
 
3750
3934
  | `data` property | Values | Description |
3751
3935
  | ------------------ | -------------------------------------------- | ------------------------------------------------------------------------------ |
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 |
3936
+ | `parents` | `Address[]` | List of parent scenarios of this scenario (campaigns, etc.) |
3937
+ | `locations` | `Address[]` | List of locations associated with this scenario |
3938
+ | `cast` | `Address[]` | individuals associated with this scenario |
3939
+ | `factions` | `Address[]` | Affiliations associated with this scenario |
3940
+ | `follows` | `Address[]` | Prerequisite scenarios that should be completed before beginning this scenario |
3757
3941
  | `status` | `draft \| playtested \| published` | Playability status of this scenario |
3758
3942
  | `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 |
3943
+ | `party.archetypes` | `Archetype[]` | Archetypes of characters suitable for completion |
3760
3944
 
3761
3945
  ### type: doc
3762
3946
 
@@ -3859,9 +4043,9 @@ authors none takes Foundry's own `icons/svg/dice-target.svg`.
3859
4043
 
3860
4044
  A bundle of notes to be taken as a single unit — an `Adventure` in Foundry VTT.
3861
4045
 
3862
- | `data` property | Values | Description |
3863
- | --------------- | ------------ | ------------------------------------------------------ |
3864
- | `contents` | `WikiLink[]` | The documents the Adventure holds; `[]` when unstated. |
4046
+ | `data` property | Values | Description |
4047
+ | --------------- | ----------- | ------------------------------------------------------ |
4048
+ | `contents` | `Address[]` | The documents the Adventure holds; `[]` when unstated. |
3865
4049
 
3866
4050
  ```yaml
3867
4051
  ---
@@ -3944,10 +4128,10 @@ data:
3944
4128
  ---
3945
4129
  ```
3946
4130
 
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. |
4131
+ | `data` property | Values | Description |
4132
+ | --------------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
4133
+ | `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. |
4134
+ | `color` | `"#RRGGBB"`, a string | The folder's colour. Unset for Foundry's default. |
3951
4135
 
3952
4136
  A folder is addressed `<package>-none-folder-<shortcode>` — **`none`**, because a
3953
4137
  `Folder` is a core Foundry document like a `JournalEntry` or a `Scene`, not a