@heroiclands/package-build 23.4.1 → 23.5.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 +35 -0
- package/README.md +29 -25
- package/assets/images/provenance.yaml +1 -0
- package/bin/package-build.mjs +259 -101
- package/ci/prettier.mjs +73 -0
- package/config.mjs +10 -0
- package/container.mjs +232 -1
- package/content-config.mjs +5 -1
- package/docs/api.md +100 -52
- package/docs/authoring/assets.md +16 -1
- package/docs/commands.md +154 -51
- package/docs/configuration.md +39 -2
- package/docs/content-format.md +30 -6
- package/docs/diagnostics.md +1 -2
- package/docs/guides/e2e.md +38 -0
- package/docs/guides/icons.md +5 -0
- package/docs/guides/module-project.md +1 -1
- package/docs/guides/release-and-deploy.md +36 -1
- package/docs/project-setup.md +16 -59
- package/docs/reference/format-details.md +180 -34
- package/docs/reference/note-types.md +69 -69
- package/engine/asset-index.mjs +63 -22
- package/engine/asset-references.mjs +355 -0
- package/engine/asset-shipping.mjs +363 -0
- package/engine/asset-types.mjs +70 -0
- package/engine/base-compiler.mjs +24 -1
- package/engine/cdn-publish.mjs +643 -0
- package/engine/index.mjs +6 -0
- package/engine/note-vocabulary.mjs +4 -1
- package/engine/pathnames.mjs +21 -10
- package/engine/project-init.mjs +4 -29
- package/engine/rank-ladder.mjs +40 -16
- package/engine/scenes.mjs +1 -1
- package/engine/standing-terms.mjs +3 -1
- package/package.json +5 -6
- package/packagebuild-metadata.jsonl +13 -13
- package/release.mjs +69 -7
- package/sohl/actors.mjs +12 -4
- package/sohl/default-item-art.mjs +23 -19
- package/types/config.d.mts +7 -0
- package/types/container.d.mts +137 -1
- package/types/engine/asset-index.d.mts +3 -2
- package/types/engine/asset-references.d.mts +136 -0
- package/types/engine/asset-shipping.d.mts +240 -0
- package/types/engine/asset-types.d.mts +36 -0
- package/types/engine/base-compiler.d.mts +7 -1
- package/types/engine/cdn-publish.d.mts +312 -0
- package/types/engine/index.d.mts +2 -0
- package/types/engine/pathnames.d.mts +2 -1
- package/types/engine/rank-ladder.d.mts +25 -6
- package/types/release.d.mts +5 -3
- package/types/sohl/default-item-art.d.mts +2 -1
- package/labels.mjs +0 -191
- package/types/labels.d.mts +0 -40
package/docs/project-setup.md
CHANGED
|
@@ -30,8 +30,7 @@ acme-bestiary/
|
|
|
30
30
|
│ ├── config.json
|
|
31
31
|
│ └── *.md # one per unreleased change
|
|
32
32
|
├── .github/
|
|
33
|
-
│ ├──
|
|
34
|
-
│ ├── ISSUE_REPORTING.md # §3 lists the same labels
|
|
33
|
+
│ │ ├── ISSUE_REPORTING.md # how issues are filed
|
|
35
34
|
│ └── workflows/
|
|
36
35
|
├── assets/
|
|
37
36
|
│ └── content/ # authored Markdown notes
|
|
@@ -161,19 +160,18 @@ anything to remove.
|
|
|
161
160
|
|
|
162
161
|
### `lint:*` — the checks, one per question
|
|
163
162
|
|
|
164
|
-
|
|
163
|
+
Six checks, each answering one question and blind to what the others see.
|
|
165
164
|
They are separate scripts rather than one because a failing chain stops at its
|
|
166
165
|
first failure, and knowing _which_ question failed is most of the diagnosis.
|
|
167
166
|
|
|
168
167
|
```json
|
|
169
|
-
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:
|
|
168
|
+
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:changelog",
|
|
170
169
|
"lint:format": "package-build format",
|
|
171
170
|
"lint:markdown": "package-build markdown",
|
|
172
171
|
"lint:markdown:fix": "package-build markdown --fix",
|
|
173
172
|
"lint:addresses": "package-build lint",
|
|
174
173
|
"lint:content-links": "package-build links",
|
|
175
174
|
"lint:lang": "package-build lang check",
|
|
176
|
-
"lint:labels": "package-build labels check",
|
|
177
175
|
"lint:changelog": "package-build changelog check"
|
|
178
176
|
```
|
|
179
177
|
|
|
@@ -194,9 +192,6 @@ first failure, and knowing _which_ question failed is most of the diagnosis.
|
|
|
194
192
|
`package-build lang coverage` and `package-build lang hardcoded`; a
|
|
195
193
|
content-only package has nothing for those two to read, and each dies naming
|
|
196
194
|
the glob that matched nothing rather than passing vacuously.
|
|
197
|
-
- **`lint:labels`** — do `.github/labels.yml` and §3 of
|
|
198
|
-
`.github/ISSUE_REPORTING.md` still list the same labels? Neither derives from
|
|
199
|
-
the other, so nothing else notices when they drift.
|
|
200
195
|
- **`lint:changelog`** — does every pending `.changeset/*.md` read like a
|
|
201
196
|
release note rather than a pasted pull-request description? Passes
|
|
202
197
|
trivially when nothing is pending: no pull request is required to carry a
|
|
@@ -499,46 +494,9 @@ tool:
|
|
|
499
494
|
|
|
500
495
|
## `.github/`
|
|
501
496
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
source of truth, synced to GitHub by the org-wide `labels` action. A label on
|
|
506
|
-
GitHub that is not listed there is deleted on the next sync, which is what makes
|
|
507
|
-
the set closed and why nothing invents one.
|
|
508
|
-
|
|
509
|
-
```yaml
|
|
510
|
-
- name: documentation
|
|
511
|
-
color: "0075ca"
|
|
512
|
-
description: Documentation about this repository — README, process, authoring guides.
|
|
513
|
-
|
|
514
|
-
- name: devops
|
|
515
|
-
color: "e07b31"
|
|
516
|
-
description: Build, tooling, CI, release, repo config.
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
**`.github/ISSUE_REPORTING.md`** §3 is the same list in prose, for a human
|
|
520
|
-
filing an issue. `package-build labels check` compares the two and fails when
|
|
521
|
-
they disagree:
|
|
522
|
-
|
|
523
|
-
```console
|
|
524
|
-
$ npx package-build labels check
|
|
525
|
-
package-build: registry and §3 agree (2 labels).
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
Neither file derives from the other, so this check is the only thing standing
|
|
529
|
-
between them and a silent divergence. Editing the registry means editing both.
|
|
530
|
-
|
|
531
|
-
Labels are **subject matter only**. Work shape — bug, feature, epic, task, spike
|
|
532
|
-
— is an issue _type_, shared across the organization, so there is no `bug` label
|
|
533
|
-
and filtering on one returns nothing.
|
|
534
|
-
|
|
535
|
-
Neither file exists in a fresh repository, and `labels check` says so rather
|
|
536
|
-
than passing vacuously:
|
|
537
|
-
|
|
538
|
-
```console
|
|
539
|
-
$ npx package-build labels check
|
|
540
|
-
package-build: labels check: .github/labels.yml does not exist.
|
|
541
|
-
```
|
|
497
|
+
**`.github/ISSUE_REPORTING.md`** says how issues are filed. Labels are
|
|
498
|
+
organisation-level and defined in `HeroicLands/.github`, so the repository
|
|
499
|
+
carries no label registry of its own.
|
|
542
500
|
|
|
543
501
|
`.github/workflows/` then carries the repository's own CI. The org-wide
|
|
544
502
|
`HeroicLands/.github` repository supplies the shared composite actions those
|
|
@@ -549,17 +507,16 @@ site-deploy workflow, so a fix to any of them reaches every repository at once.
|
|
|
549
507
|
|
|
550
508
|
A summary, because "where does this come from" is the question that recurs.
|
|
551
509
|
|
|
552
|
-
| Read
|
|
553
|
-
|
|
|
554
|
-
| `package.json`
|
|
555
|
-
| `package-build.config.yaml`
|
|
556
|
-
| `assets/content/**/*.md`
|
|
557
|
-
| `.gitignore`
|
|
558
|
-
| `.prettierignore`
|
|
559
|
-
|
|
|
560
|
-
| `
|
|
561
|
-
| `.
|
|
562
|
-
| `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
|
|
510
|
+
| Read | By |
|
|
511
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
512
|
+
| `package.json` | The Foundry package id, the version, the release addresses; the site's `baseURL`, description and author. |
|
|
513
|
+
| `package-build.config.yaml` | Everything else about the build. |
|
|
514
|
+
| `assets/content/**/*.md` | Every content command. |
|
|
515
|
+
| `.gitignore` | `package-build format`, `package-build markdown`. |
|
|
516
|
+
| `.prettierignore` | `package-build format`. |
|
|
517
|
+
| `lang/*.json` | `package-build lang`. |
|
|
518
|
+
| `.env.local` | `package-build deploy`. |
|
|
519
|
+
| `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
|
|
563
520
|
|
|
564
521
|
Everything written goes under `build/`. Nothing the toolchain generates is
|
|
565
522
|
committed.
|
|
@@ -917,7 +917,9 @@ of ownership** and every surface derives its own address from it.
|
|
|
917
917
|
|
|
918
918
|
**The first segment names the package, when an `assets/` follows it.**
|
|
919
919
|
Everything after that `assets/` is the _suffix_ — the path inside the package's
|
|
920
|
-
|
|
920
|
+
`assets/` directory, and the one piece every derived form is built from. A
|
|
921
|
+
suffix under `icons/` or `images/` reaches Foundry and the website in the
|
|
922
|
+
deployed layout, by type root and basename alone. A pathname
|
|
921
923
|
that does not open `<package>/assets/` belongs to the package being compiled,
|
|
922
924
|
and the whole of it is the suffix.
|
|
923
925
|
|
|
@@ -931,7 +933,7 @@ ships as the Foundry module `harn-ensemble`) writing `images/map.webp`:
|
|
|
931
933
|
|
|
932
934
|
| Surface | Address |
|
|
933
935
|
| ----------- | ---------------------------------------------------------- |
|
|
934
|
-
| **Foundry** | `modules/harn-ensemble/
|
|
936
|
+
| **Foundry** | `modules/harn-ensemble/images/map.webp` |
|
|
935
937
|
| **Local** | `assets/images/map.webp` |
|
|
936
938
|
| **Web** | `https://cdn.heroiclands.org/harnensemble/images/map.webp` |
|
|
937
939
|
| **Book** | `assets/images/map.webp`, staged beside the Typst source |
|
|
@@ -941,9 +943,9 @@ system ships and this repository does not hold:
|
|
|
941
943
|
|
|
942
944
|
| Surface | Address |
|
|
943
945
|
| ----------- | ------------------------------------------------------------- |
|
|
944
|
-
| **Foundry** | `systems/sohl/
|
|
946
|
+
| **Foundry** | `systems/sohl/icons/shieldnn.svg` |
|
|
945
947
|
| **Local** | `assets/icons/noun/shieldnn.svg`, in the `sohl` repository |
|
|
946
|
-
| **Web** | `https://cdn.heroiclands.org/sohl/icons/
|
|
948
|
+
| **Web** | `https://cdn.heroiclands.org/sohl/icons/shieldnn.svg` |
|
|
947
949
|
| **Book** | not carried, unless a declared replacement carries it instead |
|
|
948
950
|
|
|
949
951
|
**The content package name and Foundry id are separate values.**
|
|
@@ -1863,7 +1865,7 @@ nothing about itself, compiles into no document, and publishes no page, so the
|
|
|
1863
1865
|
| `path` | the walk | Where the file sits inside the emitting package's asset directory. |
|
|
1864
1866
|
| `attribution` | provenance | The person holding the rights, to whom attribution is legally due. |
|
|
1865
1867
|
| `source` | provenance | Where it came from — a URL, or a sentence. |
|
|
1866
|
-
| `ai` | provenance | Whether the file is machine-generated — strictly the YAML boolean `true` or `false`; any other value is a finding, never a stringified record.
|
|
1868
|
+
| `ai` | provenance | Whether the file is machine-generated — strictly the YAML boolean `true` or `false`; any other value is a finding, never a stringified record. Required: a record that leaves it out is a finding, never read as `false`. |
|
|
1867
1869
|
| `license` | provenance | The licence it is used under — an SPDX identifier, or terms. |
|
|
1868
1870
|
| `notes` | provenance | Anything else a person reading the attribution needs. |
|
|
1869
1871
|
| `role` | provenance | What the picture is for — `portrait`, `emblem`, `banner`, `plate` or `map`. Absent for an ordinary picture; a value outside that set is a finding. The `image` type only — a value on an `icon` address is a finding and is dropped. |
|
|
@@ -1874,17 +1876,21 @@ Every field is present on every record, blank where nothing states one. A fixed
|
|
|
1874
1876
|
shape is what lets a consumer read `asset.license` without first asking whether
|
|
1875
1877
|
the package happened to record one.
|
|
1876
1878
|
|
|
1877
|
-
**`path` is
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
|
1883
|
-
|
|
|
1884
|
-
|
|
|
1885
|
-
|
|
|
1886
|
-
|
|
1887
|
-
|
|
1879
|
+
**`path` is the source path.** The record is emitted by the package holding
|
|
1880
|
+
the bytes, so the path is where the file sits in that package's repository. The
|
|
1881
|
+
book stages from it; Foundry and the website serve the **deployed layout**, the
|
|
1882
|
+
type root and the basename alone:
|
|
1883
|
+
|
|
1884
|
+
| Consumer | `thalorna-none-image-thorn` resolves to |
|
|
1885
|
+
| -------- | ----------------------------------------------------------- |
|
|
1886
|
+
| Web | `<cdn base>/thalorna/images/thorn.webp` |
|
|
1887
|
+
| Book | `<asset base>/thalorna/images/beings/characters/thorn.webp` |
|
|
1888
|
+
| Foundry | `modules/thalorna/images/thorn.webp` |
|
|
1889
|
+
|
|
1890
|
+
Foundry and the website serve a picture under `icons/` or `images/` flat, by
|
|
1891
|
+
type root and basename — see [what a package ships](#what-a-package-ships);
|
|
1892
|
+
every other file keeps its path under `assets/`, and the local and book forms
|
|
1893
|
+
keep the repository's arrangement. Foundry paths use the **Foundry package id**. The two values are both `thalorna`
|
|
1888
1894
|
for this image; `harnensemble` content uses `harn-ensemble` in its Foundry paths.
|
|
1889
1895
|
|
|
1890
1896
|
##### What a role declares
|
|
@@ -1987,19 +1993,131 @@ One address resolves its record in this order:
|
|
|
1987
1993
|
2. Otherwise the nearest `provenance.yaml`, searching the file's own directory
|
|
1988
1994
|
and then its ancestors, **stopping at the type root**. A record above the root
|
|
1989
1995
|
would speak for trees it says nothing about.
|
|
1990
|
-
3. Otherwise nothing, and
|
|
1991
|
-
|
|
1996
|
+
3. Otherwise nothing, and that is a **finding at the file**: every asset is
|
|
1997
|
+
covered by a record, because the stage decides from the record whether the
|
|
1998
|
+
file ships.
|
|
1999
|
+
|
|
2000
|
+
Resolution is per file. One directory routinely holds files its own
|
|
2001
|
+
`provenance.yaml` covers beside files a sidecar answers for, and each is
|
|
2002
|
+
resolved on its own.
|
|
1992
2003
|
|
|
1993
2004
|
**The nearest record is the whole answer.** It is never merged with the one
|
|
1994
2005
|
above it, so a record states every key it means to claim — which is what lets a
|
|
1995
2006
|
reader open one file and know what the tree beneath it carries, instead of
|
|
1996
2007
|
resolving a chain by hand.
|
|
1997
2008
|
|
|
1998
|
-
That is also why `attribution` and `
|
|
1999
|
-
|
|
2000
|
-
record stating
|
|
2001
|
-
|
|
2002
|
-
|
|
2009
|
+
That is also why `attribution`, `license` and `ai` are **required in every
|
|
2010
|
+
record**: a key left out is not inherited from above, it is simply absent, so a
|
|
2011
|
+
record stating none leaves every file it covers with no rights holder, no terms,
|
|
2012
|
+
or no statement of whether a machine made it. Omitting one is a finding at the
|
|
2013
|
+
record, which an empty file is held to as well. An unstated `ai` is never read as
|
|
2014
|
+
`false`. `source`, `notes` and `role` stay optional, because a blank is a
|
|
2015
|
+
truthful answer for each of them.
|
|
2016
|
+
|
|
2017
|
+
##### What a package ships
|
|
2018
|
+
|
|
2019
|
+
The repository keeps every picture; the Foundry package carries a filtered copy.
|
|
2020
|
+
`package-build assets` stages a file from `assets/icons/` or `assets/images/`
|
|
2021
|
+
only when its resolved record says `ai: false` **and** its `license` is one of:
|
|
2022
|
+
|
|
2023
|
+
| Licence | Spelled |
|
|
2024
|
+
| ---------------------------- | ---------------------------- |
|
|
2025
|
+
| Public domain | `LicenseRef-PublicDomain` |
|
|
2026
|
+
| Creative Commons Zero | `CC0-1.0`, or `CC0` |
|
|
2027
|
+
| Creative Commons Attribution | `CC-BY-3.0`, `CC-BY-4.0` |
|
|
2028
|
+
| Attribution-ShareAlike | `CC-BY-SA-4.0` |
|
|
2029
|
+
| Noun Project Pro | `LicenseRef-NounProject-Pro` |
|
|
2030
|
+
|
|
2031
|
+
The spelling is matched exactly; nothing is normalised. A package adds licences
|
|
2032
|
+
to this list with `packageBuild.shippingLicenses`, and ships under the list
|
|
2033
|
+
together with its additions. `ai: true` is never shipped, whatever the
|
|
2034
|
+
additions say. Audio is not filtered
|
|
2035
|
+
and not laid out flat — a package stages it with an ordinary
|
|
2036
|
+
`packageBuild.assets` row.
|
|
2037
|
+
|
|
2038
|
+
**The deployed layout is flat.** A shipped picture sits at the package root as
|
|
2039
|
+
`icons/<basename>` or `images/<basename>`, whatever directories hold it in the
|
|
2040
|
+
repository. The root is the type and the basename is the shortcode with its
|
|
2041
|
+
extension, so the path follows from the address alone, and a root's shortcodes
|
|
2042
|
+
being one namespace is what keeps the flattening collision-free. The same
|
|
2043
|
+
layout serves Foundry (`modules/thalorna/images/thorn.webp`), the website's CDN
|
|
2044
|
+
(`<cdn base>/thalorna/images/thorn.webp`), and every location a world copies
|
|
2045
|
+
the pictures to.
|
|
2046
|
+
|
|
2047
|
+
**The package's asset set**, `asset-sets/default.json` under the package
|
|
2048
|
+
root, lists what it ships. The name is fixed, so a world always finds the
|
|
2049
|
+
package's own set there:
|
|
2050
|
+
|
|
2051
|
+
```json
|
|
2052
|
+
{
|
|
2053
|
+
"name": "Thalorna — shipped art",
|
|
2054
|
+
"package": "thalorna",
|
|
2055
|
+
"assets": [
|
|
2056
|
+
{
|
|
2057
|
+
"address": "thalorna-none-icon-anvil",
|
|
2058
|
+
"source": "../icons/anvil.svg",
|
|
2059
|
+
"target": "icons/anvil.svg",
|
|
2060
|
+
"size": 412
|
|
2061
|
+
}
|
|
2062
|
+
]
|
|
2063
|
+
}
|
|
2064
|
+
```
|
|
2065
|
+
|
|
2066
|
+
| Key | What it says |
|
|
2067
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2068
|
+
| `name` | The set's display name: the package's manifest title with ` — shipped art` appended, or the content package without a title. |
|
|
2069
|
+
| `package` | The content package — the addresses' first segment, not the Foundry id. |
|
|
2070
|
+
| `assets` | One entry per file, in `target` order. |
|
|
2071
|
+
| `address` | The file's canonical address. |
|
|
2072
|
+
| `target` | The file's path relative to the location's root, in the deployed layout, extension included. |
|
|
2073
|
+
| `source` | Where the file is fetched from, resolved against the URL of the set file holding it — `new URL(source, setUrl)`. The package's own set sits in `asset-sets/`, so its `source` is `../<target>`. |
|
|
2074
|
+
| `size` | The staged file's size in bytes. |
|
|
2075
|
+
|
|
2076
|
+
`target` is never the record's source `path`. Each release attaches
|
|
2077
|
+
`icons.zip` and `images.zip` beside the package archive: one picture root of
|
|
2078
|
+
the stage each, with `asset-sets/default.json`, for a GM placing the files by
|
|
2079
|
+
hand.
|
|
2080
|
+
|
|
2081
|
+
##### Every reference states its address
|
|
2082
|
+
|
|
2083
|
+
A compiled document names a picture by its Foundry path, and beside each
|
|
2084
|
+
reference it states the picture's canonical address, so a world can rebuild the
|
|
2085
|
+
path for wherever it keeps the file:
|
|
2086
|
+
|
|
2087
|
+
- **HTML.** Each `<img>` whose `src` names an asset carries
|
|
2088
|
+
`data-asset="<address>"`.
|
|
2089
|
+
- **Path fields.** The address sits in `flags.<system>.assets` on the document
|
|
2090
|
+
holding the field, as nested objects mirroring the field's path — never a
|
|
2091
|
+
dotted key, which Foundry's `expandObject` splits. `<system>` is the system
|
|
2092
|
+
the document is compiled for, its `_stats.systemId`.
|
|
2093
|
+
|
|
2094
|
+
| Document | Flag on | Keys |
|
|
2095
|
+
| ------------------------------------------- | ---------------------- | ------------- |
|
|
2096
|
+
| Actor, Item, ActiveEffect, Macro, RollTable | the document | `img` |
|
|
2097
|
+
| Actor token art | `prototypeToken.flags` | `texture.src` |
|
|
2098
|
+
| Table result | the result | `img` |
|
|
2099
|
+
| Journal image page | the page | `src` |
|
|
2100
|
+
|
|
2101
|
+
```json
|
|
2102
|
+
"img": "modules/thalorna/icons/anvil.svg",
|
|
2103
|
+
"flags": { "sohl": { "assets": { "img": "thalorna-none-icon-anvil" } } },
|
|
2104
|
+
"prototypeToken": {
|
|
2105
|
+
"texture": { "src": "modules/thalorna/icons/anvil.svg" },
|
|
2106
|
+
"flags": { "sohl": { "assets": { "texture": { "src": "thalorna-none-icon-anvil" } } } }
|
|
2107
|
+
}
|
|
2108
|
+
```
|
|
2109
|
+
|
|
2110
|
+
Embedded documents — an actor's items and effects, a journal's pages, an
|
|
2111
|
+
Adventure's contents — each carry their own. A path or an `<img>` naming no
|
|
2112
|
+
asset is left as it is.
|
|
2113
|
+
|
|
2114
|
+
**A picture the package does not ship draws a stand-in.** A reference to an
|
|
2115
|
+
excluded picture compiles to its package's own stand-in,
|
|
2116
|
+
`<package>-none-icon-missing` or `<package>-none-image-missing` — a picture
|
|
2117
|
+
under `icons/` or `images/` named `missing`, human-made and licensed to ship —
|
|
2118
|
+
and keeps the excluded picture's address in its flag or `data-asset`. A
|
|
2119
|
+
package that leaves out a picture a note references and ships no stand-in of
|
|
2120
|
+
that type fails the build with an error at the referencing note.
|
|
2003
2121
|
|
|
2004
2122
|
##### A declared replacement resolves first
|
|
2005
2123
|
|
|
@@ -4419,18 +4537,19 @@ a propertied or senatorial order supplies nearly everyone who holds them, it is
|
|
|
4419
4537
|
**Democracy**.
|
|
4420
4538
|
|
|
4421
4539
|
The distinction is not academic: it is the difference between a ladder whose top
|
|
4422
|
-
rungs are a class one is born or bought into, and
|
|
4423
|
-
|
|
4424
|
-
**both** — the civic status (Citizen) and the standing in the governing
|
|
4425
|
-
it (Senator) — because a citizen is not a member of the Senate, and
|
|
4426
|
-
two is the commonest way to get a non-monarchy wrong.
|
|
4540
|
+
rungs are a class one is born or bought into, and a body whose highest posts are
|
|
4541
|
+
offices one is voted into and then vacates. A republic's ladder therefore carries
|
|
4542
|
+
**both** standings — the civic status (Citizen) and the standing in the governing
|
|
4543
|
+
body above it (Senator) — because a citizen is not a member of the Senate, and
|
|
4544
|
+
conflating the two is the commonest way to get a non-monarchy wrong. The
|
|
4545
|
+
magistracies held for a term are offices, not rungs.
|
|
4427
4546
|
|
|
4428
4547
|
**Rank**
|
|
4429
4548
|
Definition of a level within the organization (e.g., Priest, Layperson, Member, Gang Leader, Master, etc.)
|
|
4430
4549
|
|
|
4431
4550
|
| Property | Required | Values | Description |
|
|
4432
4551
|
| ------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
4433
|
-
| `level` | yes | integer | The ranking within the affiliation,
|
|
4552
|
+
| `level` | yes | integer | The ranking within the affiliation: 0 those it excludes (expelled, excommunicated), 1 its ordinary members, higher values above them |
|
|
4434
4553
|
| `title` | yes | string | Title associated with the Rank |
|
|
4435
4554
|
| `description` | yes | string | Description of the Rank |
|
|
4436
4555
|
| `lore` | no | `Address` | The standing this rank _is_ — a `lore` note of subType `law`, shared with every other body that confers the same thing |
|
|
@@ -4451,11 +4570,37 @@ moving two lines renames the standing. A body that confers two titles at one
|
|
|
4451
4570
|
standing writes them as one rung — `title: Knight or Dame` — or gives each its
|
|
4452
4571
|
own level.
|
|
4453
4572
|
|
|
4573
|
+
**Every ladder has rung 0 and rung 1.** Level 1 is the ordinary member, and level
|
|
4574
|
+
0 names those the body excludes. A body that can name its members can name those
|
|
4575
|
+
it shuts out, whether or not it ever does, so an affiliation whose
|
|
4576
|
+
`governance.ranks` lacks either is an **error** at the `ranks` key, as is one
|
|
4577
|
+
with no ladder at all. The level-0 title is the body's own word for the excluded
|
|
4578
|
+
— Outlaw, Apostate, Níðing — never a generic "Excluded".
|
|
4579
|
+
|
|
4580
|
+
Those two rungs are the minimum. Add rungs above level 1 only where the body
|
|
4581
|
+
truly has them, generally no more than five. **A rung is a standing, what a member
|
|
4582
|
+
is; an office is a post, what a member does.** Leadership is normally an office
|
|
4583
|
+
held by the top rank rather than a rank above it, and a rank never falls when an
|
|
4584
|
+
office is left: a bishop stays a bishop after being Presiding Bishop.
|
|
4585
|
+
|
|
4586
|
+
```yaml
|
|
4587
|
+
data:
|
|
4588
|
+
governance:
|
|
4589
|
+
ranks:
|
|
4590
|
+
- { level: 0, title: Apostate, description: Put out of the communion. }
|
|
4591
|
+
- { level: 1, title: Layman, description: A baptised member of the faith. }
|
|
4592
|
+
- { level: 2, title: Priest, description: Ordained to serve a parish. }
|
|
4593
|
+
- { level: 3, title: Bishop, description: Consecrated to oversee a diocese. }
|
|
4594
|
+
offices:
|
|
4595
|
+
Presiding Bishop: Chairs the synod of bishops for a term.
|
|
4596
|
+
```
|
|
4597
|
+
|
|
4454
4598
|
```yaml
|
|
4455
4599
|
data:
|
|
4456
4600
|
governance:
|
|
4457
4601
|
ranks:
|
|
4458
4602
|
- { level: 0, title: Vrystrith, description: Kinless and owed nothing. }
|
|
4603
|
+
- { level: 1, title: Kinsman, description: Born or adopted into a household. }
|
|
4459
4604
|
- { level: 4, title: Fródrád, description: A householder whose word carries. }
|
|
4460
4605
|
- level: 5
|
|
4461
4606
|
title: Hárár
|
|
@@ -4491,7 +4636,7 @@ rank names the standing, and the standing says.
|
|
|
4491
4636
|
| `symbol` | `string` | Its emblem in words: a feather atop a golden scale, a chisel carving a star |
|
|
4492
4637
|
| `governance.model` | `GovernanceModel` | Type of government structure, if applicable |
|
|
4493
4638
|
| `governance.summary` | `string` | summary of the governance situation |
|
|
4494
|
-
| `governance.ranks` | `Rank[]` | The ranks
|
|
4639
|
+
| `governance.ranks` | `Rank[]` | The ranks the affiliation confers; level 0 and level 1 are required |
|
|
4495
4640
|
| `governance.ranks[].level` | `integer`, required | The rung's position on this body's ladder; a member's `rank` names it |
|
|
4496
4641
|
| `governance.ranks[].title` | `string`, required | What the standing is called |
|
|
4497
4642
|
| `governance.ranks[].description` | `string`, required | What the standing is |
|
|
@@ -4541,14 +4686,15 @@ the ranks and offices that exist, and a membership names which of them it holds.
|
|
|
4541
4686
|
**Rank is the organisation's, not the member's.** A bare `level: 4` on a
|
|
4542
4687
|
membership says nothing on its own; it means _Knight_ only because the polity
|
|
4543
4688
|
declared that rung. So the ladder is authored once, on the body that confers it,
|
|
4544
|
-
and a member's rank is an index into it. Level 0 is
|
|
4545
|
-
|
|
4546
|
-
recognises, and so
|
|
4689
|
+
and a member's rank is an index into it. Level 0 is the excluded — outlawed,
|
|
4690
|
+
expelled, excommunicated — which is a standing the organisation still
|
|
4691
|
+
recognises, and so every ladder defines it beside level 1.
|
|
4547
4692
|
|
|
4548
4693
|
**Offices are named, not ranked.** A Chancellor and a Marshal are both great
|
|
4549
4694
|
officers and neither is above the other, so an office is a key with a
|
|
4550
4695
|
description rather than a rung — and a being may hold an office at any rank, or a
|
|
4551
|
-
rank with no office at all.
|
|
4696
|
+
rank with no office at all. The head of a body is usually an office of this kind,
|
|
4697
|
+
held by a member of the top rank, who keeps that rank when the term ends.
|
|
4552
4698
|
|
|
4553
4699
|
An office may remain a string description or declare a `description` and a
|
|
4554
4700
|
`holders` list. Each holder names a `being` Address; optional `start` and `end`
|