Still unreleased, but module.json is at 0.6.5 now.
initiativeWarning checked the field before the prose, so a hand-edited file
carrying both printed the warning twice — the exact fault the migration was
meant to end. Prose is checked first now and always wins.
The play sheet was the only surface that did not render `avoid`, so three
legacy rosters whose warning lived there — church, forest and temple at
nightmare — lost it entirely: the helper suppressed its fallback on finding
prose the play sheet never showed. It renders `avoid` now, which it should
have anyway; it is how the table gets past a fight without having one, which
is table-facing by definition. All five surfaces now render both prose
fields, so "the prose already says it" means the same thing everywhere.
Replayed all 64 rosters in their pre-field form through every surface: 26
need a warning, 130 of 130 checks say it exactly once. The current packs
give the same result.
commit() no longer mutates st before the write — it passes an incremented
copy, so a failed write really does leave the state as it was, which is what
the retry warning promises.
The single-flight guard moved into stage.mjs as singleFlight() and has four
tests: overlapping calls never run together, the second is refused rather
than queued, the guard clears afterwards, and a throw does not wedge it. It
closes the window within one client only; two GM browsers still race, and
that needs a world-level lock rather than a module variable.
41 core tests, 60 adapter tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Still unreleased. Answering the three blockers.
The worksheet and play sheet never rendered the new field, and stripping
the prose meant they said nothing at all — the two surfaces the whole
two-surface design is about. All five renderers now go through one helper,
core/roster.mjs, which also recovers the warning for delve files generated
before the field existed and refuses to add a line the prose already
carries. Measured across four themes: 40 of 40 delves carrying a roster now
warn on worksheet, play sheet and markdown alike.
Twenty-three was the wrong set. The spelling was never the semantics:
barrow's nightmare Wraith, cave's nightmare Troll and forest's battle Troll
carry the same non-discoverable restriction without the word ONLY. Marked,
so 26. I audited all 64 rosters rather than trusting the count — the ones
left unmarked are discoverable by trying (an Ogre with 12 Grit, mooks that
keep coming until the necromancer stops), and damage works on all of them.
commit() was unprotected, so a failed settings write threw out of stageArea
with the scene already real. It is caught now and says plainly that a retry
will raise the area twice. enter() also has a single-flight guard: it is
bound to a button, it awaits the Forge for seconds, and two overlapping
calls both read the same index.
Also: packById checks the geometry is one VANITY can build rather than just
truthy; foeSection is exported and the suite holds the two renderers to
agreement on all four kinds; and the module README and the seams warning
said 0.10.5 where everything else says 0.10.4.
41 core tests, 54 adapter tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
What was on git could not be installed. foundry-module/ referenced core/ rather
than containing it, so an installed copy had no generator at all — it only ever
worked here because the live module directory had core/ copied in by hand.
core/ is now vendored into foundry-module/module/core/, minus cli.mjs and
test.mjs which are desk tools. module.json points manifest and download at
release assets, the way mapwright distributes, so a remote server can install by
URL without reaching into the repo tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>