Commit Graph
9 Commits
Author SHA1 Message Date
slaguru666andClaude Opus 5 1b2f9aa385 The warning reaches every surface; commit and concurrency are covered
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>
2026-08-06 00:35:15 +01:00
slaguru666andClaude Opus 5 0bbf57b693 The initiative warning becomes authored data, and staging gets a harness
Unreleased. The warning that v0.6.4 deleted is restored, but not as the
string heuristic that caused both faults — rosters carry beforeInitiative
now, and the renderers print it. 23 rosters have it: the 18 whose harmedBy
restricts what works, plus 5 Troll rosters that carried the instruction in
prose and so had two conventions between them. The prose copies are gone,
so it is said exactly once wherever it is said.

validate-pack enforces both halves — ONLY implies the field, and the phrase
may not appear in prose — and core/test.mjs now runs the validator over all
sixteen packs, so a pack regression fails the suite rather than waiting for
someone to remember the tool. Both rules were confirmed by breaking a pack
deliberately.

enter()'s decisions move to module/stage.mjs behind injected effects, which
makes the failure paths runnable under plain node. That immediately found a
bug in the first version of the extraction: Promise.resolve(fx.stage())
does not catch a synchronous throw, so a Forge that threw rather than
rejected would have taken the whole of enter() down. Nineteen of the new
tests are that matrix — failed scene, failed population with and without a
roster, failed hoard, failed cards, async rejection, and a Forge that
returns nothing.

The contract those tests hold: the scene is the commit point. Fail there
and nothing was entered, so a retry is safe; succeed and the turn advances
before anything else can fail, so a retry cannot restage. Everything after
degrades with a warning that says what was actually lost — the encounter
failure no longer promises a roster the area does not have, and the hoard
failure is no longer swallowed to the console.

Also: game.delve.loadPack is the validating resolver, so raiseDungeon can
no longer stage from a pack with no forgeStageType.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 00:21:10 +01:00
slaguru666andClaude Opus 5 9d220a3428 v0.6.4 — one classifier for both surfaces, and tests for it
The foe block broke twice in two releases, both times because each
surface decided for itself, in its own hand-written ternary, which of
the cases it was in. classifyFoes() in the new module/foes.mjs makes that
choice once — forged, planned, unavailable, none — and both surfaces
switch on it. foes.mjs is Foundry-free on purpose, so foundry-module/
test.mjs can run it under plain node with no shim. Fourteen tests, each
one a case that shipped broken or nearly did.

enter() now survives a Forge that throws. It had no catch, so a failure
left the scene raised, no cards posted and the turn not advanced, and a
retry would stage the area twice — while the comment above it claimed
the branch handled exactly that. A failed stage stops before the turn
advances, since there is nothing to run; a failed population keeps going,
because the scene is up and the planned roster stands in.

An area with combat heat but neither actors nor a roster used to render
nothing at all. It now says so and points at the decision and fallback.

The "say so before initiative" addendum is gone from all three renderers.
It was gated on the literal word ONLY, and the palace, port and village
battle rosters already end with that exact phrase, so it printed twice.
The packs that need the instruction carry it themselves.

Also: draft() spreads params before the pack, like raiseDungeon; and
packById validates the cached pack rather than trusting it, so the
fail-closed rule holds at boot too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 00:05:26 +01:00
tevansandClaude Opus 5 decb3bf930 Replace the footers; the repo is public now
Eleven files carried "Private VANITY development material — not for sale or
distribution", which contradicted the repo the moment it went public.

Two replacements, because the files do two different jobs. core/render.mjs
*emits* its footer into every generated delve, so that one now says where the
document came from rather than restricting what can be done with it — a
generated dungeon belongs to whoever generated it. The project's own documents
— DESIGN.md, the paper delve, both playtests, the spike report — read as
development notes in a public repo.

Samples regenerated so they carry the new line. The worksheet and play sheet
never had a footer and still do not; they are the GM's own working documents.

Module to 0.1.1: the v0.1.0 zip vendors the old render.mjs and would stamp the
stale footer on everything it generated. Cutting a version rather than swapping
assets under the existing one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 12:38:32 +01:00
tevansandClaude Opus 5 6517b10111 Fix the situation opener; guard the grammar in tests
A cold read caught the subtraction pass emitting broken English:
'Nobody — but the bells are moving is a summons is being rung...'. The opener
template assumed occupant was a noun phrase and doing was a participle; ten
situations broke that, and one occupant carried its own clause.

Root cause fixed in the data, not the template. The template also drops its
hardcoded copula for an em-dash appositive, which removes subject-verb
agreement as a failure mode entirely — 'The bells is ringing' cannot recur.

Three invariants now guard it: occupants carry no verb, actions are participle
phrases, and no rendered opener contains a stray copula. The first version of
that last test was wrong rather than the output — it counted em-dashes and
flagged 191 false positives on cue fragments that legitimately contain one.
The audit regex also had a false negative, since 'something' ends in -ing.

Tests 25 -> 28.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 08:35:29 +01:00
tevansandClaude Opus 5 2dafb9be33 Subtraction pass: integrate the area block
Third reading said the core criticism was gone — areas now arrive as playable
rooms — but that the result was over-stuffed: 'a room plus its operating
manual'. Asked for the single remaining gap, it said one word: integration.
The hand-written delve makes image, situation, pressure and choice read as one
thing; this presented them as stacked components.

So this removes blocks, not content. The image, the situation and the automatic
clue become one opening paragraph to read from. The decision collapses to
attempt/miss/or. Trigger, bane beat, fallback and skip-guidance become a single
GM footer line. The per-area truth drops the delve-level transgression, which
was already in the header and was being reprinted in every area.

Areas go from roughly thirty lines to eleven to eighteen.

A new invariant asserts the pass removed blocks and not information: for twenty
delves, every situation, onArrival, because, offer, decision, alternative,
trigger, fallback, temptation and roster must still appear in the rendered page.
Tests 24 -> 25.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 08:29:06 +01:00
tevansandClaude Opus 5 906a034ffe Areas arrive as situations, not prompts
The review's verdict after resolution blocks: 'still choices attached to
prompts, not spaces that naturally produce play'. The hand-written delve gives
a place — four people back-to-back around a dead lantern — and the decisions
fall out of it. DELVE gave a decision and asked the GM to imagine the room.

Every area now carries a situation: an occupant, what they are doing, what
changes when the party walks in, why (GM), and what it offers. 48 across 8
motifs, motif-scoped and dealt without replacement.

It produces chains nothing planned: in seed live-table-01 a grave-robber in
area 2 is trying to get a password's intonation right, and area 4 is a door
that needs one. Three routes to it now exist where there was a Wits roll.

Also closes the last two open complaints: the five roll-less decisions now have
real attempts (a voice you could not speak to was the weakest), and {priorCue}
resolves against a cue the party has actually seen instead of granting the
answer by fiat.

Tests 19 -> 24.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 08:22:29 +01:00
tevansandClaude Opus 5 09dd58bff0 Resolution blocks on every decision; motif-scope the features pool
Playtest 3 put four players (Codex) through a generated delve with only the
player-facing text. Verdict: "a list of room-prompts, not a place", which
"expects the GM to invent too much of the actual game". It localised that to
one thing — decisions had a cue and a cost but no resolution.

Every decision now carries: an attempt roll, an explicit success outcome, an
explicit failure outcome, and an always-available alternative. 48 blocks across
8 motifs. The alternative is not optional: playtest 1 died on a gate the table
could fail without knowing, and the sharpest complaint this time was a door
whose only options were "guess blind and bleed Banes" or "pay time".

Features are now motif-scoped too, so all three furniture pools belong to the
fiction rather than a global list. 136 authored entries per theme.

Three new invariants make the failure modes unauthorable: every decision has a
resolution, no decision gates progress without an alternative, and every rolled
decision states both success and failure. Tests 16 -> 19.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 08:09:24 +01:00
tevansandClaude Opus 5 07148df8e6 DELVE core: seeded generator, fiction kernel, Barrow pack, CLI
Build steps 1, 2 and 4 of the plan in DESIGN.md.

core/ is pure JS with zero Foundry globals and 11 passing invariants. The
fiction model has one root draw — an APPETITE plus the ACCOMMODATION someone
made for it — and projects the motif, danger, prize, faction, cue fragments and
the appeasement move from it, so the fiction coheres instead of being four
unrelated draws. 336 kernels from 21 authored lines; 60 generated delves gave
51 distinct kernels.

Cue fragments are tagged by facet (institution/ritual/demand/wound/anchor) and
dealt without replacement. The facet progression across the arc reproduces the
hand-authored paper delve's structure, which had arrived at it by instinct.

Generation and table dice stay apart: pressure.mjs decides when a clock roll is
due and what it means, and never rolls. That is what makes the skeleton seed an
honest promise rather than a half-truth.

paper/ holds the hand-written delve and both playtest logs. spike/ holds the
instrumented harness that measured the Forge outside Foundry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:16:58 +01:00