@heroiclands/package-build 9.0.0 → 10.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +721 -0
- package/CONTENT.md +273 -13
- package/bin/content-build.mjs +437 -7
- package/content-config.mjs +259 -28
- package/docs/content-format.md +1418 -0
- package/engine/address-charset.mjs +62 -0
- package/engine/alias-index.mjs +153 -0
- package/engine/base-compiler.mjs +194 -4
- package/engine/content-address.mjs +4 -4
- package/engine/content-format-check.mjs +570 -0
- package/engine/content-format.mjs +253 -0
- package/engine/content-links.mjs +132 -56
- package/engine/content-lint.mjs +8 -1
- package/engine/diagnostics.mjs +33 -0
- package/engine/document-subtypes.mjs +440 -0
- package/engine/field-spec.mjs +49 -43
- package/engine/frontmatter-lint.mjs +351 -28
- package/engine/generate.mjs +32 -4
- package/engine/helpers.mjs +41 -29
- package/engine/ids.mjs +19 -1
- package/engine/index.mjs +15 -0
- package/engine/item-registry.mjs +72 -5
- package/engine/kb-manifest.mjs +36 -7
- package/engine/map-notes.mjs +34 -18
- package/engine/note-claims.mjs +383 -0
- package/engine/note-vocabulary.mjs +678 -0
- package/engine/pack-config.mjs +39 -22
- package/engine/pack-router.mjs +17 -6
- package/engine/prose-lint.mjs +55 -3
- package/engine/retired-fields.mjs +117 -3
- package/engine/scenes.mjs +19 -1
- package/engine/schema-check.mjs +347 -3
- package/engine/site-build.mjs +1 -1
- package/engine/site-index.mjs +38 -21
- package/engine/system-block.mjs +513 -0
- package/engine/web-wikilinks.mjs +112 -80
- package/engine/wikilink-syntax.mjs +30 -0
- package/engine/wikilinks.mjs +67 -51
- package/package.json +6 -2
- package/sohl/actors.mjs +249 -36
- package/sohl/document-subtypes.mjs +82 -0
- package/sohl/index.mjs +3 -0
- package/sohl/items.mjs +110 -14
- package/sohl/note-schemas.mjs +11 -7
- package/types/content-config.d.mts +48 -4
- package/types/engine/address-charset.d.mts +45 -0
- package/types/engine/alias-index.d.mts +122 -0
- package/types/engine/base-compiler.d.mts +132 -4
- package/types/engine/content-address.d.mts +2 -2
- package/types/engine/content-format-check.d.mts +163 -0
- package/types/engine/content-format.d.mts +101 -0
- package/types/engine/content-links.d.mts +16 -1
- package/types/engine/content-lint.d.mts +6 -0
- package/types/engine/diagnostics.d.mts +29 -0
- package/types/engine/document-subtypes.d.mts +233 -0
- package/types/engine/field-spec.d.mts +76 -23
- package/types/engine/frontmatter-lint.d.mts +47 -2
- package/types/engine/generate.d.mts +14 -1
- package/types/engine/helpers.d.mts +21 -13
- package/types/engine/ids.d.mts +10 -0
- package/types/engine/index.d.mts +5 -0
- package/types/engine/item-registry.d.mts +21 -2
- package/types/engine/kb-manifest.d.mts +35 -8
- package/types/engine/map-notes.d.mts +21 -11
- package/types/engine/note-claims.d.mts +113 -0
- package/types/engine/note-vocabulary.d.mts +251 -0
- package/types/engine/pack-config.d.mts +4 -3
- package/types/engine/pack-router.d.mts +4 -4
- package/types/engine/prose-lint.d.mts +6 -2
- package/types/engine/retired-fields.d.mts +73 -2
- package/types/engine/schema-check.d.mts +182 -0
- package/types/engine/system-block.d.mts +281 -0
- package/types/engine/web-wikilinks.d.mts +23 -12
- package/types/engine/wikilink-syntax.d.mts +29 -0
- package/types/sohl/actors.d.mts +62 -6
- package/types/sohl/document-subtypes.d.mts +14 -0
- package/types/sohl/index.d.mts +1 -0
- package/types/sohl/items.d.mts +21 -0
package/CONTENT.md
CHANGED
|
@@ -167,7 +167,7 @@ file can be asked for rather than told:
|
|
|
167
167
|
| `rootDir` | the directory the configuration file sits in |
|
|
168
168
|
| `foundryPackage` | the `name` of the adjacent `package.json`, verbatim |
|
|
169
169
|
| `stats.systemVersion` | a **system**: that `package.json`'s `version`. A **module**: the `verified` version of the system it declares a relationship with |
|
|
170
|
-
| `itemBuilders` | the named registry (`sohl`)
|
|
170
|
+
| `itemBuilders` | the named registry (`sohl`) — or a list of names — required lazily so importing costs nothing |
|
|
171
171
|
|
|
172
172
|
**Authoring any of the first three is an error**, not an override. Each was
|
|
173
173
|
previously transcribed from a file that already stated it, and a transcription
|
|
@@ -190,6 +190,23 @@ cross-package wikilink writes to reach one of these notes. It is the
|
|
|
190
190
|
repository's identity in the address space — not a filter — and a note does not
|
|
191
191
|
restate it.
|
|
192
192
|
|
|
193
|
+
Because it is a segment of an address, the value is **validated** rather than
|
|
194
|
+
taken as written, and a violation fails the build naming the line it is on:
|
|
195
|
+
|
|
196
|
+
- **Alphanumeric** (`^[A-Za-z0-9]+$`). An address is read by counting
|
|
197
|
+
hyphen-separated segments, so the hyphen has to be purely a separator — which
|
|
198
|
+
is why `harn-adventures` is configured as `harnadventures`. This is the same
|
|
199
|
+
rule `shortcode` is already held to, and the two are one constant.
|
|
200
|
+
- **Not a note type.** The package and the type are adjacent segments, and the
|
|
201
|
+
two vocabularies are kept disjoint so a reader never has to decide which slot
|
|
202
|
+
a name is filling. `doc`, `being`, every map type, and every item type this
|
|
203
|
+
repository declares — with its `doc`-prefixed documentation form — are
|
|
204
|
+
refused.
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
package-build.config.yaml:1:1: error: package-build config: `contentPackage` is `harn-adventures`, which is not alphanumeric. It is the first segment of every address this package publishes (`harn-adventures-<type>-<shortcode>`), and an address is read by counting hyphen-separated segments — so anything outside `[A-Za-z0-9]` here makes those addresses unreadable rather than merely ugly. `harn-adventures` became `harnadventures`.
|
|
208
|
+
```
|
|
209
|
+
|
|
193
210
|
**`package:` in a note's frontmatter is retired, and declaring it fails the
|
|
194
211
|
build**, naming the file, whatever the value says. An agreeing declaration is
|
|
195
212
|
refused exactly as a disagreeing one is: there is no value that makes writing
|
|
@@ -354,6 +371,33 @@ object. Supplying `itemBuilders` is therefore all a consumer does to define an
|
|
|
354
371
|
item type of its own; a table this package ships is one possible value, not the
|
|
355
372
|
one the compiler holds.
|
|
356
373
|
|
|
374
|
+
**A tree feeding two systems declares a set of registries.** One registry is
|
|
375
|
+
also a ceiling: the accepted vocabulary is its keys, so a type only the _other_
|
|
376
|
+
system knows — `spell` and `invocation` are HM3's, `mysticalability` is SoHL's —
|
|
377
|
+
cannot be accepted at all. So `itemBuilders` takes either form:
|
|
378
|
+
|
|
379
|
+
```yaml
|
|
380
|
+
itemBuilders: sohl # one registry, and what every existing configuration says
|
|
381
|
+
itemBuilders: [sohl, hm3] # a set; the vocabulary is their union
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
A registry's **name is the system it belongs to**, which is what lets a data
|
|
385
|
+
configuration declare a set without naming each system twice. In an `.mjs`
|
|
386
|
+
configuration the same set is written out:
|
|
387
|
+
|
|
388
|
+
```js
|
|
389
|
+
itemBuilders: [
|
|
390
|
+
{ system: "sohl", builders: SOHL_ITEM_BUILDERS },
|
|
391
|
+
{ system: "hm3", builders: HM3_ITEM_BUILDERS },
|
|
392
|
+
],
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
A type **both** registries declare — `skill` is one name over two data models —
|
|
396
|
+
keeps a builder on each side. `itemBuilder(type, system)` and `itemArt(type,
|
|
397
|
+
system)` take the system that is asking; asking without one, for a type more
|
|
398
|
+
than one registry declares, **throws** rather than answering with whichever was
|
|
399
|
+
declared first.
|
|
400
|
+
|
|
357
401
|
**Configuration is the source, and the manifest is generated from it.** That
|
|
358
402
|
arrow used to point the other way: `paths.packageManifest` said where a
|
|
359
403
|
hand-authored `system.template.json` lived, and the package-id guard and the
|
|
@@ -464,12 +508,115 @@ that same map, so there is still exactly one map — and the drift a test used t
|
|
|
464
508
|
watch for is now unrepresentable, because building the registry throws if a type
|
|
465
509
|
has no art.
|
|
466
510
|
|
|
511
|
+
## The per-system block
|
|
512
|
+
|
|
513
|
+
A note is **system-agnostic**. The only system-specific things it carries are
|
|
514
|
+
the properties named after a system, and one note may carry more than one — a
|
|
515
|
+
`being` in `harn-ensemble` compiles into a SoHL `being` _and_ an HM3
|
|
516
|
+
`character`. Within a system's block:
|
|
517
|
+
|
|
518
|
+
| property | maps to |
|
|
519
|
+
| ------------------ | ------------------------------------------------------------- |
|
|
520
|
+
| `<system>.system` | `document.system` — the DataModel schema, verbatim paths |
|
|
521
|
+
| `<system>.type` | `document.type` — the subtype the note compiles into |
|
|
522
|
+
| `<system>.img` | `document.img` |
|
|
523
|
+
| `<system>.items` | `document.items` — actors only |
|
|
524
|
+
| `<system>.effects` | `document.effects` |
|
|
525
|
+
| `<system>.flags` | `document.flags` |
|
|
526
|
+
| `<system>.pack` | _nothing on the document_ — a build directive naming the pack |
|
|
527
|
+
|
|
528
|
+
Everything else a system declares sits directly under the block. `kbcat` is
|
|
529
|
+
toolchain vocabulary, and the _generators_ `items` and `attributes` expand into
|
|
530
|
+
embedded documents rather than mapping anywhere, so neither has a `system` path
|
|
531
|
+
to be written at.
|
|
532
|
+
|
|
533
|
+
`archetype` is authored there too, and is the one such key that _is_ a field:
|
|
534
|
+
the builder writes it to `system.archetype` — a number is an archetype at that
|
|
535
|
+
priority, `null` is not an archetype — exactly as `portrait` reaches
|
|
536
|
+
`sohl.system.portrait` from a shared top-level property. Its authored position
|
|
537
|
+
is unchanged (#126).
|
|
538
|
+
|
|
539
|
+
```yaml
|
|
540
|
+
type: being # the content type — system-agnostic
|
|
541
|
+
pack: actors # shared: unless a block says otherwise
|
|
542
|
+
portrait: kaldor.webp # shared: reaches both systems' fields, differently named
|
|
543
|
+
hm3:
|
|
544
|
+
type: character # this system's document subtype
|
|
545
|
+
pack: actors-hm3 # overrides the shared one, for HM3 only
|
|
546
|
+
system: # → document.system, verbatim
|
|
547
|
+
species: human
|
|
548
|
+
sunsign: ulandus
|
|
549
|
+
attributes: { str: 10, sta: 14 } # a generator, not a system field
|
|
550
|
+
sohl:
|
|
551
|
+
type: being
|
|
552
|
+
system:
|
|
553
|
+
currentMoveMedium: walk
|
|
554
|
+
archetype: 1
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**The shared fallback is declared, not name-matched.** `sohl.system.portrait`
|
|
558
|
+
and `hm3.system.bioImage` both default from one shared property, and they are
|
|
559
|
+
two real fields with different names — SoHL's `Actor.being` and HM3's
|
|
560
|
+
`Actor.character` share **no** field name at all, so a rule matching on spelling
|
|
561
|
+
would never fire. Each field declares its source instead, and resolution for a
|
|
562
|
+
system `S` is:
|
|
563
|
+
|
|
564
|
+
1. `S.system.<to>` — authored directly, wins outright;
|
|
565
|
+
2. `S.<name>` — the legacy in-block position, until the corpus moves off it;
|
|
566
|
+
3. the shared top-level property the field declares as its source, which may be
|
|
567
|
+
a **dotted path** (`data.portrait`) rather than a sibling key;
|
|
568
|
+
4. the field's own default.
|
|
569
|
+
|
|
570
|
+
`FieldSpec.name` is that declared source. It used to mean "frontmatter key under
|
|
571
|
+
`sohl:`", which is the degenerate case where source and destination happen to
|
|
572
|
+
share a name.
|
|
573
|
+
|
|
574
|
+
**`<system>.system` is written through verbatim**, at the DataModel's own paths,
|
|
575
|
+
with no renaming layer. A key the system's published `schema.json` does not
|
|
576
|
+
declare for the subtype the note compiles into is an **error naming the note**,
|
|
577
|
+
not a silent drop: Foundry discards an unknown `system` key at construction
|
|
578
|
+
without a word, so the alternative is a field the author wrote and nobody will
|
|
579
|
+
ever see. A path a declared field already writes is left to that field, so the
|
|
580
|
+
value goes through one coercion rather than two.
|
|
581
|
+
|
|
582
|
+
**What the compiler writes on its own is checked too.** A compiled document
|
|
583
|
+
carries keys no field declaration and no note ever names — `shortcode`,
|
|
584
|
+
`archetype`, `actionDefs`, `notes`, `docHtml` — because the pass writes them
|
|
585
|
+
itself, and they were compared against nothing: the declaration-derived check
|
|
586
|
+
reads `itemBuilders`, the note-side check reads `<system>.system`, and these are
|
|
587
|
+
in neither. A compile now reads the `system` block each pass **assembled** and
|
|
588
|
+
checks its keys against the same published `schema.json`, so the emitted set is
|
|
589
|
+
observed rather than listed and a compiler that grows a key is covered without
|
|
590
|
+
anyone remembering to add it. A key the schema does not declare is an error,
|
|
591
|
+
reported once per subtype rather than once per document, and the message says
|
|
592
|
+
which of two things wrote it: a `fields:` entry, which the repository can change,
|
|
593
|
+
or the compiler, which it cannot — that one means the build is running ahead of
|
|
594
|
+
the system it compiles for, and the fixes are to declare the field there or to
|
|
595
|
+
hold this package at a build that does not write it. A subtree the schema
|
|
596
|
+
declares but describes no further — a discriminated `TypedSchemaField`, stored
|
|
597
|
+
flat — is left alone rather than reported wholesale.
|
|
598
|
+
|
|
599
|
+
**A pack that declares a `system:` takes only notes carrying that block.** A
|
|
600
|
+
note that says nothing about a system has no system data, and compiling it there
|
|
601
|
+
would emit a hollow document — a subtype, and none of the fields the subtype
|
|
602
|
+
exists for. The build fails naming the note and the pack. A pack declaring no
|
|
603
|
+
system constrains nothing, and a pass whose document is not system data at all —
|
|
604
|
+
journals, macros, scenes — is not subject to the rule.
|
|
605
|
+
|
|
606
|
+
**`(type, shortcode)` resolves inside one system's catalogue.** A being names its
|
|
607
|
+
embedded items by address and never by pack, so the Item packs are read as one
|
|
608
|
+
address space; with two systems in the tree that space stops being one, because
|
|
609
|
+
`skill:sword` exists under both names. An Actor pass reads the Item packs of its
|
|
610
|
+
**own** system plus the system-neutral ones.
|
|
611
|
+
|
|
467
612
|
## Command line
|
|
468
613
|
|
|
469
614
|
```
|
|
470
615
|
npx content-build package <compile|unpack|clean> [pack] [entry]
|
|
471
616
|
npx content-build docs item-fields [--out <path>] [--title <title>]
|
|
472
617
|
npx content-build lint [root] [--no-references]
|
|
618
|
+
npx content-build content-format schema --schema <system>=<path>
|
|
619
|
+
npx content-build content-format notes [root] [--strict]
|
|
473
620
|
npx content-build links [root] [--manifests <dir>]
|
|
474
621
|
npx content-build format [paths..] [--write]
|
|
475
622
|
npx content-build markdown [paths..] [--fix]
|
|
@@ -479,18 +626,19 @@ npx content-build reachability <dir> [file] [--index <shortcode>]
|
|
|
479
626
|
npx content-build addresses diff --from <zip|dir> [--strict]
|
|
480
627
|
```
|
|
481
628
|
|
|
482
|
-
| Command
|
|
483
|
-
|
|
|
484
|
-
| `package`
|
|
485
|
-
| `docs`
|
|
486
|
-
| `lint`
|
|
487
|
-
| `
|
|
488
|
-
| `
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
491
|
-
| `
|
|
492
|
-
| `
|
|
493
|
-
| `
|
|
629
|
+
| Command | What it does |
|
|
630
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
631
|
+
| `package` | Compile the content tree into LevelDB packs, unpack a shipped pack back to JSON, or clean one. See [Install](#install). |
|
|
632
|
+
| `docs` | Render a generated reference from the configured registries. `item-fields` is the item-frontmatter page. |
|
|
633
|
+
| `lint` | Check a content tree's addresses and its frontmatter. See [Linting a content tree](#linting-a-content-tree). |
|
|
634
|
+
| `content-format` | Check the content format specification itself. See [The content format specification](#the-content-format-specification). |
|
|
635
|
+
| `links` | Check that every link in the tree lands: dead anchors, dead qualified addresses, wikilinks in frontmatter, drifted manifests, and the package homepage's own addresses. |
|
|
636
|
+
| `format` | Prettier, with the shared configuration. See [Prose: formatting and markdown](#prose-formatting-and-markdown). |
|
|
637
|
+
| `markdown` | markdownlint, with the shared rule set — the structure Prettier is indifferent to. |
|
|
638
|
+
| `manifest` | Emit this package's cross-package link manifest. See [Publishing a link manifest](#publishing-a-link-manifest). |
|
|
639
|
+
| `site` | Publish the content tree as a website. See [Publishing a website](#publishing-a-website). |
|
|
640
|
+
| `reachability` | Walk outward from an index note and report what no path reaches, for a tree meant to be navigable from one entry point. |
|
|
641
|
+
| `addresses` | Report every published item address this build has stopped publishing. See [Diffing published addresses](#diffing-published-addresses). |
|
|
494
642
|
|
|
495
643
|
Every path, pack name and root it needs comes from the consuming repository's
|
|
496
644
|
`package-build.config.yaml`, so the usual invocation takes no arguments beyond
|
|
@@ -609,6 +757,45 @@ builder, so schema and compiler cannot disagree. The hand-written compilers —
|
|
|
609
757
|
|
|
610
758
|
Nothing here writes. A check reports and an author fixes.
|
|
611
759
|
|
|
760
|
+
### The `data:` container is closed; the top level is not
|
|
761
|
+
|
|
762
|
+
A note's frontmatter has three regions, and only one of them is open (#128):
|
|
763
|
+
|
|
764
|
+
| region | describes | an unknown key is |
|
|
765
|
+
| ---------------- | -------------------------------------------- | ------------------ |
|
|
766
|
+
| top level | the note as a published artefact | passed to the page |
|
|
767
|
+
| `data:` | the subject itself, whatever system reads it | an **error** |
|
|
768
|
+
| `sohl:` / `hm3:` | the subject as one system's documents | an **error** |
|
|
769
|
+
|
|
770
|
+
**Top level is deliberately open**, for the reason the homepage rule above gives
|
|
771
|
+
at length: every key of it is copied into the generated page, so an
|
|
772
|
+
unrecognised one is a Hugo or theme parameter this build has no standing to
|
|
773
|
+
refuse. `description` is the everyday case — not a document field at all, but
|
|
774
|
+
the page's description.
|
|
775
|
+
|
|
776
|
+
**`data:` is deliberately closed**, and that is the point of having it. The
|
|
777
|
+
type-specific facts about a subject — a weapon's weight, an affliction's
|
|
778
|
+
transmission, a being's species — used to sit at the top level, where the
|
|
779
|
+
pass-through rule applied to them too. So a misspelled `wieght` became a theme
|
|
780
|
+
parameter rather than a finding, indistinguishable from a weapon that weighs
|
|
781
|
+
nothing. Under `data:` the same key is reported where it was written, with the
|
|
782
|
+
key it was probably meant to be, drawn from that type's own vocabulary:
|
|
783
|
+
|
|
784
|
+
```text
|
|
785
|
+
assets/content/Gear/Axe.md:14:5: error: "wieght" is not a `data:` property of a weapongear; the container is closed, so unlike a top-level key it is not passed through to the page. Did you mean "weight"?
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
**`subType` stays at the top level**, and is closed in its own way: a type
|
|
789
|
+
either declares a `subType` or does not, and a type that does declares its
|
|
790
|
+
values. A `weapon` declares none — SoHL distinguishes a weapon's uses by strike
|
|
791
|
+
mode rather than by kind — so `subType` on one is a finding; a `skill` declares
|
|
792
|
+
ten, so `subType: crafte` is a finding naming `craft`.
|
|
793
|
+
|
|
794
|
+
The vocabulary lives in `engine/note-vocabulary.mjs`, one entry per note type,
|
|
795
|
+
taken from the content-format specification. It is note-format knowledge rather
|
|
796
|
+
than any system's: `data:` holds what is true of the thing, and what a system
|
|
797
|
+
makes of that value is declared in that system's own half.
|
|
798
|
+
|
|
612
799
|
**A third rule was retired (#79).** Every note used to be required to repeat its
|
|
613
800
|
own `type-shortcode` address in `aliases:`. That served one reader — Obsidian,
|
|
614
801
|
so `[[type-shortcode]]` resolved in the editor — and no build ever read it: both
|
|
@@ -722,6 +909,79 @@ attempted:
|
|
|
722
909
|
cannot be improved: a package homepage is in no link manifest, so there is no
|
|
723
910
|
better form to write.
|
|
724
911
|
|
|
912
|
+
## The content format specification
|
|
913
|
+
|
|
914
|
+
`docs/content-format.md` is the contract this package honours: how a note
|
|
915
|
+
becomes a Foundry document and a web page. Three frontmatter regions, a note
|
|
916
|
+
vocabulary with its own `type` and `subType`, a declared map onto each system's
|
|
917
|
+
document fields, the precedence between a shared source and a system's override,
|
|
918
|
+
and the wikilink address grammar.
|
|
919
|
+
|
|
920
|
+
**It does not define the `sohl:` or `hm3:` schemas.** Each system defines its
|
|
921
|
+
own, and its published `schema.json` is the authoritative statement of it. The
|
|
922
|
+
format says which shared source feeds which system field; what fields exist, and
|
|
923
|
+
what they mean, belongs to the system.
|
|
924
|
+
|
|
925
|
+
That makes a mapping row **checkable rather than declarative**, and two commands
|
|
926
|
+
check it. Both read the document's own tables, so editing the specification
|
|
927
|
+
changes what they assert — a transcribed copy would be free to drift from the
|
|
928
|
+
prose the moment either was edited, which is the failure they exist to prevent.
|
|
929
|
+
|
|
930
|
+
### `content-format schema` — the specification against a published schema
|
|
931
|
+
|
|
932
|
+
```bash
|
|
933
|
+
npx content-build content-format schema --schema sohl=./schema.json
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
Every `system.*` target the document names must appear in the naming system's
|
|
937
|
+
published `schema.json`, in the `version: 1` shape `package-build schema` emits.
|
|
938
|
+
A target no schema declares is an error naming the note type, the field, the
|
|
939
|
+
system and the version it was checked at — because a field may be perfectly well
|
|
940
|
+
defined on the system's `main` and simply unreleased, and that is the difference
|
|
941
|
+
between "the specification is wrong" and "the schema has not caught up".
|
|
942
|
+
|
|
943
|
+
A target is resolved against the **union** of the system's document subtypes.
|
|
944
|
+
The mapping tables say which field a shared source reaches; _which subtype
|
|
945
|
+
receives it_ is the note-type → subtype map, which does not exist yet — so the
|
|
946
|
+
question asked is the one that can be answered honestly today, and it narrows to
|
|
947
|
+
the subtype when that map lands.
|
|
948
|
+
|
|
949
|
+
`--schema` is repeatable and takes `<system>=<path>`, because a consumer holds
|
|
950
|
+
its system's artifact and this repository holds a committed fixture, and neither
|
|
951
|
+
arrangement should have to pretend to be the other. A system the document maps
|
|
952
|
+
onto but no schema was supplied for is reported as **unchecked**, with a count:
|
|
953
|
+
HM3 publishes no artifact today, so that is the ordinary case for a fifth of the
|
|
954
|
+
claims, and a check that skipped them in silence would read as one that passed.
|
|
955
|
+
|
|
956
|
+
### `content-format notes` — a content tree against the declared vocabulary
|
|
957
|
+
|
|
958
|
+
```bash
|
|
959
|
+
npx content-build content-format notes # the configured `paths.content`
|
|
960
|
+
npx content-build content-format notes --strict # and fail on what it finds
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
Measures every authored note against the per-type `data` tables, and counts the
|
|
964
|
+
findings by class:
|
|
965
|
+
|
|
966
|
+
| class | what it means |
|
|
967
|
+
| ----------------------- | --------------------------------------------------------------------- |
|
|
968
|
+
| `unknown-type` | the format declares no `### type:` section for this note's `type` |
|
|
969
|
+
| `unknown-data-key` | a key in `data:`, which is closed, that the type does not declare |
|
|
970
|
+
| `top-level-data-key` | a declared `data` property written at top level instead |
|
|
971
|
+
| `system-block-data-key` | a declared shared source written straight into a `sohl:`/`hm3:` block |
|
|
972
|
+
|
|
973
|
+
**It reports; it does not fail.** Every authored note predates the format, so a
|
|
974
|
+
failing check would be red in every repository on the day it lands and would
|
|
975
|
+
stay red for the length of the migration — which is a check nobody can act on
|
|
976
|
+
and everybody learns to skip. The counts are the migration's progress bar
|
|
977
|
+
instead, and each class is promoted to fatal, by turning `--strict` on, as it
|
|
978
|
+
reaches zero.
|
|
979
|
+
|
|
980
|
+
What it deliberately leaves alone is a key inside a system block that the format
|
|
981
|
+
says nothing about. Those regions are closed against _the system's_ schema, not
|
|
982
|
+
against this document, and `content-build lint` already checks them against the
|
|
983
|
+
declared fields.
|
|
984
|
+
|
|
725
985
|
## Prose: formatting and markdown
|
|
726
986
|
|
|
727
987
|
```bash
|