@heroiclands/package-build 6.1.0 → 8.0.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.
- package/CHANGELOG.md +840 -0
- package/CONTENT.md +21 -1
- package/bin/content-build.mjs +105 -10
- package/bin/package-build.mjs +114 -1
- package/config.mjs +62 -3
- package/content-config.mjs +254 -22
- package/engine/base-compiler.mjs +25 -0
- package/engine/content-links.mjs +132 -27
- package/engine/diagnostics.mjs +61 -1
- package/engine/foreign-catalog.mjs +47 -0
- package/engine/generate.mjs +10 -5
- package/engine/helpers.mjs +38 -0
- package/engine/journals.mjs +8 -1
- package/engine/macros.mjs +2 -0
- package/engine/pack-config.mjs +143 -13
- package/engine/prose-lint.mjs +10 -2
- package/engine/scenes.mjs +2 -2
- package/engine/schema-check.mjs +332 -0
- package/engine/schema-extract.mjs +611 -0
- package/engine/web-wikilinks.mjs +13 -4
- package/engine/wikilink-syntax.mjs +25 -0
- package/engine/wikilinks.mjs +6 -3
- package/manifest.mjs +37 -2
- package/package.json +5 -3
- package/sohl/actors.mjs +23 -10
- package/sohl/item-fields.mjs +0 -35
- package/sohl/items.mjs +1 -1
- package/types/content-config.d.mts +14 -0
- package/types/engine/base-compiler.d.mts +18 -1
- package/types/engine/content-links.d.mts +11 -3
- package/types/engine/diagnostics.d.mts +33 -1
- package/types/engine/foreign-catalog.d.mts +15 -0
- package/types/engine/generate.d.mts +3 -2
- package/types/engine/helpers.d.mts +29 -3
- package/types/engine/journals.d.mts +7 -1
- package/types/engine/pack-config.d.mts +22 -0
- package/types/engine/prose-lint.d.mts +10 -2
- package/types/engine/schema-check.d.mts +176 -0
- package/types/engine/schema-extract.d.mts +61 -0
- package/types/engine/wikilink-syntax.d.mts +24 -0
- package/types/sohl/actors.d.mts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,845 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 8.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- d324b5a: Stop emitting five `system` fields no SoHL DataModel declares (#60).
|
|
8
|
+
|
|
9
|
+
The comparison this release adds was run against sohl's published schema at
|
|
10
|
+
0.8.2 and found five, on its first run:
|
|
11
|
+
|
|
12
|
+
| type | emitted, undeclared |
|
|
13
|
+
| ---------------- | -------------------------------------------------------- |
|
|
14
|
+
| `affliction` | `isTreated` |
|
|
15
|
+
| `trauma` | `isTreated`, `isBleeding` |
|
|
16
|
+
| `projectilegear` | `impactBase.overrideDice`, `impactBase.overrideModifier` |
|
|
17
|
+
|
|
18
|
+
Every one was discarded when the document was constructed, on every compiled
|
|
19
|
+
document, with nothing said — which is the whole of what #60 is about.
|
|
20
|
+
|
|
21
|
+
**Two of them were never storable.** `isTreated` and `isBleeding` are _derived_
|
|
22
|
+
on the logic classes: `AfflictionLogic.isTreated` is `treatmentDate != null`,
|
|
23
|
+
and `TraumaLogic.isBleeding` is `bloodLossAdvanceDurationBase != null`. So the
|
|
24
|
+
builder wrote a constant Foundry threw away while the field it is computed from
|
|
25
|
+
went unwritten — both directions of the same defect, on the same field. Nothing
|
|
26
|
+
replaces them: an untreated affliction is one whose `treatmentDate` is unset,
|
|
27
|
+
which is already the initial value.
|
|
28
|
+
|
|
29
|
+
**Three were authored fields that vanished.** `trauma.isTreated`,
|
|
30
|
+
`trauma.isBleeding` and the two projectile overrides carried a frontmatter
|
|
31
|
+
`name`, so a note could write them — and the value went nowhere.
|
|
32
|
+
|
|
33
|
+
**The projectile overrides are removed rather than reported upstream as missing
|
|
34
|
+
fields**, because nothing anywhere wants them: no DataModel declares them, no
|
|
35
|
+
logic class reads them, no localization key names them. A
|
|
36
|
+
launcher-versus-ammunition override may be worth having, but it would have to be
|
|
37
|
+
designed in the system first, and a content builder cannot be where it is
|
|
38
|
+
invented.
|
|
39
|
+
|
|
40
|
+
**Checked before removing, because three were authored.** Dropping an authored
|
|
41
|
+
field turns a note that writes it from a silent loss into an unknown-key error,
|
|
42
|
+
so all six content trees were searched first: `sohl`, `thalorna`, `kethira`,
|
|
43
|
+
`harnensemble`, `harnadventures` and `hm3` write none of the five.
|
|
44
|
+
|
|
45
|
+
**Verified.** Against sohl 0.8.2's published schema, `undeclared` falls from
|
|
46
|
+
five to zero. The twelve remaining findings are the advisory direction —
|
|
47
|
+
fields a subtype declares that no builder emits, `treatmentDate` among them —
|
|
48
|
+
and are reported rather than fatal.
|
|
49
|
+
|
|
50
|
+
**Bump**
|
|
51
|
+
|
|
52
|
+
_Major._ Three of the five were part of the authored frontmatter vocabulary, and
|
|
53
|
+
a note writing one is now an unknown-key error rather than a value silently
|
|
54
|
+
dropped. No content in this organisation writes them, but a consumer outside it
|
|
55
|
+
would have to delete the keys — and would find its documents unchanged, since
|
|
56
|
+
they never reached a saved document in the first place.
|
|
57
|
+
|
|
58
|
+
### Minor Changes
|
|
59
|
+
|
|
60
|
+
- d324b5a: Run the emitted-versus-declared field comparison in `content-build lint` (#60).
|
|
61
|
+
|
|
62
|
+
The comparison shipped in 7.0.0 with nothing calling it, because no system had
|
|
63
|
+
published its field sets yet. `sohl` now does, so this reads the artifact and
|
|
64
|
+
runs both directions.
|
|
65
|
+
|
|
66
|
+
**Which system, at which version, is already settled.** `stats.systemId` and
|
|
67
|
+
`stats.systemVersion` are derived rather than authored (#48) — a system package
|
|
68
|
+
is its own system, a module takes the one it requires, and the version is the
|
|
69
|
+
`compatibility.verified` it pins. So there is no second piece of configuration
|
|
70
|
+
to disagree with the first about whose schema to check against.
|
|
71
|
+
|
|
72
|
+
Two places to find it:
|
|
73
|
+
|
|
74
|
+
- **A system** reads its own `schema.json`, generated from its `src/`.
|
|
75
|
+
- **A module** reads the copy `content-build deps fetch` caches from the archive
|
|
76
|
+
of the version it pins — which is what makes the comparison happen at
|
|
77
|
+
`verified` rather than against whatever the system's `main` holds today.
|
|
78
|
+
|
|
79
|
+
**The fetch now keeps the schema.** Both fetch paths already unpacked the
|
|
80
|
+
dependency's archive, but only one kept the result: a download unzips into
|
|
81
|
+
`<cache>/package/` and leaves it, while `deps fetch --from` unzips into a
|
|
82
|
+
temporary directory and deletes it. A reader looking in the unpacked tree would
|
|
83
|
+
have found the schema for one and not the other — so it is copied to one known
|
|
84
|
+
place beside the extracted items instead.
|
|
85
|
+
|
|
86
|
+
**An absent schema is announced, not skipped in silence.** A system before its
|
|
87
|
+
first schema build, and a module pinning a version released before the artifact
|
|
88
|
+
existed, both have nothing to check against. That is not an error — but a check
|
|
89
|
+
that quietly does nothing is indistinguishable from one that passed, and this
|
|
90
|
+
issue exists because a defect went unnoticed for a release. So the run says so:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
No published schema for sohl@0.8.2, so emitted `system` fields are unchecked.
|
|
94
|
+
A system generates its own; a module gets one from `content-build deps fetch`.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Also lands the five fixes the comparison found.** They were pushed after
|
|
98
|
+
#116's merge and so were not part of it: `affliction` and `trauma` emitted
|
|
99
|
+
`isTreated`, `trauma` emitted `isBleeding`, and `projectilegear` emitted
|
|
100
|
+
`impactBase.overrideDice` and `impactBase.overrideModifier` — none of which any
|
|
101
|
+
DataModel declares. Without them this change would have turned `sohl`'s own
|
|
102
|
+
lint red on the defects it was written to find.
|
|
103
|
+
|
|
104
|
+
**Verified against both shapes.** Against `sohl`, the run reports zero errors
|
|
105
|
+
and twelve advisory warnings, and `lint` passes. Against `sohl-kethira-basic`,
|
|
106
|
+
whose pinned 0.8.2 archive predates the artifact, it announces the skip and
|
|
107
|
+
reports only that repository's pre-existing findings.
|
|
108
|
+
|
|
109
|
+
**Bump**
|
|
110
|
+
|
|
111
|
+
_Minor._ New reporting on an existing command, and a fetch that keeps one more
|
|
112
|
+
file. The error direction can fail a build that passed before — but only for a
|
|
113
|
+
package whose dependency publishes a schema, which no released version does yet.
|
|
114
|
+
- ee9a9a8: Compare a builder's emitted `system` fields against the receiving DataModel
|
|
115
|
+
(#60) — the comparison half.
|
|
116
|
+
|
|
117
|
+
Foundry discards an unknown `system` key when a document is constructed, and
|
|
118
|
+
says nothing: the value is absent at load while the build that wrote it reported
|
|
119
|
+
success. Both directions of that mismatch have already happened here, both
|
|
120
|
+
compiled clean, and both were found by set-subtracting compiled documents'
|
|
121
|
+
`system` keys against `defineSchema()` **by hand**.
|
|
122
|
+
|
|
123
|
+
**The emitted half needs neither compilation nor parsing.** `field-spec.mjs`
|
|
124
|
+
already makes the field list the only statement of the mapping — "the
|
|
125
|
+
declaration is the builder" — so every `system` path a type can emit is
|
|
126
|
+
`field.to`, known statically. Nothing compiles a document to find out.
|
|
127
|
+
|
|
128
|
+
**The declared half arrives as data, pinned to the declared version.** A system
|
|
129
|
+
publishes its field sets as an artifact and this reads it, the shape the link
|
|
130
|
+
manifest already uses for addresses. Against `compatibility.verified`, never the
|
|
131
|
+
system's `main`: `affiliation.subType` _is_ defined on sohl `main` and simply
|
|
132
|
+
unreleased, while `sohl-kethira-basic` pins `0.8.2` — so a check against `main`
|
|
133
|
+
passes and the field still evaporates for all 21 of its deities.
|
|
134
|
+
|
|
135
|
+
**`own` and `inherited` are recorded apart, and the two directions read
|
|
136
|
+
different sets.** A subtype's schema spreads its parent's, so `notes`, `docHtml`
|
|
137
|
+
and the rest land on every subtype; they are the system's own runtime concerns
|
|
138
|
+
and no content builder is expected to emit them.
|
|
139
|
+
|
|
140
|
+
| direction | read against | severity |
|
|
141
|
+
| --------------------- | ------------------------------------------------------------------ | -------- |
|
|
142
|
+
| emitted, not declared | `own` ∪ `inherited` — the field must exist somewhere | error |
|
|
143
|
+
| declared, not emitted | `own` only — what the subtype adds is what its builder answers for | report |
|
|
144
|
+
|
|
145
|
+
Collapsing them would report every inherited field on every type: a wall of
|
|
146
|
+
findings that are all correct and none actionable.
|
|
147
|
+
|
|
148
|
+
**A false positive the real schema caught before this shipped.** Run against
|
|
149
|
+
sohl's actual `mysticalability`, the _declared, not emitted_ direction reported
|
|
150
|
+
`charges.value` and `charges.max` on a type that populates them correctly — the
|
|
151
|
+
builder writes `charges` as a whole object and never names the leaves beneath
|
|
152
|
+
it. A declared path is now covered when the builder emits any ancestor. The
|
|
153
|
+
first real schema tried produced two false findings, which is exactly the kind
|
|
154
|
+
that teaches people to ignore a report.
|
|
155
|
+
|
|
156
|
+
**Verified against the real declarations.** With sohl's `mysticalability`
|
|
157
|
+
schema transcribed from source, the comparison reports nothing; with #35's
|
|
158
|
+
`assocMysteryCode` reinstated, it reports exactly that field.
|
|
159
|
+
|
|
160
|
+
**What this does not do yet.** It does not read an artifact from disk, and
|
|
161
|
+
nothing runs it in a build — those wait on a system actually publishing its
|
|
162
|
+
schemas, which is sohl's half and a separate change. The comparison, the format
|
|
163
|
+
and both regression cases are pinned here so that half has something to satisfy.
|
|
164
|
+
|
|
165
|
+
**Bump**
|
|
166
|
+
|
|
167
|
+
_Minor._ New surface — `engine/schema-check.mjs` and its exports — and nothing
|
|
168
|
+
existing changes behaviour. No consumer runs the comparison until an artifact
|
|
169
|
+
exists to run it against.
|
|
170
|
+
- 14cd092: Add `package-build schema`, so a system publishes its DataModel field sets from
|
|
171
|
+
here rather than from its own copy of an extractor.
|
|
172
|
+
|
|
173
|
+
The consuming half of this contract shipped in 7.0.0: `content-build lint`
|
|
174
|
+
subtracts what a package's builders emit from what a document will actually
|
|
175
|
+
receive, because Foundry discards an unknown `system` key at construction and
|
|
176
|
+
says nothing about it. The producing half lived in the first system that needed
|
|
177
|
+
it, which meant the second system to need it would have copied 491 lines — and,
|
|
178
|
+
worse, would have copied a hardcoded `SCHEMA_ARTIFACT_VERSION`, a constant this
|
|
179
|
+
package owns. Two producers stamping a third repository's constant by hand is
|
|
180
|
+
the drift worth removing before it happens rather than after: the version is now
|
|
181
|
+
imported by the producer, not restated.
|
|
182
|
+
|
|
183
|
+
**Why here and not in each system.** A DataModel's schema is only introspectable
|
|
184
|
+
inside Foundry — `defineSchema()` returns field classes that do not exist in
|
|
185
|
+
Node — so the field sets have to be read out of the source as an AST.
|
|
186
|
+
TypeScript's parser reads plain JavaScript too, and this package already pins
|
|
187
|
+
that compiler for `coverage.mjs`. Putting the reader here means a
|
|
188
|
+
JavaScript-only system does not acquire a TypeScript pin merely to describe its
|
|
189
|
+
own data models.
|
|
190
|
+
|
|
191
|
+
**Declared, because the two layouts in use disagree.**
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
packageBuild:
|
|
195
|
+
schema:
|
|
196
|
+
Item: { from: module/data/item-models.js, registry: itemModels }
|
|
197
|
+
Actor: { from: module/data/actor-models.js, registry: actorModels }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
One system keeps both registries in a single configuration module; the other
|
|
201
|
+
keeps one per file. Neither layout is more correct, and a convention guessing
|
|
202
|
+
between them would fail by reading _nothing_ rather than by complaining — which
|
|
203
|
+
is the worst failure available here, since an empty schema passes every check.
|
|
204
|
+
A registry that maps nothing is refused for the same reason.
|
|
205
|
+
|
|
206
|
+
**Four spellings of inheritance, all followed.** `...Super.defineSchema()`,
|
|
207
|
+
`...super.defineSchema()`, `Object.assign(super.defineSchema(), {…})`, and a
|
|
208
|
+
subclass with no `defineSchema()` at all. The last is a real and complete
|
|
209
|
+
declaration — `class MiscGearModel extends GearModel {}` — and reading it as
|
|
210
|
+
"declares nothing" would make every field of a whole subtype look undeclared.
|
|
211
|
+
`SchemaField` nesting is recorded as dotted paths whether written bare or as
|
|
212
|
+
`fields.SchemaField`, since both spellings are in use.
|
|
213
|
+
|
|
214
|
+
**A schema with nothing to compare against now says so.** The emitted side of
|
|
215
|
+
the check is the `fields:` of `itemBuilders`, so a package whose compendium
|
|
216
|
+
content is committed JSON rather than built from field declarations has an empty
|
|
217
|
+
one — and every field the system declares would have been reported as unemitted.
|
|
218
|
+
That is hundreds of findings whose only content is that the package does not
|
|
219
|
+
build documents that way, which is not news and not a defect. It is announced
|
|
220
|
+
once instead, for the same reason the absent-schema case is: a check that quietly
|
|
221
|
+
does nothing reads exactly like one that passed. The moment a builder declares
|
|
222
|
+
`fields:`, the comparison starts running on its own.
|
|
223
|
+
|
|
224
|
+
**Bump**
|
|
225
|
+
|
|
226
|
+
_Minor._ A new command, a new optional configuration key, and a `lint` that
|
|
227
|
+
reports strictly less than before.
|
|
228
|
+
|
|
229
|
+
## 7.0.0
|
|
230
|
+
|
|
231
|
+
### Major Changes
|
|
232
|
+
|
|
233
|
+
- a136429: Separate declaring a system from requiring one, and stamp `_stats` per pack
|
|
234
|
+
(#48).
|
|
235
|
+
|
|
236
|
+
A module shipping content for two systems could not say so. The only place to
|
|
237
|
+
state a system version was `relationships.systems`, and that list is a
|
|
238
|
+
**restriction**: Foundry's `supportsSystem` drops a package from any world whose
|
|
239
|
+
system it does not name. So the two needs were in direct conflict — name your
|
|
240
|
+
systems and become unloadable elsewhere, or stay loadable and stamp nothing.
|
|
241
|
+
|
|
242
|
+
**`harn-ensemble` is the case, and it is live.** It declares an `actors-hm3`
|
|
243
|
+
pack and an `actors-sohl` pack, and resolves to:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{ "statsSystemId": null, "statsSystemVersion": null }
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Nothing stamped, on content that was certainly built against `hm3 1.6.3` and
|
|
250
|
+
`sohl 0.8.2`. It takes that path deliberately, because declaring the two systems
|
|
251
|
+
would hide the module from every other world — including the ones that want only
|
|
252
|
+
its system-neutral journals pack.
|
|
253
|
+
|
|
254
|
+
**The split.**
|
|
255
|
+
|
|
256
|
+
| | describes | gates |
|
|
257
|
+
| ----------------- | -------------------------------------------- | --------------------------- |
|
|
258
|
+
| `systems:` | which systems this package can stamp against | **nothing** |
|
|
259
|
+
| `requiresSystem:` | — | where the package will load |
|
|
260
|
+
|
|
261
|
+
Naming a system under `systems:` restricts nothing. `requiresSystem` is separate
|
|
262
|
+
and optional, and emits the `relationships.systems` entry Foundry reads —
|
|
263
|
+
_reusing_ the declaration rather than restating it, because
|
|
264
|
+
`stats.systemVersion` sat at `0.6.0` for four releases when a transcription was
|
|
265
|
+
free to disagree with what it copied.
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
systems:
|
|
269
|
+
hm3: { compatibility: { minimum: "1.6.3", verified: "1.6.3" } }
|
|
270
|
+
sohl: { compatibility: { minimum: "0.8.2", verified: "0.8.2" } }
|
|
271
|
+
requiresSystem: null # optional; omitted, the package loads anywhere
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**A pack's `system:` now selects what stamps its documents.** `_stats` was one
|
|
275
|
+
memoised block for the whole package, so every document in every pack was
|
|
276
|
+
stamped identically. It is per pack now: `statsForPack()` resolves the pack's
|
|
277
|
+
declared system through `systems:`, and `BasePackCompiler` exposes it as
|
|
278
|
+
`this.stats`, memoised per instance — one pass, one pack, one system.
|
|
279
|
+
|
|
280
|
+
**`systemId` travels with `systemVersion`.** They are one decision, so where one
|
|
281
|
+
is omitted both are. Stamping a per-pack version against a package-wide id would
|
|
282
|
+
emit `systemId: sohl, systemVersion: 1.6.3` on HM3 documents — a plausible lie,
|
|
283
|
+
which is worse than the missing value #43 fixed, because nothing about it looks
|
|
284
|
+
wrong.
|
|
285
|
+
|
|
286
|
+
**A name that resolves to nothing is an error.** A pack's `system:` must name a
|
|
287
|
+
declared system; `requiresSystem` must too; and with a gate set, a pack naming a
|
|
288
|
+
_different_ system is refused outright — Foundry would hide the whole package
|
|
289
|
+
from any world that pack could have appeared in, so it would ship and be
|
|
290
|
+
unreachable.
|
|
291
|
+
|
|
292
|
+
**Nothing that ships today moves.** All six consumers were resolved before and
|
|
293
|
+
after; none declares a `systems:` block yet, so every pack still falls through to
|
|
294
|
+
the package-wide block it used before:
|
|
295
|
+
|
|
296
|
+
| package | `systems:` | `requiresSystem` | stamps | packs unchanged |
|
|
297
|
+
| -------------------- | ---------: | ---------------- | -------------- | --------------- |
|
|
298
|
+
| `sohl` | 0 | — | `sohl / 0.8.2` | yes |
|
|
299
|
+
| `sohl-thalorna` | 0 | — | `sohl / 0.8.2` | yes |
|
|
300
|
+
| `sohl-kethira-basic` | 0 | — | `sohl / 0.8.2` | yes |
|
|
301
|
+
| `harn-ensemble` | 0 | — | `— / —` | yes |
|
|
302
|
+
| `harn-adventures` | 0 | — | `— / —` | yes |
|
|
303
|
+
| `hm3` | 0 | — | `hm3 / 1.6.3` | yes |
|
|
304
|
+
|
|
305
|
+
**`stats.systemId` and `stats.systemVersion` are refused outright.** Authoring a
|
|
306
|
+
derived value is an error rather than an override, which is the rule this
|
|
307
|
+
configuration already applies elsewhere — and the reason is the same one that let
|
|
308
|
+
`stats.systemVersion` sit at `0.6.0` for four releases: a transcribed copy is
|
|
309
|
+
free to drift from what it copied, and nothing reads a stamped `_stats` until
|
|
310
|
+
something migrates on it. The refusal names the key, its line, and what supplies
|
|
311
|
+
it now.
|
|
312
|
+
|
|
313
|
+
The value still has to reach the validator from the loader, which is the half
|
|
314
|
+
that may read the adjacent `package.json`. It travels under a **symbol**, so the
|
|
315
|
+
channel is not a second, forgeable spelling of the key just refused: a symbol
|
|
316
|
+
cannot be written in YAML and does not appear in `Object.keys`.
|
|
317
|
+
|
|
318
|
+
**`relationships.systems` keeps working, and still answers.** It carries
|
|
319
|
+
`itemCatalog` — a separate concern this split does not replace — so a repository
|
|
320
|
+
using it would otherwise have to restate its compatibility under `systems:`
|
|
321
|
+
purely to keep stamping, which is the duplication the change exists to remove. A
|
|
322
|
+
lone relationship is a declaration as much as a gate, so it derives `systemId`
|
|
323
|
+
too. Several have no single answer and get none.
|
|
324
|
+
|
|
325
|
+
**Every consumer was migrated in the same change.** One line each:
|
|
326
|
+
|
|
327
|
+
```diff
|
|
328
|
+
stats:
|
|
329
|
+
- systemId: sohl
|
|
330
|
+
lastModifiedBy: sohlbuilder00000
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`sohl` and `hm3` are systems and are their own system by construction;
|
|
334
|
+
`sohl-thalorna` and `sohl-kethira-basic` derive it from the single system
|
|
335
|
+
relationship they already declare; `harn-ensemble` and `harn-adventures` declared
|
|
336
|
+
none and were already clean.
|
|
337
|
+
|
|
338
|
+
**Bump**
|
|
339
|
+
|
|
340
|
+
_Major._ `stats.systemId` and `stats.systemVersion` were accepted and are now
|
|
341
|
+
refused, so a configuration that resolved before can fail — which is the
|
|
342
|
+
definition this repository uses. Every HeroicLands consumer is migrated in
|
|
343
|
+
lockstep and verified to stamp exactly what it stamped before, but a consumer
|
|
344
|
+
outside that set must delete the two keys.
|
|
345
|
+
|
|
346
|
+
Part of #57, the third of its three keys that both describe and gate. #56 is
|
|
347
|
+
done, #49 shipped in 6.2.0, and this is #48.
|
|
348
|
+
|
|
349
|
+
### Minor Changes
|
|
350
|
+
|
|
351
|
+
- db40b24: Address another package's landing without naming a host (#87).
|
|
352
|
+
|
|
353
|
+
A link from one package's page to another package's landing had no form but a
|
|
354
|
+
hardcoded absolute URL, and the homepage link check (#54) exempted one — a
|
|
355
|
+
finding whose fix does not exist is noise. `sohl-kethira-basic`'s homepage writes
|
|
356
|
+
`https://www.heroiclands.org/sohl/` twice.
|
|
357
|
+
|
|
358
|
+
**The premise the exemption rested on is true, and does not lead where it looked
|
|
359
|
+
like it led.** A landing is in no link manifest: it compiles to no document and
|
|
360
|
+
is entered in no index. The reading that follows is that nothing can resolve it.
|
|
361
|
+
But a landing's address is not a _note's_ address — it is the **package's**, and
|
|
362
|
+
`PACKAGE_BASE` has recorded where each package is served all along. Consulting it
|
|
363
|
+
walks no tree, reads no manifest and builds no index, which is exactly why the
|
|
364
|
+
mechanism survives the fence: `kethira` and `harnadventures` publish a homepage
|
|
365
|
+
and nothing else, and that mode never walks a content tree.
|
|
366
|
+
|
|
367
|
+
**So the authored form is the absolute URL with the host struck off.** `/sohl/`
|
|
368
|
+
in a body, or `href: /sohl/` in a card. Both were already accepted here — nothing
|
|
369
|
+
had ever named one as the form to use, which is the whole of what was missing.
|
|
370
|
+
|
|
371
|
+
| Field | Cross-package landing | Why |
|
|
372
|
+
| ------- | --------------------- | ----------------------------------------- |
|
|
373
|
+
| body | `[SoHL](/sohl/)` | emitted verbatim, resolved by the browser |
|
|
374
|
+
| `href:` | `/sohl/` | "already resolved, used verbatim" |
|
|
375
|
+
| `url:` | **not expressible** | package-relative by construction |
|
|
376
|
+
|
|
377
|
+
`url:` cannot leave its own package, so a `url:` naming another package's landing
|
|
378
|
+
is now reported with the field as the fix rather than a path — previously it was
|
|
379
|
+
told to "write `/`", which is nonsense arrived at by taking the path after the
|
|
380
|
+
prefix when there is nothing after the prefix.
|
|
381
|
+
|
|
382
|
+
**The base comes from the roster, not from the prefix.** The finding names
|
|
383
|
+
`PACKAGE_BASE[pkg]`, so a package the roster relocates (`/setting/thalorna/`) is
|
|
384
|
+
addressed where it actually is. That is the property the manifest enforces
|
|
385
|
+
everywhere else — the address published is the address emitted — reaching these
|
|
386
|
+
links for the first time.
|
|
387
|
+
|
|
388
|
+
**Roster for landings only.** Widening the package set the other rules read would
|
|
389
|
+
have them offer manifest-based advice about packages no manifest is vendored for.
|
|
390
|
+
An in-site path naming no known package is still left alone: several surfaces a
|
|
391
|
+
landing routes to are built by other tools, and this build does not hold the set
|
|
392
|
+
of published pages.
|
|
393
|
+
|
|
394
|
+
**Measured across every homepage authored today.** All five were run before and
|
|
395
|
+
after, each under its own `package-build.config.yaml`:
|
|
396
|
+
|
|
397
|
+
| Package | Before | After | |
|
|
398
|
+
| ------------------------- | ------ | ----- | ----------------------- |
|
|
399
|
+
| `sohl-kethira-basic` | 0 | **2** | the two the issue names |
|
|
400
|
+
| `sohl-thalorna` | 0 | 0 | |
|
|
401
|
+
| `harn-ensemble` | 0 | 0 | |
|
|
402
|
+
| `harn-adventures` | 0 | 0 | |
|
|
403
|
+
| `HarnMaster-3-FoundryVTT` | 0 | 0 | |
|
|
404
|
+
|
|
405
|
+
Kethira vendors no manifest at all — it is homepage-only — so it is also the
|
|
406
|
+
proof that the roster reaches where an index does not. Applying the fix the
|
|
407
|
+
finding names takes it back to 0.
|
|
408
|
+
|
|
409
|
+
**Sequencing, stated because it decides the bump.** These are hard errors:
|
|
410
|
+
`severity: "error"`, counted into `failures`, `exitCode 1`. By this repository's
|
|
411
|
+
own rule — a new hard error is breaking only if it fails a previously-passing
|
|
412
|
+
consumer — this is minor **only once kethira's two lines are converted**, and
|
|
413
|
+
major if it ships before them. The conversion does not wait on this release:
|
|
414
|
+
`/sohl/` already passes under the current published version, verified, so it can
|
|
415
|
+
land in `sohl-kethira-basic` immediately and independently. It must land first.
|
|
416
|
+
|
|
417
|
+
**Bump**
|
|
418
|
+
|
|
419
|
+
_Minor, not patch, and not major._ Not patch: a check that previously passed a
|
|
420
|
+
page can now fail it. Not major, on the condition above — with kethira converted,
|
|
421
|
+
every homepage authored today passes before and after, and no export, option or
|
|
422
|
+
emitted document changes shape.
|
|
423
|
+
- 35bde43: Locate a configuration error in the file it was written in (#95).
|
|
424
|
+
|
|
425
|
+
Every check across `content-config.mjs` and `config.mjs` — 81 of them — reports
|
|
426
|
+
through one `fail()`, which named the offending key's **dotted path** and
|
|
427
|
+
nothing else. That is a good description and a bad locator: nothing in the line
|
|
428
|
+
is a path an editor can open or a CI annotator can resolve, in a file that runs
|
|
429
|
+
to 400 lines with fourteen sibling entries under `sections:` alone, several of
|
|
430
|
+
them flow-mapped onto one line.
|
|
431
|
+
|
|
432
|
+
The path now rides on the error as a `field`, and the loader that read the file
|
|
433
|
+
resolves it — through `positionOfYamlPath`, the same locator `manifest`'s
|
|
434
|
+
`packFolders` findings already use — so all 81 come out in the
|
|
435
|
+
`file:line:column: severity: message` form every other finding uses, path first:
|
|
436
|
+
|
|
437
|
+
```text
|
|
438
|
+
package-build.config.yaml:382:64: error: package-build config: `site.sections.being.descrption` is not a recognized option (expected one of: title, banner, description).
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Located at the boundary, not at the check.** `content-config.mjs` is the leaf
|
|
442
|
+
an `.mjs` configuration imports and performs no I/O, so it attaches the path and
|
|
443
|
+
`configFromData` — which knows the file — formats it. `config.mjs`'s pure
|
|
444
|
+
`resolvePackageBuildConfig` is unchanged for the same reason;
|
|
445
|
+
`loadPackageBuildConfig` is where its findings are located.
|
|
446
|
+
|
|
447
|
+
**Positions are dropped, never guessed.** A required key the file never declared
|
|
448
|
+
has no node of its own, so the position names the **mapping it belongs in**, one
|
|
449
|
+
level up and no further — that entry is a real node and the one the reader must
|
|
450
|
+
edit. A missing _top-level_ key has nothing above it but the document, and an
|
|
451
|
+
`.mjs` configuration has no YAML to resolve a path against at all (parsing
|
|
452
|
+
JavaScript as YAML would resolve some paths to lines that mean nothing). Both
|
|
453
|
+
report `package-build.config.yaml: error: …` — the file, without a line.
|
|
454
|
+
|
|
455
|
+
Both command lines print a located failure unprefixed, since `package-build: `
|
|
456
|
+
and `loglevel`'s `[timestamp] [ERROR]:` occupy exactly the position a parser
|
|
457
|
+
reads the path from.
|
|
458
|
+
|
|
459
|
+
**Additive.** A valid configuration resolves exactly as before; only the text of
|
|
460
|
+
a rejection changes, and it keeps the body it had. `positionOfYamlPath` gains an
|
|
461
|
+
optional `{ key: true }` — report where a key is _declared_ rather than where
|
|
462
|
+
its value sits, which is what a message naming a field wants — and
|
|
463
|
+
`engine/diagnostics` gains `yamlKeyPath`, the one translation between a dotted
|
|
464
|
+
path and a YAML key path.
|
|
465
|
+
- b7ad450: Compile actors without an Item pack of this package's own (#49).
|
|
466
|
+
|
|
467
|
+
The actors pass threw unless the package declared at least one pack of type
|
|
468
|
+
`Item`. An Item pack is system-bound by construction — Foundry requires `system`
|
|
469
|
+
on Item packs and on few others — so the guard asked a deliberately
|
|
470
|
+
system-agnostic module to declare the very thing it exists not to depend on.
|
|
471
|
+
|
|
472
|
+
**This is not hypothetical, and it is not a future case.** `harn-ensemble`
|
|
473
|
+
declares two Actor packs and no Item pack at all in its
|
|
474
|
+
`package-build.config.yaml` today. Run against that configuration, the compiler
|
|
475
|
+
does not start:
|
|
476
|
+
|
|
477
|
+
```text
|
|
478
|
+
packs declared : actors-hm3(Actor), actors-sohl(Actor)
|
|
479
|
+
itemPackJsonDirs : []
|
|
480
|
+
Actors : THREW — Actors compiler requires `itemsSourceDirs` …
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Its 2,512 beings resolve their embedded items — `skill:awar`, `attribute:str`,
|
|
484
|
+
`weapongear:…` — against the `sohl` and `hm3` catalogues through
|
|
485
|
+
`foreignSourceDirs`. The optional mechanism is the one that matters there; the
|
|
486
|
+
mandatory one had nothing to contribute.
|
|
487
|
+
|
|
488
|
+
**The guard did not test what it claimed.** It counted _declared directories_,
|
|
489
|
+
not resolvable items. An Item pack containing no documents satisfied it, while a
|
|
490
|
+
being naming an item nothing defines still failed later — so it neither
|
|
491
|
+
prevented the failure it named nor reported it where it happened. And its
|
|
492
|
+
remedy, "declare at least one pack of type `Item`", is the opposite of the fix
|
|
493
|
+
for a system-agnostic package.
|
|
494
|
+
|
|
495
|
+
**The condition actually cared about was already checked, at the site of the
|
|
496
|
+
mistake.** `resolveEmbedded` reports each unresolved `(type, shortcode)` by
|
|
497
|
+
name, with the being as context, and counts it — and those counts aggregate into
|
|
498
|
+
`totalErrors`, so a package genuinely missing an item still fails the build:
|
|
499
|
+
|
|
500
|
+
```text
|
|
501
|
+
Bandit: no predefined item for "skill:awar"
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
That is the same line #43 and the frontmatter-lint work drew: a structural
|
|
505
|
+
precondition that is cheap to state is not the condition you care about, and
|
|
506
|
+
reporting where the mistake is beats refusing to start.
|
|
507
|
+
|
|
508
|
+
**Nothing tightens.** `itemsSourceDirs` defaults to `[]` and is otherwise
|
|
509
|
+
unchanged; `foreignSourceDirs` is untouched; `itemPackJsonDirs` already returned
|
|
510
|
+
an empty list for a repository with no Item packs, and only the constructor
|
|
511
|
+
rejected it. A package that declares Item packs behaves exactly as before — the
|
|
512
|
+
1,647 existing tests pass unchanged, with four added for the empty case and for
|
|
513
|
+
the point-of-use error that replaces the guard.
|
|
514
|
+
|
|
515
|
+
**Bump**
|
|
516
|
+
|
|
517
|
+
_Minor, not patch._ No consumer that compiled before fails now — the change only
|
|
518
|
+
removes a refusal — but a configuration that was rejected is now supported, which
|
|
519
|
+
is new surface rather than a repair to existing surface. Not major for the same
|
|
520
|
+
reason: nothing that was accepted stops being accepted.
|
|
521
|
+
|
|
522
|
+
Part of #57, which is the same defect in three places: a key that both describes
|
|
523
|
+
and gates, so the legitimate case cannot be expressed. Here the gate is removed
|
|
524
|
+
and the description — which Item packs this package ships — is left saying only
|
|
525
|
+
that.
|
|
526
|
+
|
|
527
|
+
### Patch Changes
|
|
528
|
+
|
|
529
|
+
- 8aab711: Bump `glob` from 11.1.0 to 13.0.6.
|
|
530
|
+
|
|
531
|
+
**Both majors are changes to glob's command-line program, not its library.**
|
|
532
|
+
|
|
533
|
+
- **12** removed the unsafe `--shell` option, keeping it only on shells where it
|
|
534
|
+
can be implemented safely (the remediation for GHSA-5j98-mcp5-4vw2, whose
|
|
535
|
+
mitigation 11.1 had introduced).
|
|
536
|
+
- **13** moved the CLI out to a separate `glob-bin` package.
|
|
537
|
+
|
|
538
|
+
This repository never invokes that program. The single use of the library is
|
|
539
|
+
`globSync(patterns, { cwd, absolute: true })` in `readMatching`
|
|
540
|
+
(`bin/package-build.mjs`), which is untouched across both majors — no workflow,
|
|
541
|
+
hook, or script calls `glob` from a shell, so the removed binary is not a
|
|
542
|
+
dependency this package had.
|
|
543
|
+
|
|
544
|
+
**Verified rather than assumed.** `globSync` was exercised under 13.0.6 with the
|
|
545
|
+
options `readMatching` actually passes, returning the same absolute paths; the
|
|
546
|
+
repository's 1,642 tests pass unchanged.
|
|
547
|
+
|
|
548
|
+
**Bump**
|
|
549
|
+
|
|
550
|
+
_Patch, not minor._ Nothing in this package's surface moves, and the majors are
|
|
551
|
+
the library's own. Worth noting for a consumer only in one case: a repository
|
|
552
|
+
that installed `glob` transitively through this package and relied on
|
|
553
|
+
`node_modules/.bin/glob` being present must now depend on `glob-bin` directly.
|
|
554
|
+
That was never a supported edge of this package, and no HeroicLands repository
|
|
555
|
+
does it.
|
|
556
|
+
- 8e0778e: Bump `markdown-it` from 14.3.0 to 15.0.0.
|
|
557
|
+
|
|
558
|
+
This package's entire use of the library is `markdownit({ html: true })` and
|
|
559
|
+
`md.render(body)`, at three call sites — `engine/helpers.mjs`,
|
|
560
|
+
`engine/journals.mjs` and `sohl/actors.mjs`. Nothing overrides a renderer rule,
|
|
561
|
+
installs a plugin, or reaches into the parser.
|
|
562
|
+
|
|
563
|
+
**Every breaking change in 15.0.0 lands outside that surface.**
|
|
564
|
+
|
|
565
|
+
| Breaking change | Why it does not reach here |
|
|
566
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
567
|
+
| `linkify-it` → v6: no fuzzy links, no auth check, CJK link termination | `linkify` defaults to `false` and is never enabled, so the linkifier never runs |
|
|
568
|
+
| Package-internal subpath exports (`markdown-it/lib/*`) removed | Only the package root is imported |
|
|
569
|
+
| `validateLink`/`normalizeLink`/`normalizeLinkText` moved to prototype | None is read, assigned, or overridden |
|
|
570
|
+
| `StateBlock#ddIndent` removed | No plugin is installed, `markdown-it-deflist` included |
|
|
571
|
+
| Root now resolves to prebuilt `dist/` rather than raw sources | Import is by package name; the resolved path was never depended on |
|
|
572
|
+
|
|
573
|
+
**Verified rather than assumed.** The same corpus was rendered through 14.3.0
|
|
574
|
+
and 15.0.0 and diffed byte-for-byte, covering exactly the constructs the release
|
|
575
|
+
touched — bare URLs, autolinks, email-shaped text, reference links and their
|
|
576
|
+
definitions, CJK adjacent to a URL, hard line breaks, raw HTML, tables, nested
|
|
577
|
+
lists, and both fenced and indented code. Output is identical. The repository's
|
|
578
|
+
1,642 tests pass unchanged.
|
|
579
|
+
|
|
580
|
+
**Bump**
|
|
581
|
+
|
|
582
|
+
_Patch, not minor._ No export, option, or emitted document changes shape, and no
|
|
583
|
+
behaviour a consumer can observe moves. The version is a major on the library's
|
|
584
|
+
own surface, none of which this package presents onward.
|
|
585
|
+
- 6318722: Add `.github/dependabot.yml`. This repository had none, so nothing proposed a
|
|
586
|
+
dependency update — not npm, not GitHub Actions.
|
|
587
|
+
|
|
588
|
+
That matters more here than in a consumer: this is the toolchain every other
|
|
589
|
+
HeroicLands package builds with, and it is published to npm, so its dependency
|
|
590
|
+
tree reaches every consumer's build and everyone who installs it. The
|
|
591
|
+
consuming repositories are already covered; this producer was the one link in
|
|
592
|
+
the chain nothing watched.
|
|
593
|
+
|
|
594
|
+
`@foundryvtt/foundryvtt-cli` and `classic-level` are each an ungrouped
|
|
595
|
+
single-package entry, ordered ahead of the housekeeping catch-all, for every
|
|
596
|
+
update type including minor and patch. Both sit directly on the compendium
|
|
597
|
+
pack pipeline, and a version bump that silently changes how a pack compiles is
|
|
598
|
+
exactly the failure this repository's byte-level output comparisons exist to
|
|
599
|
+
catch — grouping either into a housekeeping pull request would hide which
|
|
600
|
+
dependency caused a regression.
|
|
601
|
+
|
|
602
|
+
No `ignore` entries: nothing here has a known-bad range to record.
|
|
603
|
+
`typescript` is a direct dependency, but `build.yml`'s "Declaration emit" step
|
|
604
|
+
already gates a breaking bump on every pull request.
|
|
605
|
+
|
|
606
|
+
Closes #100.
|
|
607
|
+
- cd32ea1: Render `[[target|]]` as the target's name on the web, as the packs already do
|
|
608
|
+
(#113).
|
|
609
|
+
|
|
610
|
+
A wikilink with an explicit but empty label produced an **empty anchor** —
|
|
611
|
+
`[](/rules/sohl-shock/)`, a link with no clickable text — silently, through
|
|
612
|
+
every site build:
|
|
613
|
+
|
|
614
|
+
```text
|
|
615
|
+
[[doc-shock]] => [Shock](/rules/sohl-shock/)
|
|
616
|
+
[[doc-shock|]] => [](/rules/sohl-shock/) ← before
|
|
617
|
+
[[doc-shock|]] => [Shock](/rules/sohl-shock/) ← after
|
|
618
|
+
[[#sec|]] => [](#sec) ← before
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**The two resolvers had drawn the same line in two places, differently.**
|
|
622
|
+
`parseWikilink` distinguishes "no label" from "empty label" deliberately, and
|
|
623
|
+
its docstring says so — _"`null` and `""` differ: an author may write
|
|
624
|
+
`[[x|]]`"_. The packs resolver honoured that by testing falsiness. The web
|
|
625
|
+
resolver used `??`, which falls through on `null` only, so `""` survived to the
|
|
626
|
+
output at three sites: the same-page anchor, the resolved link, and the
|
|
627
|
+
unresolved link.
|
|
628
|
+
|
|
629
|
+
That is the third instance of exactly the drift `wikilink-syntax.mjs` was
|
|
630
|
+
created to stop — its module docstring opens on the two copies of the link
|
|
631
|
+
parser having already diverged. The parse was centralised; the _interpretation
|
|
632
|
+
of the parsed parts_ was not, and it drifted in the one case the docstring
|
|
633
|
+
names.
|
|
634
|
+
|
|
635
|
+
**So the reading is stated once.** `authoredLabel()` joins the syntax module and
|
|
636
|
+
both resolvers consult it, rather than each deciding what an empty label means.
|
|
637
|
+
`labelled` is untouched and still separates `[[x]]` from `[[x|]]`, which is what
|
|
638
|
+
#1409 actually depends on — an unlabelled `[[Shock State]]` still shows the
|
|
639
|
+
author's own prose rather than the canonical name.
|
|
640
|
+
|
|
641
|
+
**Why it is worth a release rather than waiting.** `[[x|]]` becomes load-bearing
|
|
642
|
+
under the four-segment address grammar (#59): the pipe is what distinguishes an
|
|
643
|
+
address lookup from an alias lookup, so `[[target|]]` is the canonical way to
|
|
644
|
+
write an address that displays its target's name — and the planned migration
|
|
645
|
+
rewrites every authored address link into that form. Converting the corpus into
|
|
646
|
+
a form that renders an empty anchor would be a corpus-wide regression, so this
|
|
647
|
+
has to land first.
|
|
648
|
+
|
|
649
|
+
**Bump**
|
|
650
|
+
|
|
651
|
+
_Patch._ A defect fix with no new surface. `authoredLabel` is exported because
|
|
652
|
+
both resolvers import it, not as a feature for consumers; no option, address, or
|
|
653
|
+
emitted document changes shape, and no link that renders correctly today renders
|
|
654
|
+
differently after.
|
|
655
|
+
- bc2c5c8: Bump `@types/node` in the lockfile for development tooling maintenance.
|
|
656
|
+
- 1a4d882: Hold `typescript` at major 6 in Dependabot, because TypeScript 7 removes the
|
|
657
|
+
compiler API `coverage.mjs` parses with.
|
|
658
|
+
|
|
659
|
+
Dependabot proposed 6.0.3 → 7.0.2 (#105). It cannot be taken.
|
|
660
|
+
|
|
661
|
+
**TypeScript 7 is the native port, and its npm package no longer ships the
|
|
662
|
+
JavaScript compiler API.** The `"."` export resolves to `lib/version.cjs`, whose
|
|
663
|
+
entire surface is `version` and `versionMajorMinor`.
|
|
664
|
+
|
|
665
|
+
`coverage.mjs` uses that API as a _parser_, not as a compiler: it reads
|
|
666
|
+
localization keys out of a consumer's `src/**/*.{ts,mjs}` by walking a real AST,
|
|
667
|
+
deliberately down one path so JavaScript and TypeScript cannot drift. Under 7.0.2
|
|
668
|
+
the AST **vocabulary** survives behind `typescript/unstable/ast` — `ScriptTarget`
|
|
669
|
+
and every `isX` guard the scan uses — but the three things that actually drive it
|
|
670
|
+
exist nowhere in the JS surface:
|
|
671
|
+
|
|
672
|
+
| Needed by `coverage.mjs` | In 7.0.2 |
|
|
673
|
+
| ------------------------------------------------------------- | ------------------------------------------ |
|
|
674
|
+
| `createSourceFile` | **missing** — only a factory of that name¹ |
|
|
675
|
+
| `forEachChild` | **missing** |
|
|
676
|
+
| `flattenDiagnosticMessageText` | **missing** |
|
|
677
|
+
| `ScriptTarget`, `isStringLiteral`, `isPropertyDeclaration`, … | present, behind `unstable/ast` |
|
|
678
|
+
|
|
679
|
+
¹ 7's `createSourceFile` assembles a SourceFile from statements already parsed.
|
|
680
|
+
It is not a parser.
|
|
681
|
+
|
|
682
|
+
**The gate this file predicted would catch it did not.** The previous comment in
|
|
683
|
+
`dependabot.yml` recorded no ignore entry on the reasoning that `build.yml`'s
|
|
684
|
+
"Declaration emit" step already guards a breaking `typescript` bump. That step
|
|
685
|
+
passes clean under 7.0.2 — `tsc` still emits every `.d.mts`. What failed was
|
|
686
|
+
`npm test`: 11 failures in `tests/coverage.test.ts`, all
|
|
687
|
+
`TypeError: Cannot read properties of undefined (reading 'Latest')`. A dependency
|
|
688
|
+
can be load-bearing in two unrelated ways at once, and the file named only one of
|
|
689
|
+
them. That correction is now written where the wrong prediction was.
|
|
690
|
+
|
|
691
|
+
**Why an ignore rather than a migration.** Parsing in 7 lives in the Go binary,
|
|
692
|
+
reachable only through `unstable/sync`'s Project/Program API. Adopting it would
|
|
693
|
+
put a subprocess and a virtual filesystem inside a module whose stated contract
|
|
694
|
+
is "everything here is pure — source text in, references or findings out", in
|
|
695
|
+
exchange for an API whose own export path says `unstable`. Acorn is not a
|
|
696
|
+
substitute either: the default scan glob is `src/**/*.{ts,mjs}`, and the largest
|
|
697
|
+
consumer's sources are TypeScript.
|
|
698
|
+
|
|
699
|
+
That migration is worth doing when the API stabilises. It is not a dependency
|
|
700
|
+
bump, and it should not arrive as one.
|
|
701
|
+
|
|
702
|
+
**Scope of the hold.** Majors only — minor and patch releases within 6 still
|
|
703
|
+
arrive on the weekly schedule. The entry names the two conditions that lift it:
|
|
704
|
+
a stable in-process parse entry point, or a `coverage.mjs` that no longer needs
|
|
705
|
+
one.
|
|
706
|
+
|
|
707
|
+
**Bump**
|
|
708
|
+
|
|
709
|
+
_Patch._ Nothing shipped changes. `.github/dependabot.yml` is this repository's
|
|
710
|
+
own automation and sits outside `files`; the `typescript` range in
|
|
711
|
+
`package.json` is untouched, because `^6.0.3` already excludes 7 — the entry
|
|
712
|
+
stops the pull request being reopened, it does not change what resolves.
|
|
713
|
+
- f3167c3: Read the changesets action's `pr-number` output, so the step that marks the
|
|
714
|
+
Version Packages pull request stops being dead code (#84).
|
|
715
|
+
|
|
716
|
+
`release.yml` pins `changesets/action@v2` but read v1's `pullRequestNumber`. An
|
|
717
|
+
unset output evaluates to the empty string rather than erroring, so the guard was
|
|
718
|
+
`'' != ''` — always false. The step has never run once.
|
|
719
|
+
|
|
720
|
+
**The failure was silent by construction, which is why it survived three
|
|
721
|
+
releases.** A misspelled output does not fail a workflow; it disappears. `v3.4.0`,
|
|
722
|
+
`v4.0.0` and `v5.0.0` were all cut with this step skipped, and every run reported
|
|
723
|
+
green.
|
|
724
|
+
|
|
725
|
+
**Only one of the three names actually moved.** Checked against v2's own
|
|
726
|
+
`action.yml` rather than against the assumption that v2 kebab-cased everything:
|
|
727
|
+
|
|
728
|
+
| v1 | v2 | Used here |
|
|
729
|
+
| ------------------- | -------------------- | ------------------------------------- |
|
|
730
|
+
| `pullRequestNumber` | `pr-number` | yes — the two lines this change fixes |
|
|
731
|
+
| `publishedPackages` | `published-packages` | no |
|
|
732
|
+
| `hasChangesets` | `has-changesets` | no |
|
|
733
|
+
| `published` | `published` | yes — **unchanged**, and left alone |
|
|
734
|
+
|
|
735
|
+
`published` is the one name v2 kept. A blanket kebab-case sweep of this file —
|
|
736
|
+
the obvious reading of "rename the v1 outputs" — would have broken the one step
|
|
737
|
+
that was working.
|
|
738
|
+
|
|
739
|
+
**Bracket notation, not `outputs.pr-number`.** A hyphen is the subtraction
|
|
740
|
+
operator in an Actions expression, so the dotted form parses as
|
|
741
|
+
`outputs.pr - number`: a second silent-ish defect sitting directly behind the
|
|
742
|
+
first. `steps.changesets.outputs['pr-number']` is the form that means what it
|
|
743
|
+
reads as.
|
|
744
|
+
|
|
745
|
+
**What this repairs.** The Version Packages pull request is opened by
|
|
746
|
+
`GITHUB_TOKEN`, and GitHub deliberately starts no workflow runs from that token,
|
|
747
|
+
so the required `Changeset declared` context never reports on it. This step
|
|
748
|
+
exists to post that status. With it inert, every Version Packages pull request
|
|
749
|
+
has needed the check waived or force-merged by hand.
|
|
750
|
+
|
|
751
|
+
**Not yet demonstrated running, and stated rather than glossed.** The acceptance
|
|
752
|
+
criterion asks for a run where the step is not `skipped`, and that can only
|
|
753
|
+
happen on `main`, on the next release that opens a Version Packages pull request
|
|
754
|
+
— this change cannot produce one from a branch. The expression is verified by
|
|
755
|
+
parsing the workflow and by v2's manifest; the live proof arrives with the next
|
|
756
|
+
bump.
|
|
757
|
+
|
|
758
|
+
**Sibling repositories are fixed individually, not swept from here.** The same
|
|
759
|
+
defect is open on `harn-ensemble` (#13) and `HarnMaster-3-FoundryVTT` (#427),
|
|
760
|
+
where it is more severe — there the misspelling gates the release itself, and no
|
|
761
|
+
release is ever cut. Each repository owns its own copy of `release.yml`; this one
|
|
762
|
+
is not a reusable workflow. Turning it into one is a real improvement and a
|
|
763
|
+
separate change, and folding it into a two-line fix would put a shared release
|
|
764
|
+
pipeline into production on the back of a typo correction.
|
|
765
|
+
|
|
766
|
+
**Bump**
|
|
767
|
+
|
|
768
|
+
_Patch, not minor._ Nothing this package exports, emits, or documents for a
|
|
769
|
+
consumer changes. The file is this repository's own release plumbing, and it is
|
|
770
|
+
not shipped: `.github` is outside `files`.
|
|
771
|
+
- 2bdc792: Make `lint:markdown` pass, and run it in CI (#92).
|
|
772
|
+
|
|
773
|
+
This repository provides `content-build markdown` and was the one repository that
|
|
774
|
+
never ran it. The script existed, failed, and was executed by nothing — no
|
|
775
|
+
aggregate `lint` script, and no workflow naming it.
|
|
776
|
+
|
|
777
|
+
**The finding count in the issue is wrong, and the correction changes the
|
|
778
|
+
decision.** 117 is what `npx markdownlint-cli2 CHANGELOG-content-build.md`
|
|
779
|
+
reports — markdownlint's _default_ rule set, which this toolchain deliberately
|
|
780
|
+
turns off (`default: false`, then each rule enabled by name). Under the rules the
|
|
781
|
+
repository actually uses, `npm run lint:markdown` reports **three**: two
|
|
782
|
+
`MD001` heading skips and one `MD034` bare URL. The issue's "fix it — 117
|
|
783
|
+
mechanical findings" and "exclude it — 117 is too many to fix" were both
|
|
784
|
+
arguments about a number that was never the repository's.
|
|
785
|
+
|
|
786
|
+
**Excluded anyway, and not because three is still too many.**
|
|
787
|
+
`CHANGELOG-content-build.md` is the published changelog of
|
|
788
|
+
`@heroiclands/content-build` — a **deprecated repository**, absorbed into this
|
|
789
|
+
one at 3.0.0 (#32). It is frozen, not merely generated: exactly one commit has
|
|
790
|
+
ever touched it, nothing regenerates it, and it ships only because `files` lists
|
|
791
|
+
it. Its three findings are facts about what content-build published. Rewriting
|
|
792
|
+
them would edit a historical record to satisfy a rule about prose nobody will
|
|
793
|
+
write again, and would put a style-only commit in the blame of a file whose whole
|
|
794
|
+
value is being what was published. It is the same class as `CHANGELOG.md`, which
|
|
795
|
+
the shared default already ignores for the weaker reason that the next release
|
|
796
|
+
rewrites it.
|
|
797
|
+
|
|
798
|
+
**Declared locally, so no consumer moves.** The exclusion is a new
|
|
799
|
+
`.markdownlint-cli2.jsonc` in this repository, not an entry added to the shared
|
|
800
|
+
`MARKDOWN_IGNORES`. One repository's retired filename does not belong in
|
|
801
|
+
configuration six repositories consume, and the shared rule set is unchanged for
|
|
802
|
+
all of them.
|
|
803
|
+
|
|
804
|
+
**What that file had to get right, and what it documents.** `content-build
|
|
805
|
+
markdown` passes the shared rules as markdownlint-cli2's `optionsDefault`, and a
|
|
806
|
+
consumer file merges over them **key by key, each key wholesale**:
|
|
807
|
+
|
|
808
|
+
| Declared locally | Effect on the shared default |
|
|
809
|
+
| ---------------- | --------------------------------------------------------------------- |
|
|
810
|
+
| only `ignores` | rule set survives intact — `default: false` and every per-rule option |
|
|
811
|
+
| `ignores` | **replaces** `MARKDOWN_IGNORES`; it does not extend it |
|
|
812
|
+
|
|
813
|
+
So the local file restates `CHANGELOG.md`. Verified rather than assumed — with
|
|
814
|
+
that entry dropped, `lint:markdown` reports ten findings in `CHANGELOG.md`; with
|
|
815
|
+
a probe file present, `MD049` still fires with its shared `underscore` option
|
|
816
|
+
while `MD013` stays silent, which is what proves the rule set was not replaced.
|
|
817
|
+
|
|
818
|
+
`engine/prose-lint.mjs` said a consumer config "replaces it", which is the
|
|
819
|
+
reading that would send the next person to add the ignore to the shared default
|
|
820
|
+
or to a full config copy. Its docstring now states the key-by-key rule and the
|
|
821
|
+
`ignores` trap by name.
|
|
822
|
+
|
|
823
|
+
**Wired as a separate CI step, not folded into the chain.** `npm run lint` is
|
|
824
|
+
added for running both checks locally in one command, but `build.yml` gets a
|
|
825
|
+
`Markdown` step of its own beside `Formatting`. The aggregate chains with `&&`,
|
|
826
|
+
so a formatting failure would short-circuit it and hide every markdown finding
|
|
827
|
+
behind it; as two steps the failing one is named in the checks UI.
|
|
828
|
+
`lint:markdown:fix` is added too — the issue referred to it, and it did not exist.
|
|
829
|
+
|
|
830
|
+
**The exclusion cannot rot silently.** If a future markdownlint-cli2 bump changes
|
|
831
|
+
those merge semantics, the restated `CHANGELOG.md` entry stops applying and its
|
|
832
|
+
ten findings reappear — in the CI step this change adds. The guard is checked by
|
|
833
|
+
the thing it guards.
|
|
834
|
+
|
|
835
|
+
**Bump**
|
|
836
|
+
|
|
837
|
+
_Patch._ Nothing a consumer imports, calls, or configures changes behaviour. The
|
|
838
|
+
one shipped file touched is a docstring in `engine/prose-lint.mjs`, which reaches
|
|
839
|
+
consumers through the emitted declarations; everything else — the workflow, the
|
|
840
|
+
local lint config, the scripts — is this repository's own plumbing and is outside
|
|
841
|
+
`files`.
|
|
842
|
+
|
|
3
843
|
## 6.1.0
|
|
4
844
|
|
|
5
845
|
### Minor Changes
|