@heroiclands/package-build 22.21.0 → 22.23.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 (152) hide show
  1. package/CHANGELOG.md +223 -0
  2. package/assets/styles/content-base.css +162 -0
  3. package/bin/content-commands.mjs +77 -17
  4. package/bin/package-build.mjs +15 -2
  5. package/ci/content-format-reference.mjs +68 -1
  6. package/config.mjs +26 -0
  7. package/content-config.mjs +153 -6
  8. package/docs/api.md +120 -89
  9. package/docs/authoring/assets.md +14 -0
  10. package/docs/authoring/dates-and-calendars.md +41 -3
  11. package/docs/authoring/frontmatter.md +2 -2
  12. package/docs/authoring/links-and-markup.md +325 -35
  13. package/docs/commands.md +12 -10
  14. package/docs/configuration.md +117 -14
  15. package/docs/content-format.md +4 -2
  16. package/docs/guides/book.md +5 -4
  17. package/docs/guides/reference-pages.md +15 -0
  18. package/docs/reference/format-details.md +545 -104
  19. package/docs/reference/note-types.md +30 -13
  20. package/engine/actor-compiler.mjs +23 -0
  21. package/engine/anchored-sections.mjs +35 -4
  22. package/engine/anchors.mjs +26 -30
  23. package/engine/art-fields.mjs +118 -0
  24. package/engine/art-slots.mjs +7 -1
  25. package/engine/asset-index.mjs +160 -10
  26. package/engine/asset-replacement.mjs +93 -0
  27. package/engine/asset-types.mjs +240 -0
  28. package/engine/base-compiler.mjs +139 -22
  29. package/engine/base-styles.mjs +149 -0
  30. package/engine/bundles.mjs +13 -2
  31. package/engine/compendiums.mjs +8 -1
  32. package/engine/content-blocks.mjs +410 -0
  33. package/engine/content-embeds.mjs +19 -0
  34. package/engine/content-figures.mjs +468 -0
  35. package/engine/content-footnotes.mjs +197 -0
  36. package/engine/content-format-check.mjs +15 -12
  37. package/engine/content-format.yaml +1 -1
  38. package/engine/content-images.mjs +126 -25
  39. package/engine/content-index.mjs +38 -12
  40. package/engine/content-links.mjs +14 -6
  41. package/engine/content-tables.mjs +45 -4
  42. package/engine/content-tasklists.mjs +189 -0
  43. package/engine/docs-checks.mjs +4 -4
  44. package/engine/extension-attributes.mjs +48 -2
  45. package/engine/field-reference.mjs +31 -10
  46. package/engine/field-spec.mjs +10 -8
  47. package/engine/foreign-catalog.mjs +143 -18
  48. package/engine/frontmatter-lint.mjs +120 -65
  49. package/engine/frontmatter.mjs +3 -3
  50. package/engine/heading-attributes.mjs +321 -0
  51. package/engine/helpers.mjs +109 -41
  52. package/engine/homepage.mjs +8 -8
  53. package/engine/index.mjs +3 -0
  54. package/engine/infobox-registry.mjs +4 -1
  55. package/engine/infobox-render.mjs +15 -3
  56. package/engine/infobox.mjs +129 -15
  57. package/engine/itinerary-scenes.mjs +4 -10
  58. package/engine/journals.mjs +294 -50
  59. package/engine/lore-events.mjs +197 -0
  60. package/engine/macros.mjs +1 -1
  61. package/engine/markdown-expressions.mjs +434 -59
  62. package/engine/note-dates.mjs +354 -12
  63. package/engine/note-frontmatter.mjs +7 -16
  64. package/engine/note-vocabulary.mjs +183 -42
  65. package/engine/page-lists.mjs +358 -0
  66. package/engine/pathnames.mjs +36 -0
  67. package/engine/pdf-build.mjs +431 -74
  68. package/engine/pdf-images.mjs +64 -0
  69. package/engine/pdf-render.mjs +563 -66
  70. package/engine/rank-ladder.mjs +198 -0
  71. package/engine/reckoning-markers.mjs +1 -1
  72. package/engine/retired-fields.mjs +8 -13
  73. package/engine/scenes.mjs +26 -7
  74. package/engine/site-build.mjs +166 -23
  75. package/engine/site-config.mjs +10 -1
  76. package/engine/site-index.mjs +90 -3
  77. package/engine/site-preview.mjs +20 -7
  78. package/engine/social-ties.mjs +52 -14
  79. package/engine/sql-tables.mjs +66 -9
  80. package/engine/standings.mjs +89 -6
  81. package/engine/system-block.mjs +56 -30
  82. package/engine/web-wikilinks.mjs +48 -1
  83. package/hm3/actors.mjs +8 -5
  84. package/manifest.mjs +43 -24
  85. package/package.json +1 -1
  86. package/packagebuild-metadata.jsonl +13 -13
  87. package/sohl/actors.mjs +22 -99
  88. package/sohl/item-fields.mjs +5 -5
  89. package/sohl/items.mjs +1 -1
  90. package/sohl/note-schemas.mjs +11 -21
  91. package/types/config.d.mts +9 -0
  92. package/types/content-config.d.mts +48 -1
  93. package/types/engine/actor-compiler.d.mts +11 -0
  94. package/types/engine/anchored-sections.d.mts +24 -0
  95. package/types/engine/anchors.d.mts +9 -4
  96. package/types/engine/art-fields.d.mts +64 -0
  97. package/types/engine/asset-index.d.mts +43 -0
  98. package/types/engine/asset-replacement.d.mts +22 -0
  99. package/types/engine/asset-types.d.mts +18 -0
  100. package/types/engine/base-compiler.d.mts +19 -17
  101. package/types/engine/base-styles.d.mts +93 -0
  102. package/types/engine/content-blocks.d.mts +87 -0
  103. package/types/engine/content-embeds.d.mts +16 -0
  104. package/types/engine/content-figures.d.mts +101 -0
  105. package/types/engine/content-footnotes.d.mts +80 -0
  106. package/types/engine/content-format-check.d.mts +8 -6
  107. package/types/engine/content-images.d.mts +78 -12
  108. package/types/engine/content-index.d.mts +8 -2
  109. package/types/engine/content-tables.d.mts +12 -4
  110. package/types/engine/content-tasklists.d.mts +72 -0
  111. package/types/engine/extension-attributes.d.mts +22 -1
  112. package/types/engine/field-reference.d.mts +21 -0
  113. package/types/engine/field-spec.d.mts +20 -16
  114. package/types/engine/foreign-catalog.d.mts +39 -0
  115. package/types/engine/frontmatter.d.mts +1 -1
  116. package/types/engine/heading-attributes.d.mts +179 -0
  117. package/types/engine/helpers.d.mts +29 -10
  118. package/types/engine/homepage.d.mts +7 -7
  119. package/types/engine/index.d.mts +1 -0
  120. package/types/engine/infobox-registry.d.mts +4 -1
  121. package/types/engine/infobox-render.d.mts +6 -1
  122. package/types/engine/infobox.d.mts +6 -0
  123. package/types/engine/journals.d.mts +41 -12
  124. package/types/engine/lore-events.d.mts +14 -0
  125. package/types/engine/markdown-expressions.d.mts +46 -2
  126. package/types/engine/note-dates.d.mts +64 -1
  127. package/types/engine/note-frontmatter.d.mts +1 -4
  128. package/types/engine/note-vocabulary.d.mts +82 -6
  129. package/types/engine/page-lists.d.mts +126 -0
  130. package/types/engine/pathnames.d.mts +26 -0
  131. package/types/engine/pdf-build.d.mts +28 -6
  132. package/types/engine/pdf-images.d.mts +52 -0
  133. package/types/engine/pdf-render.d.mts +84 -2
  134. package/types/engine/rank-ladder.d.mts +32 -0
  135. package/types/engine/retired-fields.d.mts +4 -6
  136. package/types/engine/site-build.d.mts +27 -4
  137. package/types/engine/site-config.d.mts +9 -0
  138. package/types/engine/site-index.d.mts +67 -1
  139. package/types/engine/site-preview.d.mts +1 -1
  140. package/types/engine/sql-tables.d.mts +31 -7
  141. package/types/engine/standings.d.mts +21 -3
  142. package/types/engine/system-block.d.mts +37 -14
  143. package/types/engine/web-wikilinks.d.mts +3 -1
  144. package/types/hm3/actors.d.mts +21 -3
  145. package/types/manifest.d.mts +19 -23
  146. package/types/sohl/actors.d.mts +6 -7
  147. package/engine/content-admonitions.mjs +0 -114
  148. package/engine/content-captions.mjs +0 -181
  149. package/engine/content-secrets.mjs +0 -112
  150. package/types/engine/content-admonitions.d.mts +0 -24
  151. package/types/engine/content-captions.d.mts +0 -45
  152. package/types/engine/content-secrets.d.mts +0 -17
@@ -0,0 +1,198 @@
1
+ /*
2
+ * This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
3
+ * Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
4
+ *
5
+ * This work is licensed under the GNU General Public License v3.0 (GPLv3).
6
+ * You may copy, modify, and distribute it under the terms of that license.
7
+ *
8
+ * For full terms, see the LICENSE.md file in the project root or visit:
9
+ * https://www.gnu.org/licenses/gpl-3.0.html
10
+ *
11
+ * SPDX-License-Identifier: GPL-3.0-or-later
12
+ */
13
+
14
+ /**
15
+ * What a **rung** of an affiliation's rank ladder has to state.
16
+ *
17
+ * A rung is a map of `level`, `title` and `description`, all three required,
18
+ * plus an optional `lore` Address for a standing that needs more said about it
19
+ * than a description can hold. Those three keys together are a complete
20
+ * statement of a rung; nothing further is needed for a ladder to be correct.
21
+ *
22
+ * **A level belongs to one rung.** The ladder is read by level, so two rungs
23
+ * claiming one level leave a member's `rank` answering to whichever is written
24
+ * first — a standing whose name changes when two lines swap places. A body that
25
+ * confers two titles at one standing writes them as one rung, or gives each its
26
+ * own level.
27
+ *
28
+ * **The severity is error**, because the ladder is what a being's `rank`
29
+ * indexes into: a rung missing its `title` makes a standing that resolves to
30
+ * nothing, and one missing its `description` ships a rung that says nothing on
31
+ * a sheet or a page.
32
+ *
33
+ * Two things are deliberately **not** checked here, because something else
34
+ * already does and a finding reported twice is a finding read once and fixed
35
+ * neither time:
36
+ *
37
+ * - **that `ranks` is a list at all** — the field declares `kind: "list"`, and
38
+ * the shape check names it;
39
+ * - **that `lore` resolves** — the reference checker reads every Address in the
40
+ * note, this one included.
41
+ *
42
+ * @module
43
+ */
44
+
45
+ import { positionOfFrontmatterPath } from "./diagnostics.mjs";
46
+
47
+ /** The keys a rung may carry. */
48
+ const RUNG_KEYS = Object.freeze(["level", "title", "description", "lore"]);
49
+
50
+ /** The keys a rung must carry a value for. */
51
+ const REQUIRED_TEXT = Object.freeze(["title", "description"]);
52
+
53
+ /**
54
+ * Whether a value is a YAML mapping rather than a list or a scalar.
55
+ *
56
+ * @param {unknown} value - What was authored.
57
+ * @returns {boolean} True for a mapping.
58
+ */
59
+ function mapping(value) {
60
+ return value !== null && typeof value === "object" && !Array.isArray(value);
61
+ }
62
+
63
+ /**
64
+ * Whether a value states a whole number.
65
+ *
66
+ * A quoted `"3"` counts, because YAML hands a quoted scalar back as a string
67
+ * and the ladder reads it as a number either way — `3.5` does not, because a
68
+ * rung sits on a rung and there is nothing between two of them.
69
+ *
70
+ * @param {unknown} value - The authored `level`.
71
+ * @returns {boolean} True when it is an integer.
72
+ */
73
+ function integer(value) {
74
+ if (typeof value === "number") return Number.isInteger(value);
75
+ if (typeof value !== "string" || value.trim() === "") return false;
76
+ return Number.isInteger(Number(value));
77
+ }
78
+
79
+ /**
80
+ * Whether a value states something rather than nothing.
81
+ *
82
+ * @param {unknown} value - The authored `title` or `description`.
83
+ * @returns {boolean} True for a non-empty string.
84
+ */
85
+ function stated(value) {
86
+ return typeof value === "string" && value.trim() !== "";
87
+ }
88
+
89
+ /**
90
+ * Check one affiliation's `data.governance.ranks` against the rung contract.
91
+ *
92
+ * Each problem is its own finding, so a rung missing all three of its required
93
+ * keys reports three rather than the first: a reader fixing a ladder wants the
94
+ * whole of it in one pass.
95
+ *
96
+ * **A finding is located by the node it is about**, through the frontmatter's
97
+ * own YAML tree — a present-but-wrong value points at that value, and an absent
98
+ * key points at the rung that should have carried it, since a key that was
99
+ * never written has no position of its own. Where even the rung cannot be
100
+ * located the position is dropped rather than guessed, and the finding names
101
+ * the file alone.
102
+ *
103
+ * @param {object} note - The note being checked.
104
+ * @param {string} note.file - Its path, as a diagnostic spells it.
105
+ * @param {string} [note.raw] - Its source, for the position.
106
+ * @param {object} [note.fm] - Its parsed frontmatter.
107
+ * @returns {Array<{file: string, line?: number, column?: number,
108
+ * severity: string, message: string}>} The findings, in authored order.
109
+ */
110
+ export function checkRankLadder(note) {
111
+ const ranks = note.fm?.data?.governance?.ranks;
112
+ // Absent is the ordinary case: most bodies confer no ranks at all. A value
113
+ // that is not a list is the shape check's finding, not this one's.
114
+ if (!Array.isArray(ranks)) return [];
115
+
116
+ const findings = [];
117
+ const base = ["data", "governance", "ranks"];
118
+
119
+ /**
120
+ * One finding, located at the first of the given paths that resolves.
121
+ *
122
+ * @param {Array<Array<string|number>>} paths - Candidates, most specific
123
+ * first.
124
+ * @param {string} message - What is wrong.
125
+ * @returns {void}
126
+ */
127
+ const at = (paths, message) => {
128
+ let position = {};
129
+ for (const path of paths) {
130
+ position = positionOfFrontmatterPath(note.raw ?? "", path);
131
+ if (position.line !== undefined) break;
132
+ }
133
+ findings.push({ file: note.file, ...position, severity: "error", message });
134
+ };
135
+
136
+ // Level → how the rung holding it is named, so a repeat can say which rung
137
+ // already holds the level rather than only that something does.
138
+ const held = new Map();
139
+
140
+ ranks.forEach((rung, index) => {
141
+ const rungPath = [...base, index];
142
+ // Named by its level where it states one, because a reader scanning a
143
+ // ladder of fifteen rungs knows them by level and not by position.
144
+ const which =
145
+ integer(rung?.level) ? `rank ${rung.level}` : `rank ${index + 1} of the ladder`;
146
+
147
+ if (!mapping(rung)) {
148
+ at([rungPath, base], `${which} must be a map of level, title and description`);
149
+ return;
150
+ }
151
+
152
+ for (const key of Object.keys(rung)) {
153
+ if (RUNG_KEYS.includes(key)) continue;
154
+ at(
155
+ [[...rungPath, key], rungPath],
156
+ `${which} has unknown key ${key}; a rung takes ${RUNG_KEYS.join(", ")}`,
157
+ );
158
+ }
159
+
160
+ if (!integer(rung.level)) {
161
+ at(
162
+ [[...rungPath, "level"], rungPath],
163
+ rung.level === undefined || rung.level === null ?
164
+ `${which} needs a level — the rung's position on this body's own ladder`
165
+ : `${which} has a level that is not a whole number`,
166
+ );
167
+ } else {
168
+ // A level belongs to one rung. Two rungs claiming it leave a
169
+ // member's `rank` answering to the one written first, so the
170
+ // ladder's own order decides what the standing is called and
171
+ // moving two lines renames it. Reported on the second rung,
172
+ // naming the first, because the first is the one a reader has to
173
+ // find to tell the two apart.
174
+ const level = Number(rung.level);
175
+ const named = stated(rung.title) ? JSON.stringify(rung.title) : which;
176
+ const first = held.get(level);
177
+ if (first === undefined) held.set(level, named);
178
+ else
179
+ at(
180
+ [[...rungPath, "level"], rungPath],
181
+ `rank ${level} is declared twice, as ${first} and ${named}; a level ` +
182
+ `is a rung's identity and a member's rank indexes into it, so ` +
183
+ `each rung states its own`,
184
+ );
185
+ }
186
+
187
+ for (const key of REQUIRED_TEXT) {
188
+ if (stated(rung[key])) continue;
189
+ at(
190
+ [[...rungPath, key], rungPath],
191
+ `${which} needs a ${key}: ` +
192
+ (key === "title" ? "what the standing is called" : "what the standing is"),
193
+ );
194
+ }
195
+ });
196
+
197
+ return findings;
198
+ }
@@ -54,7 +54,7 @@ export function resolveReckoningMarkers(index, daysPerYear) {
54
54
  1,
55
55
  daysPerYear,
56
56
  );
57
- const pkg = fm.package ?? note.package ?? index?.contentPackage;
57
+ const pkg = note.package ?? index?.contentPackage;
58
58
  const declared = [];
59
59
  for (const [position, era] of rows.entries()) {
60
60
  if (!era || typeof era.shortcode !== "string" || !era.shortcode || !fm.shortcode)
@@ -58,11 +58,9 @@
58
58
  * one of them in. Every note that carried one has been moved, across four
59
59
  * repositories, and this is the third step of that retirement.
60
60
  *
61
- * Refusing it matters more than refusing an ordinary dead key, because top
62
- * level is *deliberately open*: an unrecognised key there is passed through to
63
- * Hugo, so a stray `traits:` would not be ignored loudly but would arrive on
64
- * the page as a theme parameter, checked by nothing. The whole argument for
65
- * `data:` being closed is the argument for refusing this.
61
+ * Refusing it by name rather than as an unknown key is what names the home the
62
+ * facts have: the finding says which `data:` key each one becomes, which an
63
+ * author cannot read off a list of the keys the region declares.
66
64
  *
67
65
  * `sohl.traits` is a different field that shares the name — `projectilegear`
68
66
  * declares one, and the theme's gear sidebar reads it — so the refusal is
@@ -350,8 +348,7 @@ export function traitsRetiredMessage(file) {
350
348
  (file ? ` — ${file}` : "") +
351
349
  ". A being's own description belongs in the closed container the " +
352
350
  "content format declares, where `being` declares every one of its " +
353
- "keys; at the top level it was passed through to the page unchecked, " +
354
- "so a misspelling became a theme parameter rather than a finding. " +
351
+ "keys. " +
355
352
  "`gender`, `species`, `age` and `appearance.*` move unchanged; " +
356
353
  "`traits.birthday` becomes `data.born`; three reshape — " +
357
354
  "`traits.height.m` becomes `data.height` " +
@@ -526,12 +523,10 @@ export function legacyKeyMessage(block, field, file) {
526
523
  * What a note writing a field at the **top-level key `data:` gathered it off**
527
524
  * is told.
528
525
  *
529
- * {@link legacyKeyMessage}'s counterpart for the other retiring position. The
530
- * sweep
531
- * did not invent the facts `data:` holds — it collected them out of the note's
532
- * open top level — so `portrait:` beside `img:` is the *pre-`data:`* spelling
533
- * of `data.portrait`, and both are read for the same reason both in-block
534
- * spellings are: a package moves its corpus when it is ready, not on a flag day.
526
+ * {@link legacyKeyMessage}'s counterpart for the other retiring position.
527
+ * `portrait:` beside `img:` is the bare spelling of `data.portrait`, and both
528
+ * are read for the same reason both in-block spellings are: a package moves its
529
+ * corpus when it is ready, not on a flag day.
535
530
  *
536
531
  * It says nothing about which value is emitted, because that is not what an
537
532
  * author needs from it. The value is the same either way; what the finding
package/engine/scenes.mjs CHANGED
@@ -50,6 +50,10 @@ import fs from "fs";
50
50
  import path from "path";
51
51
  import log from "loglevel";
52
52
 
53
+ // The one reader of a system block's own properties, so a scene's effect
54
+ // references resolve from the same position the item compiler emits from.
55
+ import { blockProperty } from "./system-block.mjs";
56
+
53
57
  import {
54
58
  parseMarkdownFile,
55
59
  sohlField,
@@ -76,7 +80,7 @@ import { behaviorDocId, buildScene, isMapType, regionDocId } from "./map-notes.m
76
80
  import { buildItineraryScenes } from "./itinerary-scenes.mjs";
77
81
  import { emitDiagnostic } from "./diagnostics.mjs";
78
82
  import { rasterizeMapSvg } from "./map-raster.mjs";
79
- import { artPathname, artSlot } from "./art-fields.mjs";
83
+ import { artPathname, artSlot, pathnameRoles } from "./art-fields.mjs";
80
84
  import { buildExportedScene } from "./exported-scene.mjs";
81
85
 
82
86
  /**
@@ -235,7 +239,11 @@ export class Scenes extends BasePackCompiler {
235
239
  // `resolveNoteId(fm)` with no package, which falls back to the
236
240
  // ambient one. What remains unset is a file with no address.
237
241
  if (!fm || !fm.id) continue;
238
- if (fm.shortcode && Array.isArray(fm.effects) && fm.effects.length) {
242
+ // The Active Effects a region behaviour may point at, read from
243
+ // the block that carries them onto the document — the one position
244
+ // the format accepts for them.
245
+ const effects = blockProperty(fm, "sohl", "effects");
246
+ if (fm.shortcode && Array.isArray(effects) && effects.length) {
239
247
  effectsByAddress.set(`${fm.type}-${fm.shortcode}`, {
240
248
  id: fm.id,
241
249
  type: fm.type,
@@ -246,7 +254,7 @@ export class Scenes extends BasePackCompiler {
246
254
  // second router is a second answer to where the document
247
255
  // landed, resolved from the working directory.
248
256
  pack: this.#packRouter.resolveOrNull(fm, packForType(fm.type).docType),
249
- effects: fm.effects,
257
+ effects,
250
258
  });
251
259
  }
252
260
  if (!isMapType(fm.type)) continue;
@@ -387,11 +395,15 @@ export class Scenes extends BasePackCompiler {
387
395
  * @param {string} markdown - The converted body.
388
396
  * @param {string} entryId - The JournalEntry's id.
389
397
  * @param {string} name - The map's name (its lead page).
398
+ * @param {(address: string) => string|undefined} resolveRole - See
399
+ * {@link module:engine/journals.splitPages}; threaded through so an
400
+ * unanchored map figure's page key agrees with the journal this pass
401
+ * derives it against.
390
402
  * @returns {Map<string, string>} heading key → page id.
391
403
  */
392
- #pageIds(markdown, entryId, name) {
404
+ #pageIds(markdown, entryId, name, resolveRole) {
393
405
  const pageIds = new Map();
394
- splitPages(markdown, name).forEach((page) => {
406
+ splitPages(markdown, name, resolveRole).forEach((page) => {
395
407
  const id = journalPageId(entryId, page);
396
408
  if (page.anchorSlug) pageIds.set(page.anchorSlug, id);
397
409
  const slug = slugify(page.name);
@@ -431,6 +443,12 @@ export class Scenes extends BasePackCompiler {
431
443
  compileNote(fm, markdown) {
432
444
  const name = resolveName(fm);
433
445
  const hasBody = Boolean(String(markdown).trim());
446
+ // The markdown this pass reads has already had its embeds rewritten
447
+ // into ordinary images, each `src` the pathname `pathnameRoles` keys
448
+ // its roles by — see {@link module:engine/journals.Journals#buildEntry},
449
+ // which derives this same journal's pages the same way.
450
+ const roles = pathnameRoles(this.linkIndex);
451
+ const resolveRole = (pathname) => roles.get(pathname);
434
452
  // The same doc-entry id the journals pass derives, from the
435
453
  // shared `docEntryTypes` arrangement — so neither
436
454
  // pass has to read the other's output.
@@ -503,7 +521,8 @@ export class Scenes extends BasePackCompiler {
503
521
  hasBody ?
504
522
  this.#packRouter.resolve(fm, "JournalEntry")
505
523
  : this.#packRouter.defaultOf("JournalEntry"),
506
- pageIds: hasBody ? this.#pageIds(markdown, entryId, name) : new Map(),
524
+ pageIds:
525
+ hasBody ? this.#pageIds(markdown, entryId, name, resolveRole) : new Map(),
507
526
  knownActions: this.knownActions,
508
527
  warnings,
509
528
  ...this.#resolvers(this.index, this.effectsByAddress, fm.shortcode),
@@ -527,12 +546,12 @@ export class Scenes extends BasePackCompiler {
527
546
  name,
528
547
  markdown,
529
548
  leadName: name,
549
+ resolveRole,
530
550
  // As in the journals pass: an address resolves in the
531
551
  // pack that emits it, which is what makes the folder
532
552
  // materialise there too. The id spelling that used
533
553
  // to cross packs verbatim is retired.
534
554
  folder: this.folderResolver(authoredFolder, { isAddress: true }),
535
- flags: fm.flags,
536
555
  })
537
556
  : null;
538
557