E00Project FoundationA trustworthy engineering foundation: every change lands through a green, coverage-gated, security-scanned PR; the sim core is deterministic and replayable; content is schema-validated; a greybox testbed boots in the chosen engine; and a public backlog.html always shows what is done and what is next.37/39 done · 95%
M0 Foundation 2
mw-e00.33Find and fix unit tests that fail under heavy CPU load
Description
Why
During parallel agent work on 2026-10-03,
pnpm testfailed 1–2 tests intermittently at load average 11–33 (seen on the mw-e11.7 rebase and the mw-e11.9 branch), then passed on rerun. CI has stayed green, but a load-sensitive test will eventually flake in CI too.What
Reproduce with CPU stress (e.g. run vitest with high --pool concurrency alongside a busy loop, or --repeat), identify the tests (likely wall-clock or timing-budget assertions outside tests/bench), and make them deterministic. No retries.
Acceptance criteria
AC-1 unit The full unit suite passes 5 consecutive runs under a documented CPU-stress reproduction.
AC-2 unit The tests found are fixed to not depend on wall-clock timing (or moved into tests/bench).
area:infraarea:qaepic:e00milestone:m0mw-e00.30Remove finished spike prototypes from main once their runtimes land
Description
Why
Spike prototypes sit outside every quality gate on purpose (spikes/README.md). Left on main they rot, confuse search results and invite imports into game code. Depth before breadth applies to the repo too: one maintained implementation beats two.
What
As a developer, I want finished spikes moved off main so that the only AI and engine code I find is the maintained code in src/. Tag the current spikes on an archive ref (
archive/spikes-2026), deletespikes/engine-*,spikes/determinism,spikes/bench,spikes/shared(ADR-0001, mw-e00.13) andspikes/ai-architecture(ADR-0005, mw-e11.1) from main, and update the ADRs' evidence links to point at the archive ref so the evidence stays reproducible.Scope
- In: Archive tag/branch, deletion, ADR link updates, the spikes ignore entries in eslint.config.js and .prettierignore (keep them if spikes/ stays as the home for future spikes)
- Out: Future spikes (they follow the same pattern and get their own removal)
Notes
Do it after mw-e11.2 has its own tests for the patrol → investigate → return scenario, so the spike is no longer the only executable evidence for ADR-0005.
Acceptance criteria
- AC-1 manual Given the archive ref, when checked out, then the ADR-0001 and ADR-0005 spike commands in each spike README still run.
- AC-2 unit Given main after the change, when
git ls-files spikes/runs, then it lists only spikes/README.md (or nothing). - AC-3 manual Given ADR-0001 and ADR-0005, when their evidence links are followed, then every link resolves on the archive ref.
- AC-4 unit Given main after the change, when pnpm lint, typecheck, test:coverage and build run, then they pass with no coverage change.
Waits on
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
area:infraepic:e00milestone:m0milestone:m1
E01Playable Vertical SliceA player can start the game from a title screen, pick a class, spawn into a grey-box room, walk a corridor, fight a skeleton, loot it, save, die, respawn and reload — end to end, in a browser, verified by an automated e2e test. At m2 a grey-box class gauntlet proves every class's core verbs in one course.16/21 done · 76%
M1 Grey-box slice 2
mw-e01.10Playtest the m1 slice flow and file follow-up beads
Description
Why
Automated tests prove the loop works; only people prove it is understandable.
What
Five internal testers play the slice cold with no instructions beyond controls. Observe: time to first attack, whether they find the key, whether death/reload is understood, whether they save voluntarily. Produce docs/playtests/m1-slice.md and beads for every blocker.
Scope
- In: Session plan, observation notes, findings doc, follow-up beads
- Out: Combat fun validation (e35-greybox-combat-test, e04-skeleton-fun-validation)
Notes
Run in the same sessions as e35-greybox-combat-test to save tester time.
Acceptance criteria
- AC-1 manual Given 5 testers, when they play, then >= 4 reach slice complete within 8 minutes without help.
- AC-2 manual Given testers who die, when the death screen appears, then >= 4 of 5 return to play without asking what to do.
- AC-3 content Given the findings doc, when complete, then every issue observed by >= 2 testers has a bead key listed next to it.
Waits on
- mw-e01.18 Show a slice-complete card and return to the title done
- mw-e01.9 Prove the slice plays start-to-finish in a CI e2e test done
area:qaepic:e01milestone:m1mw-e01.20Playtest: 3 testers each name a skeleton attack by its telegraph (mw-e01.5 AC-4)
Description
Why
Manual AC-4 of mw-e01.5 cannot be automated: readable telegraphs are a playfeel claim.
What
Run in the same sessions as mw-e01.10. Have 3 internal testers play the slice; record whether each can name at least one skeleton attack by its telegraph. Findings go in docs/playtests/m1-slice.md.
Scope
- In: session, per-tester result, follow-up beads for unreadable telegraphs
- Out: automated telegraph tests (mw-e04.20, closed)
Acceptance criteria
- AC-1 manual Given 3 internal testers, when they play the slice, then each can name at least one skeleton attack by its telegraph.
- AC-2 content Given the findings doc, when complete, then each tester's answer is recorded and any failure has a bead key next to it.
area:gamearea:qaepic:e01milestone:m1needs-human
M2 Class fantasies 2
mw-e01.13Build the grey-box class gauntlet exercising every class verb
Description
Why
M2 must prove each class fantasy in grey-box: "a locked tower isn't simply requires level 8".
What
As a player of any class, I want a grey-box course whose every gate has a route for my class so that my class's core verbs are proven.
Five chambers, each with one gate and four routes: (1) Parry gate: skeleton sentry — knight parry/riposte or smash a weak wall; archer headshot from a gallery; sorcerer Gust it off a ledge; thief sneak/backstab. (2) Rope bridge chasm: rope arrow anchor; Levitate/Blink; climb route; knight kicks a beam across. (3) Webbed door: Ember/fire arrow burns web; knight cuts; thief finds vent. (4) Water channel: Frostglass freeze; float crates; climb beams; blunt-arrow the sluice. (5) Guard room with locked chest and lever: lockpick; shoot the lever; Mage Hand it; shield-bash the lock.
Scope
- In: Scene data for 5 chambers, gate/route wiring, route declarations per class
- Out: The verbs (E04/E05/E08/E10)
- Out: Route audit for the real MVP (E16)
Notes
Each chamber is also a reusable e16 obstacle template. Every route must work via properties (flammable, climbable, shootable), never pairings.
Acceptance criteria
- AC-1 content Given the gauntlet scene, when validated, then every gate declares >= 3 class routes referencing existing capability ids.
- AC-2 integration Given chamber 3's web, when any heat source above its ignition threshold touches it, then it burns away regardless of source (spell, arrow, torch).
- AC-3 integration Given a player breaks the chamber 2 beam before crossing, when the chasm is re-evaluated, then at least one remaining route still exists for every class (no softlock, edge).
- AC-4 perf Given the densest gauntlet chamber with 4 creatures and fire, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e03.13 Support hanging objects on ropes that burn, cut or snap done
- mw-e04.12 Parry window and riposte critical attack done
- mw-e04.17 Smash breakable walls and objects by impact force open
- mw-e05.10 Rope arrows anchor into wood and create climbable ropes open
- mw-e05.13 Shootable triggers and cuttable ropes activate mechanisms done
- mw-e08.10 Add Mage Hand: grab, move and use distant objects done
- mw-e08.13 Add Levitate: float the caster or an object upward slowly open
- mw-e08.2 Add Ember: precise ignition of flammable things done
- mw-e08.5 Add Frostglass: freeze targets and create temporary ice surfaces open
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e13.1 Add the Forgotten skeleton with the e04 three-move set done
area:contentepic:e01milestone:m2mw-e01.14Replay the class gauntlet end to end for each of the four classes
Description
Why
Class fantasies must stay proven as systems change; a regression in any class verb should fail CI.
What
Four recorded replays (knight, archer, sorcerer, thief) that complete the gauntlet using only class-granted capabilities, asserted headlessly in CI and via Playwright on Chromium nightly.
Scope
- In: 4 replays, headless CI job, nightly Playwright job, capability-use assertion
- Out: Playtests of class feel (e35-class-fantasy-tests)
Notes
The capability-use assertion reads capability.used events and fails if a replay used a capability outside its class kit.
Acceptance criteria
- AC-1 integration Given each class replay, when run headless, then the gauntlet.complete fact is set and the final hash equals the golden hash.
- AC-2 integration Given the thief replay, when capability.used events are inspected, then no event references a capability not granted by the thief class (edge).
- AC-3 e2e Given the nightly Chromium run, when all four replays execute, then each finishes with zero console errors.
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e01.13 Build the grey-box class gauntlet exercising every class verb open
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
area:qaepic:e01milestone:m2
M3 MVP content 1
mw-e01.12Wake in context after death instead of reloading
Description
Why
Bible §4.5: dying in the Knot wakes you in the Fold, dying in the Briarwood wakes you at the Sleeping Ox. Death must move the story sideways.
What
As a player, I want to wake somewhere meaningful after I die so that failure becomes part of the story.
Extends the respawn resolver with mode wake-in-place: no reload; the world state is kept; the player is moved to the destination spawn with restored health, set facts (e.g. rescues.byHorn += 1), an optional cost (coin/trinket fee), and a wake text id. Death screen still offers "Load last save" as a secondary option. Rules for Knot → Fold, Knot (Horn dead) → goblin camp or Forgotten laying-out, Briarwood → Sleeping Ox are content in the respective area items.
Scope
- In: wake-in-place mode, fee/cost effects, counters, wake text hook, death-screen option ordering
- Out: Rule content per area (e26-horns-hall-fold, e25-briarwood-greybox)
- Out: Wake texts (e39-failure-narratives)
Notes
Creatures that killed the player return to routine; hostile creatures near the wake point are pushed to Searching elsewhere so the player never wakes into a lethal loop.
Acceptance criteria
- AC-1 unit Given a wake-in-place rule for region knot with fact horn.alive, when the player dies, then the player is placed at the Fold spawn at full health and rescues.byHorn increments by exactly 1.
- AC-2 unit Given a rule with a fee of 20 coins and a player holding 5, when applied, then the fee takes 5 coins and sets fact debt.kettleback = 15 (edge: no negative gold).
- AC-3 integration Given the player wakes in place, when inspected, then no world facts or entity deltas are rolled back other than those in factsToSet.
- AC-4 integration Given a hostile creature within 15 m of the wake spawn, when the wake completes, then that creature is relocated or set to a non-combat state before input resumes (edge).
- AC-5 e2e Given the death screen after a wake-eligible death, when shown, then "Wake" is the default focused option and "Load last save" is available.
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e01.8 Run the player death and respawn loop done
- mw-e23.4 Make quest failures continue the story instead of dead-ending open
- mw-e27.5 Query world facts with a shared condition API for all systems done
area:gameepic:e01milestone:m3
E02Core Character ControllerMovement, camera, jumping, climbing, stance, interaction, targeting and animation hooks feel excellent and responsive on keyboard + mouse (gamepad second). All movement rules live in src/sim as a deterministic kinematic controller driven by abstract input actions, so replays, AI and tests share the exact same rules the player feels.29/39 done · 74%
M1 Grey-box slice 9
mw-e02.25Fade the player and near occluders when the orbit camera is pulled in close
Description
Why
When collision pulls the orbit camera (mw-e02.4) in tight, for example behind a pillar in the testbed corridor or with the player backed against a wall, the placeholder capsule can fill half the screen and hide the path ahead. mw-e02.4 scoped a transparency fade of occluders near the camera, but it shipped without one.
What
As a player, I want the character and anything right in front of the camera to fade out when the camera is squeezed in close, so that I can still see where I am going.
Scope
- In: Fade the player's body by camera distance, and fade scene pieces that the camera's near sphere or the camera-to-player line passes through, with a smooth fade in and out.
- Out: Camera placement and collision (done in mw-e02.4).
Notes
src/game/camera/orbit-camera.ts already publishes boom length and ideal distance each frame. The fade is presentation only and never touches the sim.
Acceptance criteria
- AC-1 unit Given the boom pulled in below the fade start distance, when a frame is drawn, then the player's opacity falls monotonically with distance and is fully opaque at or beyond it.
- AC-2 unit Given a scene piece between the camera and the player, when a frame is drawn, then that piece fades out within 0.2 s and fades back in once it is clear.
- AC-3 e2e Given the testbed corridor, when the bot walks past the pillars, then there are no console errors and the frame time stays within the perf budget.
area:gamearea:renderarea:simclass:allepic:e02milestone:m1size:smw-e02.28Owner feel review of lock-on tuning
Description
Why
mw-e02.16 shipped lock-on with documented default numbers the bead did not specify (distance weighting, priority weight, turn rate, flick thresholds, camera framing). Feel is the owner's call.
What
As a designer, I want the lock-on feel reviewed in the testbed arena so that the defaults are confirmed or retuned.
Scope
- In: src/content/data/lock-on/player.json values (distanceWeight 0.5, priorityWeight 0.25, turnRate 720°/s, stick flick 0.7/0.3, mouse flick 40/5 counts, framing 0.2 s / 0.5 / −10°).
- Out: rule changes (new beads).
Notes
Open the testbed, walk into the arena, lock on (Q / RS click), cycle (Tab, mouse swipe, RS flick), strafe with A/D.
Acceptance criteria
- AC-1 manual Given the testbed arena with three dummies, when the owner locks, cycles and strafes with keyboard+mouse and with a pad, then each tuning value is confirmed or a new value is recorded in the content file.
area:designarea:gamearea:simepic:e02milestone:m1milestone:m2needs-humanmw-e02.39Pushables in content: surface friction lookup, class strength, authored push config
Description
Follow-up from mw-e02.18 (PR #313). Push sim (src/sim/push) ships but: (1) friction comes from the object or an optional surface hook, not the surface under the object; (2) strength defaults to 100 for all classes (knight should be stronger, via class data); (3) push.config (axis, grid, rotateStep) cannot be authored in scene data; (4) prompt still reads Push while grabbing; (5) rotation is not swept and can clip walls; (6) PUSH_LOAD_FACTOR=4 was added to the bead formula so stone vs ice differ — confirm or retune. Place at least one pushable crate in the testbed.
AC-1 unit A crate on an ice surface pushes faster than on stone using the surface under it.
AC-2 unit Knight strength from class data exceeds the default and pushes faster.
AC-3 unit Scene data can author axis, grid and rotateStep, validated by schema.
AC-4 unit Rotation that would penetrate a wall is refused.
AC-5 e2e The testbed has a pushable crate the player can grab, push and release.
area:gamearea:simepic:e02milestone:m1mw-e02.27Interpolate the lock point for moving targets in the lock marker and framing
Description
Why
The lock-on marker and camera framing (mw-e02.16) read the locked target's lock point from the sim's ViewAnchor as of the last tick. Training dummies stand still, but a moving creature would make the marker step at 60 Hz instead of gliding with the interpolated render.
What
As a player, I want the lock ring to stay glued to a moving target so that locked combat reads cleanly at any refresh rate.
Scope
- In: interpolate the target's lock point with RenderSync alpha (or read the drawn transform) for the HUD marker and LockFraming.
- Out: lock-on rules (src/sim/targeting).
Notes
src/game/player/testbed-player.ts lockTarget(); src/game/player/lock-marker.ts.
Acceptance criteria
- AC-1 unit Given a target moving at 5 m/s and a 144 Hz display, when frames are drawn between ticks, then the marker position follows the interpolated lock point (error ≤ 1 px at 1080p).
- AC-2 unit Given a stationary target, when frames are drawn, then the marker writes no DOM changes (idle HUD stays free).
area:gamearea:simepic:e02milestone:m1milestone:m2mw-e02.29Add InteractPrompt widget to the UI component gallery
Description
From PR #100: InteractPrompt is not in src/ui/gallery.ts, so gallery axe and text-scale checks don't cover it. Add it.
area:gamearea:simepic:e02milestone:m1mw-e02.30Give NPC interactors their own interact input
Description
From PR #100: interaction system applies the Interact button to every interactor entity by default. Fine while only the player interacts; future NPC interactors must supply input via the system's input option.
area:gamearea:simepic:e02milestone:m1mw-e02.34Heavy encumbrance blocks the pull-up from a ledge hang
Description
Why
mw-e02.12 notes that heavy encumbrance may block pull-up (data). Mantling and hangs shipped without it because the sim has no carried weight yet.
What
As a player carrying too much, I want pulling up from a hang to fail (with a cue), so that weight matters on climbs.
Scope
- In: an encumbrance threshold in the controller's ledge tuning (content); the ledge hook refuses the pull-up above it
- Out: the inventory and weight model itself
Acceptance criteria
- AC-1 unit Given a hanging player above the pull-up weight threshold, when jump is pressed, then the player keeps hanging; below it, the pull-up runs.
area:gamearea:simclass:allepic:e02milestone:m1mw-e02.35FakeCollisionWorld: an airborne capsule pushing into a wall sticks at the wall's square top edge
Description
Why
Found while testing mw-e02.12: in the in-memory collision world, a falling capsule held against a wall whose top edge is within the capsule's height hangs in mid-air (velocity 0, not grounded) forever. The fake's square-edge Minkowski approximation reports the wall's top face (normal +y) for a diagonal down-and-in sweep beside the edge, and the controller's slide then cancels the fall. Rapier rounds edges, so the game is unaffected, but unit tests and goldens on the fake can hit it.
What
As a developer, I want the fake to behave like Rapier at wall top edges, so that controller tests never pin a test-double artifact.
Scope
- In: fix the fake's contact choice at edges (prefer the face the core is beside), plus a contract test run against both worlds
- Out: changes to the Rapier port
Acceptance criteria
- AC-1 unit Given a capsule falling while pushing into a 2 m wall, beside its top edge, when stepped, then it slides down and lands, in both the fake and the Rapier collision world (contract suite).
area:gamearea:simepic:e02milestone:m1mw-e02.37Make bulky a world property per ADR-0003 (carry follow-up)
Description
Why
mw-e02.17 (PR #301) marks shoulder-carried things with a carry.bulky marker component to avoid a property-schema version bump; ADR-0003 names bulky as a world property.
What
Add bulky to the property vocabulary (schema version bump + migration), switch src/sim/carry to read it, drop the marker component.
Acceptance criteria
AC-1 unit Given an entity with the bulky property, when carried, then the carry is a shoulder carry (x0.5 speed, no jump).
area:gamearea:simepic:e02milestone:m1
M2 Class fantasies 1
mw-e02.19Run a controller feel review and retune against reference games
Description
Why
Automated tests keep the controller correct; only humans can tell if it feels excellent. Numeric targets need validating against the tone references (Souls weight, Zelda responsiveness).
What
Time-boxed (1 day) playtest of the greybox traversal course with 3+ testers, comparing jump arc, acceleration, camera lag and mantle timing against reference feel notes. Output: tuning changes committed to controller data and a short findings doc.
Scope
- In: Playtest script, questionnaire, tuning deltas
- In: docs/design/controller-feel.md with before/after values
- Out: Combat feel — see mw-e04
Notes
Time-box: 1 day. Record sessions (e34 telemetry optional).
Acceptance criteria
- AC-1 manual Given 3 testers on the traversal course, when surveyed, then median "controls feel responsive" score is ≥ 4/5.
- AC-2 content Given the review, when finished, then all tuning changes are committed to controller data and golden replays are re-blessed with diffs attached.
- AC-3 manual Given any tester reports a specific frustration (e.g. missed mantle), when reviewed, then it is either fixed or filed as a bead with a failing test.
Waits on
- mw-e02.12 Mantle onto and grab ledges detected from world geometry done
- mw-e02.3 Move controller tuning values into schema-validated content data done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
area:designepic:e02milestone:m2needs-humansize:s
E03Systemic Interaction EngineDoors, switches, movable objects, breakables, fire, water, ice, electricity, gas, light, physics and triggers form one reusable, property-based world simulation. Nothing is scripted pairwise: objects declare properties (flammable, wet, frozen, conductive, weight, fragile, climbable, liftable, hideable, reflective…) and elements propagate by rules over those properties, so a spell, an arrow, a torch, a creature and a puzzle all touch the same systems. This is the architectural heart of "Wait… I can do that?".37/58 done · 64%
M1 Grey-box slice 18
mw-e03.33Validate signal-graph entity bindings against level entity ids
Description
Why
mw-e03.21 signal graphs bind nodes to level entities by name ("entity": "lower-lock-gate"). Nothing checks those names at content load yet because levels are not content; a typo only fails when the graph is placed at runtime.
What
As a designer, I want the content loader to reject a graph whose bindings name entities missing from the level that places it, so that typos fail in CI rather than in play.
Scope
- In: cross-reference check from the level/placement data to signal-graph bindings, with file path and pointer
- Out: the level format — see mw-e01.4 greybox level
Notes
The sim already throws on a missing binding when the graph is placed (addSignalGraph).
Acceptance criteria
- AC-1 content Given a level placing a graph whose node binds an entity id absent from the level, when validated, then validation fails naming the id and the level file.
area:contentarea:simepic:e03milestone:m1size:smw-e03.36Save and load the sim's physics state in the world save section
Description
Why
Since mw-e03.35 a World with physics carries PhysicsState (engine id plus Rapier's snapshot as base64 and a handle table) in its WorldSnapshot, and restore() requires it. The save world section's schema (src/game/save/format/registry.ts) is a strictObject without a physics field, so saving a world that has physics would be rejected, and physics would be lost across save/load.
What
Add the optional physics field to the world section (a new section version with an identity migration, like facts in v2), with round-trip and fixture tests. Consider compressing the Rapier bytes (ADR-0001: about 350 KB per 200 bodies) and whether static level geometry should be rebuilt from the level rather than saved (mw-e27.3 baselines).
Scope
- In: world section schema/version, save fixtures, round-trip test with a physics world
- Out: dynamic bodies (mw-e03.10)
Acceptance criteria
- AC-1 unit Given a World with Rapier physics and a loaded scene, when saved and loaded into a new World, then the state hash is identical and stepping on matches the uninterrupted run.
- AC-2 unit Given a save from before this change (no physics), when loaded into a World without physics, then it loads unchanged.
area:gamearea:simepic:e03milestone:m1mw-e03.32Test trigger volumes for element presence in element-field cells
Description
Why
mw-e03.21 filters volumes by element presence on entities (fire = burning, water = wetness > 0, ice = frozen, charge = charge > 0). A room flooding with gas or a patch of burning oil on the floor lives in the element grid (mw-e03.4), not on entities, so a volume cannot see it yet.
What
As a designer, I want a trigger volume filter that tests element-field cells inside the volume (fire, gas, water, charge above a threshold) so that burning oil or seeping gas can trip a mechanism.
Scope
- In: a field-presence predicate for volume nodes (element + min concentration), content schema + sim evaluation
- Out: the element grid itself — see mw-e03.4
Notes
Reuse shapeBounds to find the cells; keep evaluation order canonical.
Acceptance criteria
- AC-1 unit Given a volume filtered to gas ≥ 0.3, when gas cells above that concentration overlap it, then active is true; below it, false.
- AC-2 content Given a field predicate naming an unknown element, when validated, then validation fails naming the element.
Waits on
- mw-e03.4 Build a sparse spatial grid for temperature, moisture, gas and charge done
area:simepic:e03milestone:m1milestone:m2size:smw-e03.34Attribute fire spread to whoever started it (instigator chains through fire)
Description
Follow-up from mw-e03.5 (PR #68). Fire rules currently write property changes with source null: a fire lit by the player's torch and spread through three crates carries no attribution, so quests, crime and AI blame (mw-e04.19, mw-e11) cannot tell who burned the barn. Record the heat source that ignited an entity (e.g. the attributed stimulus that raised its temperature, or the burning entity whose flames reached it) in a snapshotted component, and pass it as the source of ignition/burn/burnout property writes and fire events.
AC-1 unit Given a player heat stimulus that ignites crate A whose flames ignite crate B, when both burn, then fireIgnited for B and B's burning propertyChanged carry the player as source.
AC-2 unit Given the attribution state, when the world is snapshotted and restored mid-fire, then attribution survives and replays hash identically.
area:simepic:e03milestone:m1mw-e03.38Let light pass partially through foliage, cloth and glass
Description
Why
Shadows under a hedge or behind a curtain should be dimmer, not pitch black or fully lit. Line of sight (mw-e09.1) already treats partial cover as transmittance; the light field (mw-e03.15) only knows opaque blockers.
What
As a thief, I want foliage and cloth to cast soft shadows so that partial cover hides me partly.
Light paths through transparent (glass, water, ice) and partial-cover entities are attenuated by a transmittance factor, using the same occlusion vocabulary as line of sight (opaque | transparent | partial:<factor>), instead of only opaque entities blocking fully.
Scope
- In: transmittance for dynamic light occluders; sharing the occlusion factor source with mw-e09.1
- Out: coloured light and caustics (render only)
Notes
Follow-up from mw-e03.15. Keep the sampling budget (AC-6 of mw-e03.15).
Acceptance criteria
- AC-1 unit Given a partial:0.5 bush between an emitter and a sample, when sampled, then the emitter's contribution is halved.
- AC-2 unit Given a transparent pane between an emitter and a sample, when sampled, then the contribution is unchanged.
- AC-3 perf Given the mw-e03.15 light benchmark, when partial occluders are added, then it stays within budget.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
area:simclass:thiefepic:e03milestone:m1size:smw-e03.43Ledges on movable props and runtime-spawned geometry
Description
Why
mw-e03.22 extracts ledges once at scene load from static greybox parts. Movable props (physics-object crates from testprops with bodies) and runtime-spawned geometry (propped ladders, debug-console spawns) have no ledges, so a player cannot mantle a pushed crate.
What
Ledges for physics-object boxes derived from their current pose (top edges of upright boxes), refreshed as they move/sleep, exposed through the same LedgeIndex query (LedgeHit) as scene ledges.
Notes
Build on src/sim/climb/ledges.ts (extractLedges, LedgeIndex). Only upright boxes need ledges; tilted ones can be skipped.
Acceptance criteria
- AC-1 unit Given a 1.4 m physics crate at rest, when ledges are queried near its top, then its four top edges are returned with its entity.
- AC-2 unit Given the crate is pushed 2 m, when queried on the next tick, then the ledges follow its new pose.
- AC-3 unit Given the crate is tipped over past 20°, when queried, then it has no ledges (edge).
area:simepic:e03milestone:m1milestone:m2size:smw-e03.44Reconcile breakable profiles with the unused toughness and bashable world properties
Description
Why
PR #138 (mw-e03.11) added a breakable profile type with its own fragile threshold and resistances, but the existing world properties
toughness(single-hit threshold per kind) andbashableremain unused. Two ways to express the same thing breaks the single property-based model (contract §2).What
Either derive breakable thresholds from
toughness/bashableor remove those properties; also decide whether physics impacts should wear down structural hp (today they only test fragility) and whether break loudness belongs on the material rather than the profile.Acceptance criteria
- AC-1 unit Given a content entity, when it declares breakability, then exactly one property path determines its break thresholds and the validator rejects the other.
area:simepic:e03milestone:m1mw-e03.45Thrown liquid containers shatter and spill a property-carrying puddle
Description
Why
mw-e17.6 shipped thrown oil flasks that land whole and burn where they lie. The bead's intent (a thrown flask shatters and leaves a flammable puddle) needs breakables that spill their liquid contents.
What
Fragile containers break on impact above a threshold and spawn a puddle entity inheriting the container's liquid worldProperties (flammable, wet, conductive...), with a data-driven radius. Purely property-based — no flask-specific code.
Notes
Follow-up from PR #154 (mw-e17.6).
Acceptance criteria
AC-1 unit A fragile container with liquid properties breaking emits a puddle spawn carrying exactly those properties.
AC-2 integration In the greybox testbed a thrown oil flask shatters, leaves a puddle, and igniting it burns the area via e03 propagation.
area:simepic:e03milestone:m1mw-e03.46Check sustained fire next to wooden level geometry (floors, walls) can't soft-lock a scene
Description
PR #279 (mw-akb) gives every flammable thing, including wooden floors and walls, an ambient temperature, so Ember can now light level geometry. An idle probe showed no spread, but sustained fire beside wooden architecture is untested: decide whether structural pieces burn, char or are fireproof, and make sure burning one can't remove a required floor/door/path.
Acceptance criteria
- AC-1 integration: burn the wooden pieces in the Sleeping Ox and the slice; required paths stay traversable; result documented as a design rule
area:simepic:e03milestone:m1mw-e03.48Thrown items carry their own damage type for impacts
Description
Follow-up from mw-e03.20 (PR #322). physicsImpact from thrown/dropped bodies is always blunt, so a thrown dagger cannot trip a pierce-only bullseye. Read a damage type (and optional element) from the thrown item's content data and carry it on the impact.
AC-1 unit A thrown dagger (pierce) at >= 20 J activates a pierce-filtered bullseye.
AC-2 unit A thrown crate (no damage type) stays blunt.
AC-3 unit Hit-zone check optionally respects the switch's facing plane (front hits only) when configured.
area:simepic:e03milestone:m1mw-e03.50Element state SFX: crackle loop, sizzle, freeze crack, zap
Description
mw-e03.28 maps burning/wet/frozen/charged to VFX and mesh looks, and the element-feedback table has an optional audioCue per state, but the sound manifest has no crackle loop, sizzle, freeze crack or zap cue. Add the cues (mw-e38 asset beads) and set audioCue in src/content/data/element-feedback/element-feedback.json. A looping cue (crackle while burning) needs a loop hook in ElementFeedbackBridge; today it plays one-shots on entering a state.
area:simepic:e03milestone:m1mw-e03.51Element states for entities that start a scene in a state (no propertyChanged)
Description
mw-e03.28's bridge reacts to propertyChanged, so an entity that loads burning, wet or frozen from scene data (addProperties fires no event) gets no VFX or look, e.g. lanterns and braziers in briar-glen-lane (their flame is the light rig's). Seed the bridge from a world scan at scene load, skipping entities whose flame the light rig already draws.
area:simepic:e03milestone:m1mw-e03.53Wet state legibility and colour-independent cues
Description
mw-e03.28 screenshot review: wet (darker bluish albedo + blue drips) is the weakest of the four states at game camera distance and is easily mistaken for a dark crate. Strengthen it (wet sheen, puddle decal vfx-decal-wet-01, larger drips) and give every state a non-colour cue (shape or motion), coordinated with mw-e31.8.
area:simepic:e03milestone:m1mw-e03.54Chaos room scene for element interaction e2e and playtest (AC-5)
Description
mw-e03.28 AC-3 and AC-5 refer to a chaos room that does not exist. The e2e uses crates spawned into the testbed. Add a purpose-built scene with spaced props for fire, freeze, wet and charge scenarios and the 6-object identification playtest.
area:simepic:e03milestone:m1mw-e03.55Light receivers only answer beams: a lit brazier, Ember's light or any field light cannot turn a beam sensor on; no light spell exists
Description
Found in mw-e15.9: sunbeam-hall expresses 'light the sensor by igniting a brazier near it' in the puzzle goal (any of sensor lit, brazier lit) because lightReceiver reads beams only (lightActivated property is declared but unread), and the bead's 'light spell' has no spell content. Let receivers (or a sensor node) read light-field level, and add a light spell.
area:simepic:e03milestone:m1mw-e03.56Ceiling beams cannot be traversed: no hang-and-shimmy across overhead beams over a gap
Description
Found in mw-e15.9: the frozen channel cannot declare 'climb the ceiling beams' because the climb system has ledge hang/shimmy and wall/rope climbing but no overhead traversal. Related open: mw-e10.9 (climb tagged surfaces), mw-e03.14 (buoyancy: crates sink, so 'float crates' is not declarable), mw-e08.5 (no cold source until Frostglass), mw-e08.13 (no self-levitation).
area:simepic:e03milestone:m1mw-e03.58No airflow or draft field: 'follow the drafts in the smoke' dark-crossing solution cannot be authored
Description
Found in mw-e15.10: the dark crossing was meant to be solvable by following drafts in smoke to the safe path. There is no smoke, draft or airflow field to read or render (wind only blows flames out as a force stimulus). Add an airflow source (vents, gaps) the stimulus API and renderer can read, then declare the route in src/content/data/puzzle/dark-crossing.json.
area:simepic:e03milestone:m1milestone:m2mw-e03.57Closing a trapdoor over a liquid volume is blocked by the water layer entity (drawbridge from trapdoors impossible)
Description
Found in mw-e15.9: four trapdoors (state open) in a flooded trench, wired to close from a lever, stay 'blocked' with door.blockedBy set to the liquid layer entity (placement radius 2 at the volume centre). A drawbridge over water would be a data-only second route for the frozen channel.
area:simepic:e03milestone:m1
M2 Class fantasies 1
mw-e03.27Replay a library of emergent combo scenarios in a greybox chaos room
Description
Why
We need living proof that "Wait… I can do that?" moments emerge from properties, and a regression net so they keep emerging as rules change.
What
As a designer, I want a catalogue of emergent combos, each a replayable greybox scenario, so that I can see and protect the game's surprising interactions.
A chaos-room greybox level plus ≥ 12 scenario files, each with setup, input log and asserted outcome expressed via properties/facts. Initial catalogue: oil puddle ignition chain; burn rope → dropped chandelier; freeze river → walk across; soak enemies + charge in puddle; frozen actor shattered by blunt hit; levitated crate weighs 0 on plate; wedge portcullis with crate; gust extinguishes candles but not brazier; smoke blocks sight line; burning haystack ejects hider; focused beam ignites web barrier; ice block on pressure plate melts → plate releases (timer puzzle for free).
Scope
- In: Chaos room level, scenario schema, runner integrated with e00-replay-harness
- In: 12+ scenarios with property-based outcome assertions
- In: Catalogue doc docs/design/emergence-catalogue.md generated from scenario metadata
- Out: Randomised coverage — see e03-interaction-matrix
Notes
Scenario outcomes are phrased as properties ("some entity with flammable ends burnt"), never "the rope entity". New combos discovered in playtests get added here.
Acceptance criteria
- AC-1 integration Given each scenario, when replayed in CI, then the asserted property outcome holds and the final state hash matches its golden.
- AC-2 content Given the scenario directory, when validated, then there are ≥ 12 scenarios and each has setup, inputs, outcome and a player-facing description.
- AC-3 integration Given a rule change breaks a scenario, when CI runs, then the failure names the scenario and its outcome description.
- AC-4 integration Given the ice-on-plate scenario, when simulated, then the plate deactivates only after the ice melts below the weight threshold (edge: time-based emergent state).
- AC-5 e2e Given the chaos room in the browser, when the Playwright bot runs the rope/chandelier scenario, then it completes with no console errors.
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e03.13 Support hanging objects on ropes that burn, cut or snap done
- mw-e03.17 Redirect light beams with mirrors and lenses onto receivers done
- mw-e03.19 Activate pressure plates by total weight of anything resting on them done
- mw-e03.23 Hide in wardrobes, barrels and haystacks and search containers done
- mw-e03.26 Property-test all element × property combinations in a matrix harness done
area:designarea:qaclass:allepic:e03milestone:m2size:m
M3 MVP content 2
mw-e03.9Spread gas, smoke and steam clouds that react to fire and wind
Description
Why
Gust clearing poisonous gas, igniting swamp gas, smoke blocking sight lines for a thief escape — gas turns rooms into systemic spaces.
What
As a thief, I want smoke to block vision and flammable gas to explode when lit so that I can escape or set traps.
Gas types in the element field (smoke: blocks vision; steam: obscures, scalds lightly; poison: damages actors; flammable/marsh gas: ignites with flash + force stimulus when concentration ≥ threshold). Gas diffuses, rises/sinks by type, is blocked by walls/doors, vents through openings, and is pushed by force stimuli (Gust).
Scope
- In: Gas species data (buoyancy, diffusion, decay, effects)
- In: Ignition of flammable gas → explosion stimulus
- In: Visibility occlusion query for stealth/AI (density along a ray)
- Out: AI reaction to smoke — see mw-e11
- Out: Gas VFX — see e29-vfx-framework
Notes
Closed doors block gas (door entities are field blockers) — opening a door lets smoke pour out.
Acceptance criteria
- AC-1 unit Given flammable gas at concentration ≥ threshold in a cell and a heat source ≥ ignition, when resolved, then the gas is consumed and a force + heat stimulus is applied with radius scaled by the consumed volume.
- AC-2 unit Given smoke between an observer and a target, when densityAlongRay is queried, then it returns the integrated density and exceeds the vision-block threshold for a 2 m thick cloud.
- AC-3 unit Given a closed door separating two rooms, when gas fills room A, then room B stays at 0 until the door opens, after which concentration equalises over time.
- AC-4 unit Given a force stimulus through a gas cloud, when applied, then gas is displaced along the force direction by the documented amount.
- AC-5 unit Given gas in a fully sealed room with no decay source, when simulated for 30 minutes, then total gas mass is conserved and no cell exceeds max concentration (edge).
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
area:simclass:allepic:e03milestone:m3size:mmw-e03.14Float and sink objects in liquid volumes by density
Description
Why
Floating crates as stepping stones, sinking a heavy chest to trip a submerged plate, or ice slabs drifting are cheap emergent traversal once density is a property.
What
As a player, I want wooden objects to float and iron to sink so that I can improvise rafts and stepping stones.
Buoyancy force from submerged volume × liquid density vs. object density, with drag; objects adjust to the liquid level; wet/soaked materials may change density (data).
Scope
- In: Buoyancy + drag forces for bodies in liquid volumes
- In: Standing on floating bodies (moving platform)
- In: Density changes from waterlogging (data)
- Out: Currents / flowing rivers — out of scope for MVP
Notes
Ice (density 0.92) floats automatically once frozen slabs break free.
Acceptance criteria
- AC-1 unit Given an object with density 0.6 in water (1.0), when settled, then 60% ± 5% of its volume is submerged.
- AC-2 unit Given an iron object (density 7.8), when placed in water, then it sinks to the bottom and can rest on a submerged pressure plate.
- AC-3 unit Given a floating crate with a 20 kg load exceeding its buoyancy reserve, when loaded, then it sinks (edge).
- AC-4 unit Given the water level drops (drain), when resolved, then floating objects descend with the surface and settle on the floor.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
area:simclass:allepic:e03milestone:m3size:s
E04Knight CombatSword/shield gameplay supports attack chains, blocks, parries, dodges, stagger, heavy/charged attacks, shield bashes, weapon reach, armor trade-offs and satisfying hit reactions — and a greybox skeleton fight is fun with no rewards. The shared damage model underpins every class.26/39 done · 67%
M1 Grey-box slice 6
mw-e04.25Out-of-breath cues when the knight is Exhausted
Description
Why
mw-e04.5 AC-6: running out of stamina must be readable without the HUD (souls-like fairness). The sim now emits StaminaExhausted / StaminaRecovered (src/sim/combat/stamina.ts) but nothing presents them yet.
What
As a knight, I want to hear and see that I am out of breath so that I stop swinging before the HUD tells me.
On StaminaExhausted: heavy-breathing loop on the player bus and a hunched/winded idle and slower recovery pose; on StaminaRecovered: breath settles and the loop stops. ActionRejected{reason:"stamina"} plays a short strained grunt (once per press). Placeholder assets via e28 cue sheets until final audio lands.
Scope
- In: cue-sheet entries and game-layer bindings for the three events, placeholder animation state, manual playtest
- Out: HUD stamina bar (mw-e04.10), final SFX/animation assets (audio/art beads)
Acceptance criteria
- AC-1 unit Given a StaminaExhausted event, when the game layer processes it, then the breathing cue starts once and stops on StaminaRecovered.
- AC-2 unit Given ActionRejected{reason:"stamina"}, when processed, then the strain cue plays once.
- AC-3 manual (carried from mw-e04.5 AC-6) Given a 5-minute sandbox session, when a playtester fights the attacker dummy, then they can describe when they are "out of breath" without looking at the HUD.
Waits on
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
- mw-e04.9 Combat sandbox scene with dummy creatures and frame-data overlay done
area:audioarea:gameclass:allepic:e04milestone:m1mw-e04.27Playtest dodge fairness in the combat sandbox (e04.8 AC-6)
Description
Why
mw-e04.8 AC-6 is a manual feel check that needs the combat sandbox's attacker dummy (mw-e04.9), which did not exist when the dodge shipped.
What
As a player, I want dodges that feel fair so that timing, not luck, keeps me alive. Once the sandbox exists, three playtesters practise rolling through the attacker dummy's metronome swing for 3 minutes each.
Scope
- In: the playtest, logging counterexamples with replay files
- Out: tuning changes (file separately with evidence)
Notes
Dodge rules: src/sim/combat/dodge; numbers in src/content/data/move/dodge-roll.json and backstep.json. Golden replays of an on-time vs one-tick-early roll: tests/replays/dodge-*.json.
Acceptance criteria
- AC-1 manual Given the sandbox attacker dummy, when three playtesters practise for 3 minutes, then each reports dodges "felt fair" (no hits they believed they had dodged); any counterexample is logged with a replay file.
Waits on
- mw-e04.9 Combat sandbox scene with dummy creatures and frame-data overlay done
area:simclass:allclass:knightepic:e04milestone:m1milestone:m2mw-e04.38Knight's light swing passes over short creatures like the 0.9 m fixture hound
Description
Why
Found while wiring creature spawning (mw-e12.4): the knight's light attack, swung at a fixture-hound (0.9 m tall, hurtbox capsule 0–0.9 m) standing 1.2 m in front of him, never connects, while the same swing at the 1.8 m fixture-guard at the same distance does. Hounds, rats, goblins and other low creatures must be hittable with the knight's basic attack, or the player has no answer to them.
What
Decide (owner/design) how low creatures are hit: angle the light chain's socket track lower, add a downward/low attack, or auto-aim the swing's height at the locked target. Then make it so.
Scope
- In: knight melee reaching low targets; a test with a short creature.
- Out: creature hurtbox regions per rig (asset beads).
Acceptance criteria
- AC-1 integration Given fixture-hound 1.2 m in front of the knight, when he performs the light attack (locked on or not), then it deals damage to the hound.
- AC-2 integration Given fixture-guard at the same distance, the light attack still hits as today.
area:contentarea:simclass:knightepic:e04milestone:m1milestone:m2mw-e04.29Owner review: dodge feel defaults and controls
Description
Why
mw-e04.8 shipped numbers and controls the bead did not fix; they are documented defaults awaiting the owner's call.
What
Review and confirm or change: backstep travel 1.2 m (backstep.json); roll attack 10/4/20 ticks, 14 stamina, 16 slash, 12 poise (roll-attack.json); keyboard dodge on R (F stays the remap example; Ctrl/Alt trip browser shortcuts); pad dodge on B with crouch moved to D-pad Down and ability 3 on LB only; a swing dodged once cannot hit the dodger later in the same active window; a dodge and an attack pressed on the same tick start the attack; the roll moves only on its 13 active ticks and stands still in recovery.
Scope
- In: decisions, data edits, docs/design/controls.md
- Out: new mechanics
Acceptance criteria
- AC-1 manual Given the list in the description, when the owner has reviewed it, then each item is confirmed or changed in content data/bindings with the decision noted in the move notes or controls doc.
area:simclass:allclass:knightepic:e04milestone:m1milestone:m2mw-e04.32Playtest the knight light chain for weight (mw-e04.6 AC-8)
Description
Why
mw-e04.6 AC-8 is a manual playtest (three testers, median ≥ 4/5 for 'swings feel heavy and committed', none say floaty) that needs the combat sandbox (mw-e04.9).
What
Run the 5-minute sandbox session and record ratings.
Scope
- In: the playtest and its notes
Notes
Also review the defaults chosen in mw-e04.6: planted during attacks, facing locked through recovery, chain memory forgotten on interrupt, training dummy 200 hp.
Acceptance criteria
- AC-1 manual Given a 5-minute sandbox session, when three playtesters use the chain, then the median rating for 'swings feel heavy and committed' is ≥ 4/5 and none describe attacks as floaty.
area:simclass:knightepic:e04milestone:m1milestone:m2mw-e04.35Blind A/B playtest: hit-stop on vs off for impact (mw-e04.11 AC-5)
Description
mw-e04.11 AC-5 manual: given A/B builds with hit-stop on vs off, three playtesters try both blind; at least two of three prefer hit-stop on for impact. Hit-stop off = set every tier to 0 in src/content/data/hit-stop/hit-stop.json. Also a chance to tune the PLACEHOLDER tick values.
area:simclass:knightepic:e04milestone:m1milestone:m2
M2 Class fantasies 6
mw-e04.17Smash breakable walls and objects by impact force
Description
Why
"The knight might discover that an old wall can simply be smashed apart." Force-based breaking turns the knight's strength into a world verb (Classes change possibilities).
What
As a knight, I want to smash cracked walls, barricades and crates with heavy blows so that brute force opens routes other classes find differently.
Any object with world property
breakableand atoughnessbreaks when a single hit'simpactForce(J) reaches itstoughnessfor that kind of hit (querybreaksUnder(type, energy), e03 world properties). impactForce = move base × charge multiplier × weapon class (blunt ×1.5). Hits below toughness advance a visible crack stage (max 2) but never break it — a readable hint that more force is needed. Breaking spawns debris physics objects, emits a loud noise event for stealth, can reveal passages, and persists via e27-world-state-store. The rule is property-based: any source dealing impactForce (bash, blunt arrow, gust spell, rolling boulder) works.Scope
- In: Toughness rule, crack stages, debris spawn, noise event, persistence
- Out: Destruction visuals (e29-destruction-fx)
- Out: Placing weak walls in levels (e26 dungeon)
Notes
Suggested toughness: crate 20, barricade 60, weak masonry wall 110 (charged heavy blunt or equivalent only).
Acceptance criteria
- AC-1 unit Given any
breakableobject with toughness 110, when a hit with impactForce 120 lands, then it breaks andObjectBrokenis emitted with the instigator. - AC-2 unit Given the same object, when a hit with impactForce 60 lands, then its crack stage increments (capped at 2) and it does not break.
- AC-3 integration Given a weak wall hiding a passage, when broken, then debris physics objects spawn, the passage collider is traversable and a noise event of loudness ≥ 80 is emitted.
- AC-4 integration Given a broken wall, when the game is saved and reloaded, then the wall remains broken.
- AC-5 unit Given a non-knight source (e.g. a blunt projectile) producing impactForce ≥ toughness, when it hits, then the object breaks identically (no class check).
- AC-6 manual Given a greybox room with one cracked wall, when three fresh playtesters play as knight, then at least two try hitting it within 2 minutes without being told.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.11 Break fragile objects and smash weakened walls into debris done
- mw-e04.13 Heavy and charged attacks with hyperarmor done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simclass:knightepic:e04milestone:m2mw-e04.22Golden replay suite for combat timing boundaries
Description
Why
Timing windows are the game; golden replays guarantee a refactor never silently shifts a parry by one tick.
What
A set of recorded input logs + expected state hashes in
tests/replays/combat/: 3-hit chain; parry first/last/one-late tick; guard break; i-frame boundary (tick 14 vs 15); charged release at 11/12/60/90 ticks; hit-stop stacking; wall recoil. CI runs them on every PR; a failure prints the first divergent tick and the differing component.Scope
- In: Fixtures, CI wiring, divergence reporter, fixture re-record script with review checklist
- Out: Replay harness itself (e00-replay-harness)
Notes
Re-recording a fixture requires a PR note explaining the intended timing change.
Acceptance criteria
- AC-1 integration Given the combat fixtures, when run in CI, then all replays match their stored hashes.
- AC-2 integration Given a deliberate 1-tick change to the parry window, when the suite runs, then the parry-last-tick fixture fails and reports the first divergent tick and component
ParryState. - AC-3 unit Given src/sim/combat, when coverage runs, then it reports 100% on all metrics.
- AC-4 integration Given a fixture recorded with an older move-data version, when run, then the harness fails with a clear "fixture data version mismatch" message rather than a hash diff.
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e04.11 Hit-stop freezes attacker and victim briefly on impact done
- mw-e04.12 Parry window and riposte critical attack done
- mw-e04.13 Heavy and charged attacks with hyperarmor done
- mw-e04.8 Dodge roll and backstep with invulnerability frames done
area:qaclass:allepic:e04milestone:m2mw-e04.24Validate a greybox skeleton fight is fun with no rewards
Description
Why
Constitution: "Fighting a skeleton should be enjoyable even when the player doesn't need XP or loot." This spike proves or disproves that before content scales.
What
Question: with placeholder art/audio, is repeatedly fighting a greybox skeleton (3-move set from e04-creature-attack-hooks) intrinsically enjoyable for the knight with zero XP/loot? Time-box: 2 days. Protocol: ≥ 5 playtesters (internal acceptable), short tutorial, then an arena that spawns a new skeleton on demand; observe, record replays and telemetry (parries, dodges, voluntary re-fights, session length), short survey.
Scope
- In: Test protocol, arena variant of the sandbox, telemetry events, write-up with tuning recommendations
- Out: Implementing the recommendations (follow-up beads)
- Out: External m4 playtests (e35)
Notes
Pass bar: ≥ 4 of 5 testers voluntarily fight ≥ 3 skeletons beyond the required one and median "fun" ≥ 4/5. Failing is a valid outcome and must produce concrete tuning beads. Related: e13 bestiary skeleton, e35 fun validation.
Acceptance criteria
- AC-1 manual Given ≥ 5 playtest sessions, when completed, then docs/playtests/skeleton-fun-01.md records per-tester voluntary re-fight counts, fun rating (1–5) and quotes.
- AC-2 integration Given the arena build, when a session runs, then telemetry events for parry attempts/successes, dodges, deaths and re-fights are exported via e34-telemetry-events.
- AC-3 manual Given the results, when compared to the pass bar (≥ 4/5 testers fight ≥ 3 extra skeletons; median fun ≥ 4), then the write-up states PASS or FAIL explicitly.
- AC-4 manual Given a FAIL or any rating ≤ 2, when the write-up is finished, then at least three tuning beads are proposed with the replay files that motivated them.
Waits on
- mw-e04.11 Hit-stop freezes attacker and victim briefly on impact done
- mw-e04.12 Parry window and riposte critical attack done
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e04.9 Combat sandbox scene with dummy creatures and frame-data overlay done
- mw-e28.4 Combat impact audio matrix: hits, blocks, parries, whiffs done
- mw-e29.4 Impact VFX by material: sparks, splinters, bone dust, parry flash done
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:qaclass:knightepic:e04milestone:m2mw-e04.33Creatures standing in fire catch fire per creature burning rules
Description
Why
mw-e04.19 says fire volumes deal 8 fire/s *and apply burning per creature rules*. The hazard damage shipped (src/sim/combat/environment, 8 fire/s in 250 ms pulses through the damage model), but creatures have no burning status yet: fire rules (mw-e03.5) only ignite world objects with flammable/temperature properties.
What
Give creatures a burning state (duration, damage over time, extinguished by water/rolling) applied when a creature is hit by a hazard whose property is burning, respecting fire immunity (resistance 0 never ignites).
Discovered from mw-e04.19.
Acceptance criteria
- AC-1 unit Given a creature with fire resistance 1 in a fire volume, when a pulse lands, then it gains the burning state per its creature data.
- AC-2 unit Given a creature with fire resistance 0, when it stands in fire, then it never gains burning.
- AC-3 unit Given a burning creature entering water, then burning ends.
area:simclass:knightepic:e04milestone:m2mw-e04.37Playtest: parry window is learnable against the sandbox metronome
Description
Why
mw-e04.12 AC-8 is a manual playtest the agent cannot run: parrying must feel learnable (Knight fantasy: mastery).
What
As the owner, I want three playtesters to practise parrying the combat sandbox attacker dummy (?scene=combat-sandbox, 2 s metronome, parry on 3 / LB, F3 frame data shows the window, the Parried stun and 'riposte ready') so that the 10-tick window and the placeholder riposte tuning are validated.
Also judge these placeholder decisions from mw-e04.12: (1) parry bound to ability 3 (LB / 3) while block holds LT / right click; (2) a successful parry still plays out its 30 ticks before the knight can riposte (no early exit); (3) the riposte's 8/4/24 frames, 2.0 m thrust and critical-tier hit-stop (src/content/data/move/sword-riposte.json).
Scope
- In: playtest, tuning notes back into move data
- Out: creature attacks through the executor are not yet tagged parryable (they route through hit volumes in mw-e04.20)
Acceptance criteria
- AC-1 manual Given the sandbox attacker dummy with a 2 s metronome, when three playtesters practise for 5 minutes, then each lands >= 5 parries and rates the window 'learnable' >= 4/5 (carried over from mw-e04.12 AC-8).
Waits on
- mw-e04.12 Parry window and riposte critical attack done
area:simclass:knightepic:e04milestone:m2mw-e04.36Hit-stop freezes knockback travel and creature-executor attacks too
Description
mw-e04.11 freezes the action timeline, its hitbox sweeps and hit-reaction clocks. Not yet frozen: a knocked-back/launched victim keeps flying via the character controller during the freeze, and attacks run by the creature attack executor (src/sim/combat/attacks) have their own clock and ignore hit-stop. Decide whether they should follow local time (isHitStopped) and implement.
area:simclass:knightepic:e04milestone:m2
M3 MVP content 1
mw-e04.23Combat assist settings: wider parry, extra i-frames, stamina
Description
Why
Accessibility: more players should reach the joy of a parry without removing the skill ceiling for others.
What
As a player, I want optional combat assists so that timing-heavy combat is accessible to me.
Options via e31-settings-framework: parry window ×1.0/×1.5/×2.0 (extends the end tick), i-frames +0/+3/+6 ticks, stamina costs ×1.0/×0.75, extended creature telegraph cue (+10 ticks warning). Values are injected into the sim at session start and recorded in the replay header for determinism.
Scope
- In: Assist options, sim injection, replay header recording, settings UI entries
- Out: Difficulty levels / enemy damage scaling (later if needed)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given parry assist ×2.0, when parry is pressed on tick 0, then hits on ticks 4–23 are parried (window 20 ticks).
- AC-2 unit Given i-frame assist +3, when a medium-load roll executes, then i-frames cover ticks 2–17.
- AC-3 integration Given a replay recorded with assists on, when replayed on a machine with assists off, then the header values are used and hashes match.
- AC-4 unit Given assists changed mid-session, when applied, then they take effect at the next safe point (no action in progress) and an
AssistsChangedevent is logged.
Waits on
- mw-e04.12 Parry window and riposte critical attack done
- mw-e04.8 Dodge roll and backstep with invulnerability frames done
- mw-e31.1 Create typed settings store and options menu skeleton done
- mw-e31.10 Provide difficulty presets and granular assists read by the sim done
area:simclass:allepic:e04milestone:m3
E05ArcheryBows feel excellent; arrows have physical presence (gravity, drag, sticking, retrieval), weak points and elevation matter, and trick arrows (fire, water, rope, noise, blunt) solve problems through world properties rather than scripted pairings.7/21 done · 33%
M2 Class fantasies 13
mw-e05.3Bow draw, aim and release firing selected arrow type
Description
Why
Archery must feel excellent from the first shot: a satisfying draw, precise aim and immediate, physical release (Archer fantasy: precision).
What
As an archer, I want to draw, aim and release arrows so that I can hit precise targets at range.
Hold fire to draw: full draw at 48 ticks (800 ms, shortbow). Launch speed lerps 30%→100% of 60 m/s by draw fraction. Releasing before 12 ticks cancels the shot and returns the arrow. Drawing costs 6 stamina; holding full draw beyond 120 ticks drains 8 stamina/s and at 0 the draw collapses (arrow returned). Aim mode uses the over-shoulder camera from e02-camera (FOV 70→55°), walk speed 50% while drawn. Consumes the selected arrow type from the quiver (e17-inventory-core); a cycle input switches type. Dodge or block cancels the draw and returns the arrow.
Scope
- In: Draw/release/cancel, launch speed curve, stamina, aim mode hookup, quiver selection, bow data (shortbow)
- Out: Sweet-spot release (e05-draw-timing)
- Out: Sway and reticle (e05-aim-sway)
- Out: Ballistics (e05-arrow-projectile)
Notes
Anchor e05-bow-core: with e05-arrow-projectile (a dependency) this completes draw, aim, release and arrow physics.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given draw held for 24 ticks, when released, then an arrow spawns at 65% of full launch speed (30% + 0.5 × 70%).
- AC-2 unit Given draw held for 8 ticks, when released, then no arrow spawns and the quiver count is unchanged.
- AC-3 unit Given full draw held for 120 ticks, when held further, then stamina drains 8/s and at 0 the draw collapses with the arrow returned.
- AC-4 unit Given an empty quiver for the selected type, when fire is pressed, then the draw does not start and
ActionRejected{reason:"ammo"}is emitted. - AC-5 unit Given a drawn bow, when dodge is pressed, then the draw cancels, the arrow returns to the quiver and the dodge executes.
- AC-6 manual Given a 5-minute archery range session, when three playtesters shoot, then median rating for "the bow feels satisfying" is ≥ 4/5.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
area:simclass:archerepic:e05milestone:m2mw-e05.4Focused release window and rhythm bonus for well-timed shots
Description
Why
Draw timing gives archery a mastery curve comparable to the knight's parry (Archer fantasy: draw timing).
What
As an archer, I want a brief sweet spot at full draw so that well-timed releases are rewarded.
Focused release: releasing within 6 ticks (100 ms) after reaching full draw → +25% damage, +15% launch speed, zero sway,
FocusedShotevent (reticle pulse + chime cue). Rhythm: consecutive focused releases reduce nock time from 20 to 14 ticks, stacking up to 3 times; any unfocused shot or 3 s idle resets stacks.Scope
- In: Focused window, bonuses, rhythm stacks, events for presentation
- Out: Reticle visuals (e05-aim-sway)
- Out: Audio chime (e28-bow-audio)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given full draw reached on tick 48, when released on tick 48 or tick 53, then the shot is focused; on tick 47 or tick 54, then it is not.
- AC-2 unit Given a focused release, when the arrow spawns, then its damage template is ×1.25 and launch speed ×1.15.
- AC-3 unit Given three consecutive focused shots, when the fourth arrow is nocked, then nock time is 14 ticks; after one unfocused shot, then it returns to 20.
- AC-4 unit Given 2 rhythm stacks and 3 s without firing, when the next draw starts, then stacks are 0.
- AC-5 manual Given the archery range, when three playtesters shoot for 5 minutes, then each can hit the focused window on ≥ 50% of shots by the end and describes it as "satisfying".
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:simclass:archerepic:e05milestone:m2mw-e05.5Deterministic aim sway, breath hold and bow reticle
Description
Why
Sway and breath control make long shots a skill and keep the arrow honest to what the reticle shows.
What
As an archer, I want my aim to steady or waver based on what I am doing so that long shots demand composure.
Sway is simulated (seeded from sim RNG, so replays match): 0° for the first 60 ticks at full draw, then grows 0.1°/s up to 1.5°; walking adds 0.6°; being hit adds a 2° kick decaying over 300 ms; crouched & stationary ("braced") −40%. Hold-breath input at full draw zeroes sway for up to 3 s at 10 stamina/s. Launch direction = reticle direction + current sway offset. Reticle HUD shows draw progress and sway; optional trajectory arc preview (assist, off by default) via settings.
Scope
- In: Sway model, breath hold, braced modifier, reticle HUD, arc preview assist
- Out: Camera aim mode (e02-camera)
- Out: Lead indicator (e05-moving-targets)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given full draw held for 60 ticks while stationary, when released, then sway offset is exactly 0°.
- AC-2 unit Given full draw held 20 s, when sampled, then sway amplitude is capped at 1.5° (0.9° while braced).
- AC-3 unit Given breath hold active with 25 stamina, when 3 s pass, then sway stays 0 for 2.5 s and resumes when stamina hits 0.
- AC-4 integration Given the same seed and inputs, when two runs fire 10 swaying shots, then launch directions match bit-for-bit.
- AC-5 unit Given the arc preview assist disabled (default), when drawing, then no arc renders; enabled, then the arc matches e05-arrow-projectile's prediction within 0.2 m at 30 m.
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e31.1 Create typed settings store and options menu skeleton done
area:simclass:archerepic:e05milestone:m2mw-e05.8Fire arrows ignite flammable things; dip arrows in open flame
Description
Why
Setting a distant oil slick or rope alight is a classic archer "wait, I can do that?" — and must emerge from properties, not scripts.
What
As an archer, I want fire arrows and the ability to light any arrow in a flame so that I can ignite things at range.
Fire arrows carry the
burningproperty in flight; on impact they applyburningin a 0.3 m radius through e03-element-propagation — anyflammableobject or creature there reacts per its own rules. Dip to light: while drawing, if the arrow tip is within 0.8 m of anyburningentity for 30 ticks, the nocked arrow becomes burning. Burning arrows passing through a water volume or heavy rain are extinguished.Scope
- In: Fire payload behaviour, dip-to-light rule, extinguish in water
- Out: Fire spread rules (e03-element-propagation)
- Out: Fire visuals (e29-arrow-fx)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 integration Given any
flammableobject, when a burning arrow impacts within 0.3 m, then the object gainsburningvia element propagation on that tick. - AC-2 integration Given a non-flammable stone wall, when a burning arrow impacts, then nothing ignites and the arrow burns out after 3 s.
- AC-3 unit Given a drawn standard arrow held 0.6 m from a burning brazier for 30 ticks, when released, then the arrow carries
burning; at 29 ticks, then it does not. - AC-4 integration Given a burning arrow that passes through a water volume, when it exits, then it no longer carries
burningand ignites nothing on impact. - AC-5 manual Given a greybox room with a brazier and an oil-soaked rope bridge, when three fresh archer playtesters play, then at least one discovers dip-to-light unprompted within 5 minutes.
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:simclass:archerepic:e05milestone:m2mw-e05.9Water arrows douse fires, extinguish lights and wet surfaces
Description
Why
Dousing a torch from the shadows serves stealth, puzzles and fire-fighting through one property (multiple solutions).
What
As an archer, I want water arrows that shatter into a splash so that I can put out fires and lights and make surfaces wet.
On impact the arrow shatters and applies
wetin a 1.5 m radius and removesburningfrom anything inside it (torches, braziers, burning creatures). Extinguished light sources lower the local light level via e03-light-field. Wet surfaces then behave per element rules (e.g. conduct shock). Hitting extreme heat produces whatever e03 rules dictate (e.g. steam). Water arrows never stick.Scope
- In: Water payload, extinguish, wet application
- Out: Light-level recomputation (e03-light-field)
- Out: Steam/conduction rules (e03-element-propagation)
Notes
Related: stealth group — dousing lights increases thief/archer concealment.
Acceptance criteria
- AC-1 integration Given any
burningentity within 1.5 m of impact, when the water arrow shatters, thenburningis removed from it that tick. - AC-2 integration Given a lit torch providing light, when extinguished by a water arrow, then the light level queried at the torch's position decreases.
- AC-3 integration Given a wet floor and a shock source per e03 rules, when shock is applied, then conduction occurs across the wetted area (property-based, not arrow-specific).
- AC-4 unit Given a water arrow, when it hits anything, then it never sticks and is not retrievable.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:simclass:archerepic:e05milestone:m2mw-e05.10Rope arrows anchor into wood and create climbable ropes
Description
Why
Opening a vertical route with a rope arrow is the archer's answer to the locked tower (Classes change possibilities).
What
As an archer, I want to shoot a rope arrow into a wooden beam and climb it so that I can reach places others cannot.
Rope arrows stick only into surfaces tagged
softAnchor(the wood and earth material presets) whose normal is within 60° of horizontal or facing downward (beams, ceilings). On stone/metal they bounce off with sparks (readable hint). A hit spawns an 8 m rope entity hanging from the anchor, climbable via e02-player-controller climb hooks. Ropes areflammable(burning rope falls and drops climbers) andcuttable. Max 3 active ropes per area (oldest retracts).Scope
- In: Anchor rule, rope spawn, climb hookup, rope properties, active cap, retrieval at anchor
- Out: Climbing mechanics (e02-player-controller)
- Out: Rope visuals (e29-arrow-fx)
Notes
Ropes persist while the area is loaded; persistence across saves is not required for MVP.
Acceptance criteria
- AC-1 integration Given a wooden beam overhead, when a rope arrow hits it, then an 8 m rope spawns and the player can climb to the beam.
- AC-2 integration Given a stone wall, when a rope arrow hits it, then it bounces, no rope spawns and the arrow remains retrievable.
- AC-3 integration Given a player climbing a rope, when the rope gains
burning, then within 3 s it detaches and the player falls (environment fall rules apply). - AC-4 unit Given 3 active ropes, when a 4th is created, then the oldest retracts and returns a rope arrow at its anchor position.
- AC-5 unit Given a wooden surface whose normal points 80° upward (a floor), when a rope arrow hits it, then no rope spawns.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.22 Mark climbable surfaces and ledges from properties and geometry done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:simclass:archerepic:e05milestone:m2mw-e05.11Noise arrows create sound events that lure creatures
Description
Why
Luring a guard away with a rattling arrow is a clean, systemic distraction shared with the stealth model.
What
As an archer, I want a noise arrow that rattles where it lands so that I can pull creatures toward a spot.
On impact it emits a noise event (loudness 70, duration 2 s) into e09-sound-propagation at the impact point; how creatures react is entirely their perception/AI. In water the loudness is ×0.3. Deals no damage (blunt head, 1 blunt).
Scope
- In: Noise payload emission, water muffling, positional cue event
- Out: Noise propagation and creature reaction (stealth group)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 integration Given a noise arrow impact, when resolved, then exactly one noise event of loudness 70 lasting 120 ticks is emitted at the impact position.
- AC-2 integration Given impact inside a water volume, when resolved, then the noise event loudness is 21.
- AC-3 integration Given a creature within hearing range per e09 rules, when the noise event propagates, then the creature receives it (reaction owned by AI; test asserts delivery only).
- AC-4 unit Given a noise arrow hitting a creature, when resolved, then it deals ≤ 1 damage and causes no reaction beyond flinch rules.
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
area:simclass:archerepic:e05milestone:m2mw-e05.12Blunt arrows knock objects, press switches and stun
Description
Why
Non-lethal, physics-first arrows let the archer solve problems by shoving the world rather than killing it.
What
As an archer, I want blunt arrows that hit hard without sticking so that I can knock things over, press switches at range and stun unaware foes.
Blunt damage, penetration 0, poise damage ×2.5,
impactForceMul2.5, never sticks (drops at impact; always retrievable unless it lands in a hazard). Applies impulse to physics objects (knock a bucket off a shelf). A head hit on an unaware creature whose schema allowsknockoutrenders it unconscious for 60 s (non-lethal).Scope
- In: Blunt behaviour, impulse, knockout rule
- Out: Switch/mechanism triggering (e05-shoot-mechanisms)
- Out: Unconscious state handling in AI (e11)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 integration Given a 3 kg object on a shelf, when hit by a blunt arrow, then it receives an impulse and falls.
- AC-2 unit Given an unaware creature with
knockout: true, when hit in the head by a blunt arrow, then it becomes unconscious for 3600 ticks. - AC-3 unit Given an aware creature, when hit in the head by a blunt arrow, then it is not knocked out and takes blunt damage with ×2.5 poise damage.
- AC-4 unit Given a blunt arrow impact on any surface, when resolved, then it does not stick and is retrievable at the impact point.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e05.6 Headshots, weak points and armored regions that deflect done
area:simclass:archerepic:e05milestone:m2mw-e05.14Greybox archery range for tuning and e2e tests
Description
Why
Draw timing, drop and trick arrows need a fast, repeatable test space just as melee has the combat sandbox.
What
As a developer, I want an archery range scene so that I can tune ballistics and verify trick arrows quickly.
Static targets at 10/20/40/60 m, targets on rails at 2/4/6 m/s, a 6 m elevated platform, an overhead wooden beam (rope anchor), a brazier, a
shootablebell wired to a door, acuttablerope holding a crate, a pool of water. Stats overlay: hit %, flight time, impact speed, focused-shot rate.Scope
- In: Scene, fixtures, stats overlay, deterministic seed
- Out: Final art for the range (none planned)
Notes
Lives alongside the combat sandbox under /dev/archery.
Acceptance criteria
- AC-1 e2e Given the range loaded in Chromium, when it boots, then all fixtures exist, no console errors occur and the overlay reads 0 shots.
- AC-2 integration Given the rail targets, when 10 s elapse, then their positions match the deterministic schedule within 0.01 m.
- AC-3 integration Given 10 scripted shots at the 20 m target, when complete, then the overlay hit % equals hits/shots from sim events.
- AC-4 e2e Given the bell/door fixture, when an arrow hits the bell, then the door opens within 1 s.
- AC-5 e2e Given the archery range in Chromium, when Playwright draws for 48 ticks and releases at the 20 m target, then the target registers a hit and no console errors occur. (Moved from mw-e05.3 AC-6 by owner decision 2026-10-02.)
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e04.9 Combat sandbox scene with dummy creatures and frame-data overlay done
- mw-e05.21 Wire the bow and arrows into the game world with the aim camera done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:toolsclass:archerepic:e05milestone:m2mw-e05.15Elevation grants archery damage and range advantage
Description
Why
High ground should matter to an archer: climbing a tower should change the fight (Archer fantasy: elevation).
What
As an archer, I want shooting from above to hit harder and reach farther so that seeking high ground is a real tactic.
Range gain is natural from ballistics. Additionally: shooter at ≥ 3 m above target → +15% damage; ≥ 6 m → +25% (data-tunable). Implemented as an attacker-stage modifier in the damage pipeline tagged
elevation.Scope
- In: Elevation modifier, thresholds in data
- Out: AI reaction to unreachable attackers (e11)
Notes
Related: AI group should give melee creatures a behaviour for unreachable archers (seek path, throw rocks, take cover).
Acceptance criteria
- AC-1 unit Given a shooter 4 m above the target, when a 20-pierce arrow hits, then 23 damage applies and the hit is tagged
elevation. - AC-2 unit Given a shooter 2.9 m above, when an arrow hits, then no elevation bonus applies.
- AC-3 unit Given a 30%-draw shot from 10 m height versus ground level, when simulated, then horizontal range increases by ≥ 20%.
- AC-4 unit Given a shooter below the target, when an arrow hits, then no bonus or penalty applies.
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:simclass:archerepic:e05milestone:m2mw-e05.16Moving-target payoffs: leg stumbles, wing downs, lead assist
Description
Why
Hitting a running target should be hard and rewarding (Archer fantasy: moving targets).
What
As an archer, I want well-led shots on moving creatures to trip or ground them so that tracking a target is worth mastering.
A
limb(leg) hit on a creature moving > 3 m/s causes a stumble (60-tick knockdown for bipeds; quadrupeds slowed 50% for 3 s). Awingregion hit on a flying creature grounds it for 4 s. Optional lead-indicator assist (off by default): shows the intercept point for the lock-on target from its velocity and arrow flight time (3-iteration solver).Scope
- In: Moving-limb stumble rule, wing grounding, lead solver + indicator assist
- Out: Flying locomotion (e12)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a biped moving at 4 m/s, when a leg region is hit, then it is knocked down for 60 ticks; moving at 2 m/s, then normal hit reactions apply.
- AC-2 unit Given a flying creature, when its wing region is hit, then it is grounded for 240 ticks.
- AC-3 unit Given a target moving at a constant 5 m/s at 30 m, when the lead solver runs, then its predicted intercept is within 0.3 m of the simulated arrow impact.
- AC-4 unit Given the lead assist disabled (default), when locked on, then no indicator renders.
Waits on
- mw-e05.5 Deterministic aim sway, breath hold and bow reticle open
- mw-e05.6 Headshots, weak points and armored regions that deflect done
area:simclass:archerepic:e05milestone:m2mw-e05.17Archer mobility: quickstep while drawn and aerial shots
Description
Why
The archer's fantasy is precision and movement; they should reposition without losing a draw.
What
As an archer, I want to sidestep while holding a draw and shoot mid-jump so that I can stay mobile under pressure.
Quickstep: 12-tick sidestep with i-frames ticks 2–7, 15 stamina, retains up to 60% of current draw. Aerial shot: firing while airborne is allowed with +1.0° additional spread; draw progress continues in the air.
Scope
- In: Quickstep move, draw retention, aerial shot spread
- Out: Knight roll (e04-dodge-iframes)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given full draw, when quickstep executes, then draw fraction after it is 0.6 and i-frames cover ticks 2–7.
- AC-2 unit Given 40% draw, when quickstep executes, then draw fraction remains 0.4 (retention caps at 60%, never increases).
- AC-3 unit Given the player airborne, when an arrow is released, then its spread includes +1.0°.
- AC-4 manual Given the range with rail targets, when three playtesters use quickstep, then median rating for "I feel mobile as an archer" is ≥ 4/5.
Waits on
- mw-e04.8 Dodge roll and backstep with invulnerability frames done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:simclass:archerepic:e05milestone:m2mw-e05.20Creature projectile attacks fly as ballistic arrows and meet level geometry
Description
Why
Creature projectiles (mw-e12.5) fly in straight lines and pass through walls (the executor says level geometry does not stop them yet), while arrows (mw-e05.2) arc, collide with the level and use region-tagged hurtboxes. Two projectile models drift apart; a skeleton archer's arrow should behave like the player's.
What
Route projectile attacks through the arrow system: the attack's projectile data names an arrow content id (or a drag-free projectile body),
runActivelooses it withfireArrow(shooter = attacker), and hits go through the hurtbox query and CollisionWorld like any arrow. Keep AttackProjectileLaunched/AttackHit for AI and cues. Retire the straight-lineprojectileSystemonce no content uses it.Scope
- In: executor projectile launch via fireArrow, attack schema field, migration of shipped projectile attacks, tests.
- Out: creature bow animation, AI aiming with lead.
Discovered from mw-e05.2.
Acceptance criteria
- AC-1 unit Given a creature projectile attack at a target behind a wall, when fired, then the projectile stops at the wall and deals no damage.
- AC-2 unit Given a projectile attack at a target in range, when it hits, then AttackHit is emitted with the damage results of the struck hurtbox's region.
- AC-3 integration Given the creature attack replays, when re-recorded, then only content hashes change or the change is explained.
Waits on
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
area:simclass:archerepic:e05milestone:m2
Post-MVP 1
mw-e05.18Extended trick arrows as pure data: frost, shock, smoke
Description
Why
Proves the systemic promise: new arrows need data, not code, and immediately interact with existing world properties.
What
As a designer, I want to add frost, shock and smoke arrows purely in data so that the arrow roster grows without engineering.
Frost applies
frozen(e.g. water surfaces become walkable ice per e03 rules); shock applieselectrified(conducts through wet/conductive); smoke spawns a vision-blocking volume. No new sim code beyond what payload types already support.Scope
- In: Three arrow data entries, tests exercising each
- Out: New payload primitives (would need a separate engineering bead)
Notes
If any arrow needs code, split that capability into its own bead first.
Acceptance criteria
- AC-1 content Given the three new arrow entries, when validated, then they pass with no schema changes.
- AC-2 integration Given a water surface, when a frost arrow impacts it, then it gains
frozenper e03 rules. - AC-3 integration Given a smoke arrow impact, when resolved, then a vision-blocking volume exists for its declared duration and blocks sight queries through it.
- AC-4 unit Given the change set for this bead, when reviewed, then no files under src/sim are modified (asserted by a CI path check on the PR).
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e05.1 Data schema for arrow types and property-based payloads done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
area:contentclass:archerepic:e05milestone:post-mvp
E06Magic FrameworkA data-driven spell architecture where every spell is a content file composed of a delivery (self, projectile, AoE, beam, touch, summon point) and a list of effect operations (damage, world-property stimulus, force, status, summon, transform, displace). Spells never script pairwise interactions: they emit world stimuli (heat, cold, force, light, moisture, charge, life, decay, sound) into the systemic engine (E03), which resolves outcomes through object properties. Casting has a mana resource, cast times, channeling, interruption and botched casts that create new situations.14/25 done · 56%
M2 Class fantasies 11
mw-e06.7Deliver spells as AoE volumes, cones and reflecting beams
Description
Why
Battlefield manipulation needs areas, cones and beams, and beams that reflect enable light puzzles (puzzle constitution).
What
As a sorcerer, I want sphere, cone, line/wall and ground-decal volumes plus continuous beams so that shockwaves, fire walls and light rays are all data.
Volumes: instant or persistent with tick rate, falloff curve, line-of-sight check option. Beams: raycast each tick, length, width, reflect off objects tagged reflective up to N bounces, blocked by opaque solids.
Scope
- In: volume shapes, persistence, tick, falloff, LOS option; beam raycast, reflection, max bounces
- Out: stimulus semantics (e06-world-stimulus-emitters)
- Out: specific spells (mw-e08)
Notes
Beam reflection uses the reflective property only — any mirror, polished shield or ice surface tagged reflective works.
Acceptance criteria
- AC-1 unit Given a 4 m sphere with linear falloff, when targets stand at 0, 2 and 4.1 m, then they receive 100%, 50% and 0% magnitude.
- AC-2 unit Given a 60° cone of range 6 m, when a target is at 35° off-axis, then it is not affected; at 25°, it is.
- AC-3 unit Given a persistent volume with tick 500 ms and duration 3 s, when it expires, then exactly 6 ticks were applied.
- AC-4 unit Given a beam hitting an object tagged reflective, when maxBounces=2, then the beam reflects by the surface normal and stops after the second reflection.
- AC-5 unit Given an AoE with requiresLOS, when a target is fully behind an opaque wall, then it receives no effect.
- AC-6 perf Given 8 persistent volumes and 4 beams active in the stress scene, when simulated, then magic sim cost p95 ≤ 1.0 ms per tick on the reference machine.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
area:simclass:sorcererepic:e06milestone:m2mw-e06.10Summon temporary creatures and objects with allegiance and caps
Description
Why
Summoning (Animate Bones, Mirror Self decoys, conjured objects) is a qualitatively different verb from damage.
What
As a sorcerer, I want summon ops that spawn a creature or object archetype for a duration, optionally consuming nearby world material, so that summons feel grounded in the world.
Parameters: archetype id (from creature schema), duration, allegiance (caster/neutral), maxActive per caster, requiresMaterial {property tag, radius, count} (e.g. remains), on-expire ops, command hooks (follow/stay/attack).
Scope
- In: summon op, material consumption by property tag, lifetime, cap enforcement (oldest dismissed), allegiance faction assignment
- Out: summon AI behaviours (E11/E12 — Related: follow/guard behaviours)
- Out: specific summons (mw-e08)
Notes
Material requirement uses property tags such as remains, not specific corpse ids.
Acceptance criteria
- AC-1 unit Given maxActive=2 and two active summons, when a third is summoned, then the oldest is dismissed with its on-expire ops and the new one spawns.
- AC-2 unit Given requiresMaterial {tag: remains, radius: 6, count: 1} and no remains within 6 m, when cast, then the cast fails with reason "no-material", emits a SpellFizzled event and refunds 100% mana.
- AC-3 unit Given remains within range, when the summon succeeds, then the consumed remains entity is removed and the summon spawns at its position.
- AC-4 unit Given duration 30 s, when 30 s pass, then the summon is removed and on-expire ops apply at its location.
- AC-5 integration Given a summon with allegiance caster, when hostile creatures perceive it, then they treat it as hostile per faction rules.
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:simclass:sorcererepic:e06milestone:m2mw-e06.11Temporarily transform creatures into other archetypes and back
Description
Why
Polymorph-style effects are iconic "wait, I can do that?" magic and must restore state safely.
What
As a sorcerer, I want a transform op that swaps a creature's archetype (body, senses, attacks, properties) for a duration and restores it exactly so that turning a guard into a chicken is safe and systemic.
Preserves identity, health ratio, inventory, quest/dialogue state, alert memory; the new form uses its own senses/locomotion/properties (a transformed creature that is now flammable burns).
Scope
- In: transform op, snapshot/restore, health ratio mapping, size-safe restore (find free space), resist rules via creature tags
- Out: player self-transformation (post-MVP, Related: future spell)
- Out: specific forms (mw-e08 Polymorph)
Notes
Restore must handle the form being killed (original dies? or reverts with 1 HP — configurable per spell) and not enough space to restore (push to nearest free space).
Acceptance criteria
- AC-1 unit Given a creature at 60% health transformed into a form with different max HP, when transformed and restored, then health ratio is 60% in both forms (minus damage taken).
- AC-2 unit Given a transformed creature, when the duration ends, then inventory, faction, dialogue flags and identity id are byte-identical to the pre-transform snapshot.
- AC-3 unit Given a creature tagged immune-transform, when targeted, then the op fails with reason "resisted" and emits SpellResisted.
- AC-4 unit Given the transformed form is killed and the spell sets onFormDeath=revert, when killed, then the original reverts with 1 HP and a stagger status.
- AC-5 unit Given insufficient space to restore a large creature, when reverting, then it is placed at the nearest free position within 3 m, or the revert is deferred until space exists.
- AC-6 integration Given a save made while a creature is transformed, when loaded, then the transform remaining duration and snapshot restore correctly.
Waits on
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e30.1 Define versioned save format with section registry and migrations done
area:simclass:sorcererepic:e06milestone:m2mw-e06.12Teleport and swap positions with collision-safe placement
Description
Why
Teleportation opens traversal routes (classes change possibilities) and must never put the player inside walls.
What
As a sorcerer, I want displace ops (teleport-to-point, swap-with-target, short dash) with safe placement so that Blink-like spells are reliable and exploitable.
Validates destination: capsule fits, not inside solid geometry, optional line-of-sight requirement, ledge/floor snapping, falls through if no floor (story!).
Scope
- In: displace op handler, capsule fit test, LOS option, nearest-valid fallback search, swap semantics
- Out: specific spells (mw-e08 Blink)
Notes
Teleporting over a chasm with no floor intentionally drops the caster — failure creates stories.
Acceptance criteria
- AC-1 unit Given a teleport target inside a wall, when resolved, then the caster lands at the nearest valid point along the aim ray before the wall, never inside geometry (1000 seeded fuzz trials).
- AC-2 unit Given requiresLOS and a target point behind a closed door, when cast, then it fails with reason "no-line-of-sight".
- AC-3 unit Given a swap-with-target op on a creature too large to fit at the caster's position, when cast, then the swap is rejected with reason "no-space" and mana is refunded.
- AC-4 unit Given a teleport destination with no floor within 1 m, when cast, then the caster arrives at the destination and begins falling.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
area:simclass:sorcererepic:e06milestone:m2mw-e06.13Resolve botched casts into data-driven miscast outcomes
Description
Why
Failure creates stories: a botched spell should produce a new situation (smoke, a wild blink, a sudden frost) instead of a dull "fizzle".
What
As a sorcerer, I want interrupted, over-cast or poorly-understood spells to sometimes misfire in school-flavoured ways so that mistakes are memorable.
Miscast triggers: over-cast (mana deficit), late interruption, low comprehension (from E07), casting while a status like drenched/charged conflicts. A weighted miscast table per school (and optional per spell) picks outcomes composed from existing ops: backfire on self at reduced magnitude, random-direction delivery, harmless cosmetic, loud noise event, stimulus burst at caster (e.g. frost puddle, smoke).
Scope
- In: miscast request handling, severity calculation, weighted tables in content, seeded selection, MiscastOccurred event
- Out: comprehension model (e07-comprehension-study)
- Out: VFX for miscasts (placeholder cue per school)
Notes
Miscast outcomes must never softlock or instakill: cap self-damage at 25% max HP. Every outcome reuses existing ops so miscasts interact with the world too (a fire miscast can ignite the bookshelf you are standing next to).
Acceptance criteria
- AC-1 unit Given a miscast request with severity 0.1, when resolved with a fixed seed, then the outcome is drawn from the table's low-severity band and is deterministic across runs.
- AC-2 unit Given any miscast outcome, when applied to a caster at full HP, then self-damage never exceeds 25% max HP (property test over all tables).
- AC-3 integration Given a Fire-school miscast producing a heat burst at the caster, when a flammable object is within the burst radius, then it ignites via E03 rules.
- AC-4 content Given a miscast table whose weights sum to 0 or referencing an unknown op, when validated, then validation fails.
- AC-5 unit Given a miscast outcome "loud", when applied, then a noise event of the configured loudness is emitted for stealth/AI.
- AC-6 manual Given a playtest of 20 deliberate over-casts, when observed, then at least 3 distinct outcome types occur and testers describe them as funny or interesting rather than punishing.
Waits on
- mw-e06.3 Track mana pool, regeneration and spell costs done
- mw-e06.4 Support cast times, channeling, cooldowns and interruption done
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
area:simclass:sorcererepic:e06milestone:m2mw-e06.17Bind spell events to placeholder VFX and SFX cues
Description
Why
Magic must look and sound magical even in grey-box; placeholders unblock gameplay until the art pass (gameplay is never blocked by final art).
What
As a developer, I want spell events (wind-up, release, travel, impact, persistent, miscast) to trigger named VFX and SFX cues so that swapping placeholders for final assets is a data change.
Cue registry maps cue id → placeholder effect (coloured particles per school, primitive meshes) and placeholder sound; missing cue logs a warning and plays a school default.
Scope
- In: cue registry, event→cue binding in src/game, school-coloured placeholder particle presets, placeholder SFX hooks
- Out: final VFX (E29 asset beads)
- Out: final SFX (E28/E38 asset beads)
Notes
Cue naming follows asset ID convention: vfx-spell-<spell>-<phase>, sfx-spell-<spell>-<phase>.
Acceptance criteria
- AC-1 integration Given a spell impact event with vfxCue "vfx-spell-ember-impact", when emitted, then the VFX system receives a spawn request for that cue at the impact position.
- AC-2 integration Given a cue id with no registered asset, when triggered, then the school default placeholder plays and one warning is logged per cue id (not per trigger).
- AC-3 content Given every spell in the catalogue, when validated, then each referenced cue id matches the naming convention.
- AC-4 e2e Given the greybox testbed, when every catalogue spell is cast in sequence, then no console errors occur and a screenshot sanity check detects non-background pixels at each impact.
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.10 Spell VFX slots and templates for the spell pipeline done
area:gameclass:sorcererepic:e06milestone:m2mw-e06.18Persist mana, cooldowns and active spell effects in saves
Description
Why
Save-game stability is a technical pillar; a fire wall or summon must survive save/load exactly.
What
Serialize mana, cooldowns, in-progress channel state (cancelled on load), persistent volumes, summons and statuses into the versioned save format with round-trip and migration tests.
Scope
- In: magic save section, round-trip tests, migration test scaffold
- Out: known spells and study progress (e07-learning-save-state)
- Out: transform snapshots (e06-transformation-framework)
Notes
In-flight projectiles are dropped on save (documented) unless trivial to keep.
Acceptance criteria
- AC-1 unit Given a sim with 2 persistent volumes, 1 summon and 3 statuses, when saved and loaded, then state hashes of the magic subsystem match.
- AC-2 unit Given a save while channeling, when loaded, then the channel is cancelled and no mana is lost beyond ticks already applied.
- AC-3 unit Given a save from magic schema v1, when loaded by v2 code, then the migration test passes.
Waits on
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
- mw-e30.1 Define versioned save format with section registry and migrations done
area:simclass:sorcererepic:e06milestone:m2mw-e06.22Prove spell cold freezes wet and waterSurface things (split from mw-e06.5 AC-2)
Description
Split from mw-e06.5 (its AC-2) because the freeze rule is mw-e03.7, not yet built. Once e03.7 lands, add the integration test through the spell pipeline.
Acceptance criteria
AC-1 integration Given any wet or waterSurface tile inside a spell cold stimulus >= its freeze threshold, when applied, then it gains the frozen property for the stimulus duration plus melt time. Generic property-tagged fixtures only (no named props).
Waits on
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
area:simclass:sorcererepic:e06milestone:m2mw-e06.23Prove spell charge conducts through connected conductive and wet objects (split from mw-e06.5 AC-3)
Description
Split from mw-e06.5 (its AC-3) because conduction is mw-e03.8, not yet built. Once e03.8 lands, add the integration test through the spell pipeline.
Acceptance criteria
AC-1 integration Given a spell charge stimulus hitting a conductive object that touches other conductive or wet objects, when applied, then charge propagates to the connected objects per E03 rules. Generic property-tagged fixtures only.
Waits on
- mw-e03.8 Conduct electrical charge through conductive and wet objects done
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
area:simclass:sorcererepic:e06milestone:m2mw-e06.19Let creatures and NPCs cast catalogue spells through the same pipeline
Description
Why
Enemy mages should obey the same rules as the player so that interrupts, miscasts and world interactions apply to them too.
What
As a designer, I want creature definitions to list spells with usage hints so that goblin shamans and liches cast via the standard pipeline.
Exposes a cast-request API for AI; creature mana optional; AI can be interrupted and miscast.
Scope
- In: creature schema extension for spells (via E12 hooks), AI cast API, tests
- Out: AI decision-making about when to cast (E11 — Related: combat behaviours)
Notes
Player-learnable spells dropped as books by enemy casters is handled in e07-book-acquisition-hooks.
Acceptance criteria
- AC-1 unit Given a creature with a fixture projectile spell, when AI requests a cast at a target, then the pipeline processes it identically to a player cast (same events).
- AC-2 unit Given a creature caster hit hard during wind-up, when poise breaks, then its cast is interrupted.
- AC-3 content Given a creature referencing an unknown spell id, when validated, then validation fails.
Waits on
- mw-e06.4 Support cast times, channeling, cooldowns and interruption done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:simclass:sorcererepic:e06milestone:m2mw-e06.20Add magic debug commands and stimulus visualisation
Description
Why
Fast content creation needs designers to grant spells, ignore mana and see stimuli while testing combinations.
What
Debug console commands: spell.grant <id|all>, spell.infiniteMana, spell.noCooldown, spell.forceMiscast <table>, spell.reload (hot-reload spell JSON); overlay drawing active stimulus volumes, beams and summon lifetimes.
Scope
- In: console commands, hot reload of spell content in dev builds, debug overlay
- Out: general console (e33-debug-console)
Notes
Hot reload re-validates; invalid file keeps the previous version and prints errors.
Acceptance criteria
- AC-1 integration Given the debug console, when "spell.grant all" runs, then every catalogue spell is known and assignable.
- AC-2 integration Given a spell file edited with an invalid value, when hot-reloaded, then the previous definition remains active and the validation error is printed.
- AC-3 integration Given spell.infiniteMana on, when casting 100 spells, then mana never decreases.
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsclass:sorcererepic:e06milestone:m2
E07Spellbook & Magical LearningSpells are discovered through books, shops, exploration, quests, secrets and experimentation rather than appearing on level-up. Books are inventory items with language, condition, comprehension difficulty and legality; reading and study teach spells; damaged, untranslated and forbidden books create side-goals; experimentation with the world reveals new verbs. Progression unlocks new verbs (Firebolt → Flame Jet → Fire Wall), never +% tiers.3/13 done · 23%
M2 Class fantasies 10
mw-e07.3Model comprehension so hard books teach unstable spells first
Description
Why
Difficult books should be a challenge, not a lock; casting a half-understood spell is a botched-cast story generator.
What
As a sorcerer, I want difficult books to be learnable early at low comprehension so that I can cast advanced spells unreliably until I understand them.
Comprehension = f(book difficulty, related known spells in same school, insights, reference books owned). Below threshold 1.0 the spell is "unstable": miscast chance scales with (1 − comprehension). Re-study or related learning raises comprehension.
Scope
- In: comprehension formula in data, unstable spell state, miscast chance hook into e06-botched-casts, UI-facing values
- Out: miscast outcomes (e06-botched-casts)
Notes
Anti-grind: comprehension rises from knowledge (books, insights, related spells), not from casting count alone.
Acceptance criteria
- AC-1 unit Given difficulty 0.8 and no related knowledge, when learned, then comprehension is 0.2 and the spell is flagged unstable.
- AC-2 unit Given comprehension 0.2, when cast 1000 times with a seeded RNG, then miscast rate is 80% ± 3%.
- AC-3 unit Given the player learns two more spells of the same school, when comprehension is recomputed, then it increases by the configured per-related-spell bonus and caps at 1.0.
- AC-4 unit Given comprehension reaches 1.0, when computed, then the unstable flag clears and no comprehension miscasts occur.
- AC-5 content Given a comprehension config with negative bonuses, when validated, then validation fails.
Waits on
- mw-e06.13 Resolve botched casts into data-driven miscast outcomes open
- mw-e07.2 Learn spells by reading and studying book items done
area:simclass:sorcererepic:e07milestone:m2mw-e07.6Give forbidden books social risk and whispering side effects
Description
Why
Forbidden knowledge is tempting; carrying or reading it should create risk and stories rather than a simple lock.
What
As a sorcerer, I want forbidden books to teach powerful spells at a social cost so that I decide whether to be seen with them.
Forbidden books are contraband: guards/NPCs who perceive the player reading or openly casting forbidden-school spells react (disposition drop, confiscation attempt, alert). Some carry "whispers" side effects (noise events at night, attracting certain creatures).
Scope
- In: legality flag handling, witness detection via perception hooks, disposition/world-state consequences, whisper effect
- Out: guard dialogue content (E22/E24)
- Out: buying/selling forbidden stock (e21-stolen-forbidden-stock)
Notes
Necromancy school spells (e.g. Animate Bones) come from forbidden books in the MVP.
Integration note: Promoted — Animate Bones (P1/m2) is taught by a forbidden book.
Acceptance criteria
- AC-1 integration Given an NPC with a lawful disposition that perceives the player reading a forbidden book, when perception succeeds, then a ContrabandWitnessed event fires and the NPC's disposition drops by the configured amount.
- AC-2 integration Given no witness has line of sight, when the player reads a forbidden book, then no consequence fires.
- AC-3 unit Given a book with whispers, when carried in inventory between 22:00 and 04:00 in-game, then a quiet noise event is emitted every 60 s at the player position.
- AC-4 unit Given a book dropped or stashed, when whispers would emit, then they emit at the book's location instead.
- AC-5 unit Given a forbidden spell learned, when cast unseen, then no ContrabandWitnessed event fires.
Waits on
- mw-e07.2 Learn spells by reading and studying book items done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simclass:sorcererepic:e07milestone:m2mw-e07.7Discover spell insights and variants through world experimentation
Description
Why
"Wait… I can do that?" — some magic should be discovered by experimenting, not bought.
What
As a sorcerer, I want casting my spells in unusual world situations to occasionally reveal an insight so that experimentation teaches new verbs.
Insight rules are data: trigger = (spell-sourced stimulus type, target property/state, optional second stimulus) observed by the sim; reward = new spell, spell variant, or comprehension boost. Example rules (property-based): cold stimulus on a water surface → insight "ice bridge" (unlocks Frostglass surface variant); force stimulus on a burning object → insight "flung flame".
Scope
- In: insight rule schema, observer on stimulus-resolution events, one-time unlocks, InsightDiscovered event + journal entry
- Out: the unlocked spells themselves (mw-e08)
Notes
Rules may only reference stimulus types and property tags, never object or spell ids for triggers (validator enforces).
Acceptance criteria
- AC-1 unit Given an insight rule {stimulus: cold, targetProperty: waterSurface}, when a player-sourced cold stimulus freezes any water surface, then InsightDiscovered fires once and the reward is granted.
- AC-2 unit Given the same condition caused by a non-player source (a frost trap), when resolved, then no insight fires.
- AC-3 unit Given an insight already discovered, when its trigger recurs, then nothing is granted again.
- AC-4 content Given an insight rule whose trigger references an object id, when validated, then validation fails ("triggers must use properties").
- AC-5 integration Given a save after discovering an insight, when loaded, then the insight remains discovered.
Waits on
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e07.2 Learn spells by reading and studying book items done
area:simclass:sorcererepic:e07milestone:m2mw-e07.8Unlock follow-on spell verbs through mastery and advanced books
Description
Why
The anti-grind rule: progression unlocks new verbs (Firebolt → Flame Jet → Fire Wall), not +12% tiers.
What
As a sorcerer, I want mastering a spell to open its next verb, learnable by study, insight or advanced book, so that growth changes what I can do.
Verb chain data: spell.next[] with unlock requirements (comprehension 1.0, N meaningful uses — e.g. hits on distinct target types, an insight, or owning an advanced book). A content validator rejects chain steps that differ only numerically from their predecessor (same delivery + same op types).
Scope
- In: verb chain data, unlock evaluation, integration with e19 progression model, anti-grind validator
- Out: specific chains (mw-e08: Ember/Firebolt/Flame Jet/Fire Wall, Mage Hand → Telekinetic Throw & Catch)
Notes
"Meaningful uses" counts distinct contexts (distinct property tags affected), not raw cast count.
Acceptance criteria
- AC-1 unit Given Firebolt with next Flame Jet requiring comprehension 1.0 and 5 distinct affected property tags, when both are met, then Flame Jet becomes studyable and a VerbUnlocked event fires.
- AC-2 unit Given 50 casts affecting only 1 property tag, when evaluated, then Flame Jet remains locked.
- AC-3 content Given a chain step with the same delivery and op types as its predecessor differing only in magnitudes, when validated, then validation fails with "numeric-only progression".
- AC-4 unit Given an advanced book that teaches a chain step directly, when studied before requirements are met, then the spell is learned but unstable (comprehension ≤ 0.5).
- AC-5 integration Given the e19 progression model, when a verb unlocks, then it is recorded as a capability unlock.
Waits on
- mw-e07.3 Model comprehension so hard books teach unstable spells first open
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:simclass:sorcererepic:e07milestone:m2mw-e07.9Read books in a page-turning reader with a study action
Description
Why
Books are the sorcerer's core reward; reading one should feel like opening a treasure, not a tooltip.
What
As a sorcerer, I want to open a book into a two-page reader showing lore, diagrams and a Study button with time estimate so that reading is a moment.
Shows garbled text for unknown language, torn-page placeholders for missing pages, forbidden warning, comprehension estimate, "already known" state.
Scope
- In: reader UI, study action with time estimate, placeholder parchment/page art, keyboard + gamepad navigation, text scaling via settings
- Out: final book/parchment art (asset prompt beads)
Notes
Honour E31 text sizing and dyslexia-friendly font option if available.
Acceptance criteria
- AC-1 integration Given a book with 6 pages, when opened, then pages 1–2 show and next/prev keys navigate in pairs without going past the last page.
- AC-2 integration Given a damaged book, when a missing page is shown, then a torn-page placeholder renders with the text "Pages missing".
- AC-3 integration Given the Study button, when pressed, then the displayed estimate matches the sim's computed remaining study minutes.
- AC-4 integration Given the player is in combat (Alerted/Combat nearby), when Study is pressed, then it is disabled with a tooltip reason.
- AC-5 e2e Given the greybox testbed, when a fixture book is opened and closed with the keyboard, then no console errors occur.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e07.2 Learn spells by reading and studying book items done
area:uiclass:sorcererepic:e07milestone:m2mw-e07.10Browse known spells, insights and verb chains in the grimoire
Description
Why
The grimoire is the sorcerer's trophy case and planning tool — seeing locked next verbs motivates exploration.
What
As a sorcerer, I want a grimoire listing known spells by school with description, cost, comprehension, world interactions discovered and next verbs (silhouetted if locked) so that I plan experiments and hotbar loadouts.
Drag spell to hotbar; filter by school; insights journal page; "rumoured spells" section fed by NPC rumour hooks.
Scope
- In: grimoire UI, school tabs, drag-to-hotbar, insight journal page, locked-verb silhouettes, rumoured spells list
- Out: hotbar (e06-spell-hotbar-ui)
- Out: final grimoire art (asset prompt beads)
Notes
"World interactions discovered" lists property tags the player has personally affected with that spell — reinforcing the systemic model.
Acceptance criteria
- AC-1 integration Given 5 known spells across 3 schools, when the grimoire opens on the Fire tab, then only Fire spells are listed.
- AC-2 integration Given a spell with a locked next verb, when viewed, then the next verb shows as a silhouette with its unlock requirement text.
- AC-3 integration Given a spell dragged onto hotbar slot 4, when dropped, then slot 4 holds that spell.
- AC-4 integration Given no known spells (non-sorcerer start), when opened, then an empty state is shown without errors.
- AC-5 integration Given the player ignited 3 distinct property-tagged objects with Ember, when viewing Ember, then those 3 property tags are listed as discovered interactions.
Waits on
- mw-e06.16 Select spells from a hotbar and quick-select wheel with mana HUD done
- mw-e07.2 Learn spells by reading and studying book items done
area:uiclass:sorcererepic:e07milestone:m2mw-e07.11Find books on bookshelves, enemies and as rewards
Description
Why
The world rewards curiosity: bookshelves can contain books, and casters carry the books they learned from.
What
As a sorcerer, I want to search bookshelves, loot books from enemy casters and receive books from quests or characters so that books come from everywhere, not just shops.
Bookshelf-tagged objects have searchable loot tables (mostly mundane lore, occasional spell book); creature casters optionally drop a book for one of their spells; quest/dialogue reward actions can grant books.
Scope
- In: bookshelf search interaction, book entries in loot tables, caster book drop rule, grant-book action for quest/dialogue
- Out: specific placements (E24–E26 world content)
- Out: shops (mw-e21)
Notes
Searching a bookshelf takes 2 s and makes a quiet noise (thief interplay).
Acceptance criteria
- AC-1 integration Given a bookshelf-tagged object with a loot table containing a spell book, when searched with a fixed seed, then the book is added to inventory and the shelf becomes "searched" in world state.
- AC-2 integration Given a searched bookshelf, when searched again, then nothing new is granted.
- AC-3 unit Given a creature caster with dropsSpellBook=true, when killed, then its loot contains a book teaching one of its spells.
- AC-4 integration Given a dialogue/quest action grantBook, when executed, then the book is added to inventory.
- AC-5 unit Given a full inventory (if capacity limits exist), when a book is found, then it is placed on the ground at the player position rather than lost.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e07.1 Define book item schema: spell, language, condition, legality done
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simclass:sorcererepic:e07milestone:m2mw-e07.12Persist known spells, study progress, languages and insights
Description
Why
Losing a learned spell to a save bug would betray the core sorcerer reward loop.
What
Serialize known spells + comprehension, per-book study progress, known languages, discovered insights, verb unlock counters into the versioned save with round-trip and migration tests.
Scope
- In: learning save section, round-trip tests, migration scaffold
- Out: inventory contents (E17/E30)
Notes
Unknown spell ids in a save (content removed) are dropped with a warning, not a crash.
Acceptance criteria
- AC-1 unit Given 4 known spells, 2 in-progress studies, 1 language and 2 insights, when saved and loaded, then all values are identical.
- AC-2 unit Given a save referencing a spell id no longer in content, when loaded, then the spell is dropped, a warning is logged and load succeeds.
- AC-3 unit Given a v1 learning save, when loaded by v2, then the migration test passes.
Waits on
- mw-e07.2 Learn spells by reading and studying book items done
- mw-e30.1 Define versioned save format with section registry and migrations done
area:simclass:sorcererepic:e07milestone:m2mw-e07.4Restore damaged books by finding their missing pages
Description
Why
Damaged books turn a spell into an exploration hook: the rest of the pages are somewhere in the monastery.
What
As a sorcerer, I want a damaged book to teach a partial version of its spell and point me towards missing pages so that finding pages completes it.
Missing pages are separate page items (loot) linked by bookId; a damaged book teaches nothing, lore-only, or an unstable variant as configured; combining pages restores the book.
Scope
- In: page items, combine action, partial-teach behaviour, restoration event
- Out: placement of pages in the world (E24–E26 content)
Notes
Missing page text may hint at location (Related: world-content group authors hints).
Acceptance criteria
- AC-1 unit Given a damaged book missing pages 3 and 7 configured partialTeach=unstable, when studied, then the spell is learned with comprehension capped at 0.5.
- AC-2 unit Given the player holds both missing page items, when combining, then the book becomes intact, the page items are consumed and the comprehension cap lifts.
- AC-3 unit Given the player holds only page 3, when combining, then the book remains damaged with only page 7 missing.
- AC-4 content Given a page item referencing a book that is not damaged, when validated, then validation fails.
Waits on
- mw-e07.3 Model comprehension so hard books teach unstable spells first open
area:simclass:sorcererepic:e07milestone:m2mw-e07.5Translate books written in unknown languages
Description
Why
Untranslated books create quests and reasons to talk to scholars and find lexicons — curiosity rewarded.
What
As a sorcerer, I want books in unknown languages to be unreadable until I obtain a lexicon or a translation so that decoding knowledge feels earned.
Languages are data; the player knows languages via lexicon items or NPC translation service (dialogue action); partially known languages show garbled text proportional to knowledge.
Scope
- In: language registry, known-language state, lexicon items, translate action hook for dialogue, garbled text renderer data
- Out: NPC translator dialogue content (E22/E24)
Notes
Garbling is deterministic per book + knowledge level so the same text appears on re-read.
Acceptance criteria
- AC-1 unit Given a book in "Old Tessic" and no knowledge, when read, then study is disabled with reason "language" and pages render garbled.
- AC-2 unit Given the player acquires the Old Tessic lexicon item, when reading, then the book is studyable.
- AC-3 unit Given 50% language knowledge, when rendering page text, then the same deterministic ~50% of words are garbled on every read.
- AC-4 integration Given a dialogue action "translate book X", when executed, then the book gains a translated flag and is studyable regardless of language knowledge.
Waits on
- mw-e07.2 Learn spells by reading and studying book items done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:simclass:sorcererepic:e07milestone:m2
E08Spell CatalogueAn MVP library of ~20 spells across Fire, Frost, Storm, Arcane, Illusion, Alteration, Conjuration, Necromancy, Nature, Light, Shadow, Gravity and Time, emphasising qualitatively different effects: ignition, freezing, force, telekinesis, teleportation, decoys, restraint, summoning, gravity, transformation, light, darkness, water and time. Every spell interacts with the world only through properties and stimuli, and ridiculous combinations are encouraged.3/25 done · 12%
M2 Class fantasies 22
mw-e08.1Document magic schools, spell data conventions and too-useful policy
Description
Why
Twenty spells authored by different people must feel like one coherent magic system.
What
docs/design/magic-schools.md: each school's fantasy, primary stimuli, colour tag for placeholder VFX, default loudness, miscast flavour; spell file template; naming for cue/icon/cover ids; testing template (effects + ≥2 property interactions); explicit "too useful is allowed" policy and the too-useful label.
Scope
- In: design doc
- In: spell file template in src/content/spells/_template
- In: test file template
- Out: individual spells
Notes
Schools in MVP: Fire, Frost, Storm, Arcane, Illusion, Alteration, Conjuration, Necromancy, Nature, Light, Shadow, Gravity, Time.
Acceptance criteria
- AC-1 content Given the spell template file, when validated by the spell schema, then it passes.
- AC-2 content Given the magic-schools doc, when checked by a docs test, then every school enum value in the schema has a section.
- AC-3 manual Given a designer new to the project, when authoring a fixture spell from the template, then they finish in ≤ 30 minutes without code changes.
Waits on
- mw-e06.1 Define zod spell schema with delivery, effect ops and cue ids done
area:designclass:sorcererepic:e08milestone:m2mw-e08.3Add Firebolt and its Flame Jet follow-on verb
Description
Why
The canonical anti-grind chain starts here: Firebolt is a verb, Flame Jet is a new verb, not +12%.
What
As a sorcerer, I want Firebolt (fast damaging bolt that ignites on impact) and, once unlocked, Flame Jet (channeled short cone that sets everything in front ablaze and pushes gas) so that fire progression changes how I fight.
Flame Jet is a chain step of Firebolt with a different delivery (cone channel).
Scope
- In: Firebolt spell
- In: Flame Jet spell
- In: verb chain link Firebolt → Flame Jet
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Fire. Placeholder cues: vfx-spell-firebolt-{cast,travel,impact}, vfx-spell-flame-jet-channel, sfx-spell-firebolt-{cast,impact}, sfx-spell-flame-jet-loop. Icon: icon-spell-firebolt. Book cover: ui-book-cover-firebolt.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given any flammable object hit by Firebolt, when resolved, then it ignites and a 1 m heat splash applies around the impact.
- AC-3 integration Given Flame Jet channeled for 2 s at a group of objects, when resolved, then every flammable object in the cone ignites and non-flammable ones take no fire state.
- AC-4 integration Given a gas volume tagged flammableGas in the Flame Jet cone, when channeled, then the gas ignites per E03 propagation (explosion allowed).
- AC-5 content Given the Firebolt → Flame Jet chain, when checked by the anti-grind validator, then it passes (different delivery).
- AC-6 unit Given Flame Jet channeled with mana running out mid-channel, when mana reaches 0, then channeling stops cleanly (over-cast disallowed).
Waits on
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.6 Deliver spells as physical projectiles with collision and arcs done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
- mw-e07.8 Unlock follow-on spell verbs through mastery and advanced books open
area:contentclass:sorcererepic:e08milestone:m2mw-e08.5Add Frostglass: freeze targets and create temporary ice surfaces
Description
Why
Constitution example; freezing water is a key puzzle verb and ice surfaces are traversal and combat tools.
What
As a sorcerer, I want a frost bolt that freezes what it hits and, aimed at water or ground, creates a temporary slick ice sheet so that I can freeze enemies, cross water and make foes slip.
Cold stimulus at impact; creatures accumulate chill → slowed → frozen-solid (shatterable by force); water surfaces become walkable ice for 20 s; ground becomes slippery (low friction).
Scope
- In: Frostglass spell
- In: ice sheet surface volume
- In: frozen-solid shatter rule via force
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Frost. Placeholder cues: vfx-spell-frostglass-{cast,travel,impact}, vfx-spell-frostglass-ice-sheet, sfx-spell-frostglass-{cast,impact,shatter}. Icon: icon-spell-frostglass. Book cover: ui-book-cover-frostglass.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given any
waterSurfacearea, when hit by Frostglass, then a walkable frozen patch of 3 m radius exists for 20 s then melts back to water. - AC-3 integration Given a creature hit 3 times within 4 s, when resolved, then it becomes frozen-solid for 3 s; a force ≥ threshold during that time shatters the ice and deals bonus damage.
- AC-4 integration Given a creature walking onto an ice sheet, when moving, then its friction is reduced per E03 and it can slip (knockdown chance per creature balance data).
- AC-5 integration Given a burning object hit by Frostglass, when resolved, then it is extinguished.
- AC-6 unit Given a creature tagged cold-immune, when hit, then no chill accumulates.
Waits on
- mw-e06.22 Prove spell cold freezes wet and waterSurface things (split from mw-e06.5 AC-2) open
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.6 Deliver spells as physical projectiles with collision and arcs done
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.6Add Wellspring: conjure a burst of water that soaks an area
Description
Why
Conjured water is the connective tissue for combinations: douse fires, fill basins, soak foes for frost and lightning.
What
As a sorcerer, I want to conjure a splash of water at a point so that I can extinguish fires, make things wet and fill containers.
AoE moisture stimulus 3 m; creates a temporary puddle (
waterSurface) for 15 s; fills container-tagged objects (basins, scales) with water weight.Scope
- In: Wellspring spell
- In: temporary puddle volume
- In: container fill via moisture
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Conjuration. Placeholder cues: vfx-spell-wellspring-{cast,burst}, vfx-spell-wellspring-puddle, sfx-spell-wellspring-{cast,splash}. Icon: icon-spell-wellspring. Book cover: ui-book-cover-wellspring.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given burning objects inside the 3 m area, when Wellspring bursts, then they are extinguished and gain wet.
- AC-3 integration Given a creature in the area, when hit, then it gains wet for 20 s, which E03 rules make conductive and freezable.
- AC-4 integration Given an object tagged container under the burst, when filled, then its weight increases by the configured water mass (can trigger pressure plates).
- AC-5 unit Given Wellspring cast in a space with no floor within 10 m, when resolved, then water falls and the puddle forms where it lands or nowhere if it falls out of world.
Waits on
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
area:contentclass:sorcererepic:e08milestone:m2mw-e08.7Add Gust: blast creatures back, clear gas and propel objects
Description
Why
Constitution example; force + gas-clearing gives a cheap, flexible physics verb.
What
As a sorcerer, I want a cone of wind that pushes creatures and loose objects and disperses gas and smoke so that I can shove foes off ledges or clear poison.
Cone force impulse scaled by weight; displaces gas volumes; fans existing fires (spreads heat downwind) and blows out small flames.
Scope
- In: Gust spell
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Storm. Placeholder cues: vfx-spell-gust-cone, sfx-spell-gust-whoosh. Icon: icon-spell-gust. Book cover: ui-book-cover-gust.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given loose objects of 5 kg and 80 kg in the cone, when Gust fires, then the 5 kg object is thrown ≥ 4 m and the 80 kg object moves ≤ 0.5 m.
- AC-3 integration Given a poisonous gas volume in the cone, when Gust fires, then the gas concentration in the cone drops by ≥ 80% and relocates downwind.
- AC-4 integration Given a small flame (candle-scale light source) in the cone, when Gust fires, then it is extinguished; a large fire instead spreads 1 m downwind.
- AC-5 integration Given a creature near a ledge, when pushed past the edge, then it falls and takes fall damage per creature rules.
- AC-6 unit Given a creature tagged anchored/heavy, when hit, then it staggers but is not displaced.
Waits on
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
- mw-e06.8 Apply spell forces: impulse, knockback, pull and lift done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.8Add Thunderclap: a violent shockwave that breaks and deafens
Description
Why
Constitution example: short-range shockwave with substantial environmental effects — and it is loud.
What
As a sorcerer, I want a point-blank radial shockwave that knocks creatures down, breaks fragile objects and shakes loose unstable things so that I can reshape a room in one cast.
Radial force + fragile-break + "unstable" property trigger (loose rubble, chandeliers) + very loud noise event + short deafened status on creatures (reduced hearing).
Scope
- In: Thunderclap spell
- In: deafened status
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Storm. Placeholder cues: vfx-spell-thunderclap-wave, sfx-spell-thunderclap-boom. Icon: icon-spell-thunderclap. Book cover: ui-book-cover-thunderclap.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given any object tagged fragile within 4 m, when Thunderclap fires, then it breaks.
- AC-3 integration Given any object tagged unstable (hanging, precarious) within 6 m, when Thunderclap fires, then it detaches and falls under physics.
- AC-4 integration Given creatures within 4 m, when hit, then they are knocked down if poise is exceeded and gain deafened for 5 s.
- AC-5 integration Given sleeping or Unaware creatures within audible range of loudness 100, when Thunderclap fires, then they transition to at least Suspicious via the alert state machine.
- AC-6 unit Given the caster standing on an ice sheet, when cast, then the caster is not displaced by their own shockwave (self-exempt) but the ice cracks if tagged fragile.
Waits on
- mw-e06.14 Make spells emit light and noise that stealth and AI perceive done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
- mw-e06.8 Apply spell forces: impulse, knockback, pull and lift done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.9Add Arc Lash: lightning that chains through conductive and wet
Description
Why
Electricity rewards reading the environment: wet enemies, chains, puddles and metal armour become conductors.
What
As a sorcerer, I want a short beam of lightning that jumps between conductive or wet targets so that soaking enemies or luring them onto metal pays off.
Beam with charge stimulus; E03 conduction propagates through connected conductive/wet objects and water surfaces; powers chargeActivated mechanisms.
Scope
- In: Arc Lash spell
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Storm. Placeholder cues: vfx-spell-arc-lash-beam, vfx-spell-arc-lash-chain, sfx-spell-arc-lash-crackle. Icon: icon-spell-arc-lash. Book cover: ui-book-cover-arc-lash.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given three wet creatures standing in a connected puddle, when one is hit, then all three receive charge damage via propagation.
- AC-3 integration Given a dry creature not touching conductive objects, when hit, then only that creature is affected.
- AC-4 integration Given any mechanism tagged chargeActivated, when hit by Arc Lash, then it activates.
- AC-5 integration Given the caster standing in the same puddle as the target, when cast, then the caster also receives charge damage (failure creates stories).
- AC-6 unit Given a creature tagged insulated, when in a conductive chain, then propagation stops at it.
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e06.23 Prove spell charge conducts through connected conductive and wet objects (split from mw-e06.5 AC-3) open
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
area:contentclass:sorcererepic:e08milestone:m2mw-e08.11Add Telekinetic Throw & Catch: hurl objects, creatures and arrows
Description
Why
Anti-grind example from the constitution: telekinesis → throwing creatures → catching projectiles.
What
As a sorcerer, I want Mage Hand's follow-on verb to fling held objects (or small creatures) and snatch incoming projectiles from the air so that telekinesis becomes a combat tool.
Throw applies a large impulse to the held entity; creatures under a size tag can be grabbed; catch window 0.4 s freezes an incoming projectile, which can then be thrown back.
Scope
- In: Telekinetic Throw spell (chain step of Mage Hand)
- In: projectile catch
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Arcane. Placeholder cues: vfx-spell-telekinetic-throw-{launch,catch}, sfx-spell-telekinetic-throw-{launch,catch}. Icon: icon-spell-telekinetic-throw. Book cover: ui-book-cover-telekinetic-throw.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a held 10 kg object thrown at a creature, when it hits at ≥ 8 m/s, then collision damage and stagger apply per force rules.
- AC-3 integration Given a small-tagged creature within range, when grabbed and thrown into any fragile object, then the object breaks and the creature takes collision damage.
- AC-4 integration Given an incoming arrow or spell projectile, when catch is timed within the 0.4 s window, then the projectile stops and can be re-thrown with its on-hit ops intact.
- AC-5 unit Given catch pressed outside the window, when the projectile arrives, then the catch fails and the projectile hits normally.
- AC-6 content Given the Mage Hand → Telekinetic Throw chain, when checked by the anti-grind validator, then it passes.
Waits on
- mw-e07.8 Unlock follow-on spell verbs through mastery and advanced books open
- mw-e08.10 Add Mage Hand: grab, move and use distant objects done
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.12Add Blink: teleport several metres through open space
Description
Why
Constitution example; opens balconies, gaps and escapes — a classic sequence-breaker we deliberately allow.
What
As a sorcerer, I want to teleport up to 8 m to an aimed point with line of sight so that I can cross gaps, escape grapples and reach ledges.
Uses displacement op with LOS; breaks rooted/grappled statuses; leaves a brief light flash and a quiet noise at origin and destination.
Scope
- In: Blink spell
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Arcane. Placeholder cues: vfx-spell-blink-{depart,arrive}, sfx-spell-blink-{depart,arrive}. Icon: icon-spell-blink. Book cover: ui-book-cover-blink.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a ledge 7 m away and 3 m up with line of sight, when Blink is cast at it, then the caster stands on the ledge.
- AC-3 integration Given a closed window or bars between caster and target, when cast, then Blink fails with reason "no-line-of-sight" and mana is refunded.
- AC-4 integration Given the caster is rooted by any restraint status, when Blink succeeds, then the rooted status is removed.
- AC-5 integration Given a guard watching the origin point, when the caster blinks out of sight, then the guard's last-known position is the origin (it did not see the destination).
- AC-6 unit Given aim into a wall, when cast, then the caster lands at the nearest safe point before the wall.
Waits on
- mw-e06.12 Teleport and swap positions with collision-safe placement open
- mw-e06.14 Make spells emit light and noise that stealth and AI perceive done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.13Add Levitate: float the caster or an object upward slowly
Description
Why
Constitution: "the sorcerer might levitate onto a balcony" — vertical traversal as a class-specific solution.
What
As a sorcerer, I want to levitate myself (or a targeted object) upward at 1.5 m/s for up to 6 s so that I can reach balconies, cross pits slowly or lift a platform.
Channeled lift status; drains mana per tick; ends gently (slow-fall for 2 s) when mana runs out or cancelled.
Scope
- In: Levitate spell
- In: slow-fall ending
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Gravity. Placeholder cues: vfx-spell-levitate-loop, sfx-spell-levitate-loop. Icon: icon-spell-levitate. Book cover: ui-book-cover-levitate.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a balcony 6 m above, when Levitate is channeled for 4 s with lateral steering, then the caster reaches the balcony.
- AC-3 integration Given mana runs out mid-levitation, when it ends, then the caster slow-falls for 2 s and takes no fall damage from ≤ 8 m.
- AC-4 integration Given Levitate targeted at any object below the lift weight limit, when channeled, then it rises (e.g. lifting a platform or pressure-plate weight).
- AC-5 unit Given the caster is hit and poise breaks while levitating, when interrupted, then levitation ends and slow-fall applies.
Waits on
- mw-e06.4 Support cast times, channeling, cooldowns and interruption done
- mw-e06.8 Apply spell forces: impulse, knockback, pull and lift done
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.14Add Gravity Well: pull creatures and loose objects to a point
Description
Why
Constitution example; clumping enemies and debris sets up spectacular combinations.
What
As a sorcerer, I want to place a 5 s gravity point that drags creatures and loose objects inward so that I can group enemies, pull them off ledges or yank items from shelves.
Persistent volume with damped point pull scaled by weight; objects pulled into each other collide (fragile breaks); combos with Thunderclap/Flame Jet.
Scope
- In: Gravity Well spell
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Gravity. Placeholder cues: vfx-spell-gravity-well-loop, vfx-spell-gravity-well-collapse, sfx-spell-gravity-well-{loop,collapse}. Icon: icon-spell-gravity-well. Book cover: ui-book-cover-gravity-well.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given 4 creatures within 6 m, when the well persists 5 s, then all creatures under the heavy tag end within 1.5 m of the centre.
- AC-3 integration Given loose objects on a shelf within range, when the well activates, then they are pulled off and fly toward the centre, breaking if fragile on collision.
- AC-4 integration Given the well placed beyond a ledge, when creatures are pulled, then they fall.
- AC-5 unit Given the caster within the radius, when active, then the caster is also pulled (not exempt) — intentionally allowed hazard.
- AC-6 perf Given 30 loose objects and 8 creatures in one well, when simulated, then magic + physics cost p95 ≤ 3 ms per tick on the reference machine.
Waits on
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
- mw-e06.8 Apply spell forces: impulse, knockback, pull and lift done
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.15Add Mirror Self: an illusory double that draws attention
Description
Why
Constitution example; deception that plugs into the stealth/AI simulation instead of damage.
What
As a sorcerer, I want to project an illusory copy of myself at a point so that enemies investigate or attack it while I slip away.
Summons a decoy entity perceivable by sight and hearing (emits footsteps/noise), attracting perception priority; dispels when struck or on duration end; creatures tagged true-sight ignore it.
Scope
- In: Mirror Self spell
- In: decoy entity archetype
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Illusion. Placeholder cues: vfx-spell-mirror-self-{spawn,shimmer,dispel}, sfx-spell-mirror-self-{spawn,dispel}. Icon: icon-spell-mirror-self. Book cover: ui-book-cover-mirror-self.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a guard in Unaware state with the decoy in view, when perceived, then the guard targets the decoy as the player-equivalent stimulus (Suspicious → Investigating toward decoy).
- AC-3 integration Given the decoy is struck by any attack, when hit, then it dispels and the attacker enters Searching with last-known position at the decoy.
- AC-4 integration Given a creature tagged true-sight, when the decoy is in view, then it is ignored.
- AC-5 integration Given the decoy placed in darkness, when guards look, then its visibility uses the same light-field-based visibility model as the player.
- AC-6 unit Given maxActive 1, when cast again, then the previous decoy dispels.
Waits on
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.17Add Wisp Light: a floating orb that lights darkness and repels
Description
Why
Navigating darkness is a puzzle pillar; a light spell trades stealth for vision and scares light-averse creatures.
What
As a sorcerer, I want to summon a floating light orb that follows me or can be sent to a point so that I can explore dark places, light up puzzles and drive back light-averse creatures.
Light op attached to a small summoned entity (follow or fixed); 90 s duration; sends creatures tagged light-averse into fear via their own properties.
Scope
- In: Wisp Light spell
- In: wisp entity
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Light. Placeholder cues: vfx-spell-wisp-light-orb, sfx-spell-wisp-light-{spawn,hum-loop}. Icon: icon-spell-wisp-light. Book cover: ui-book-cover-wisp-light.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a dark room with light level 0.05, when the wisp is present, then the light field within 5 m of it is ≥ 0.5.
- AC-3 integration Given any light-sensitive mechanism (tagged lightActivated) within the wisp's radius above its threshold, when the wisp arrives, then it activates.
- AC-4 integration Given a creature tagged light-averse within 5 m, when the wisp approaches, then it retreats per its disposition rules.
- AC-5 integration Given the player carries a follow-wisp while sneaking, when a guard evaluates visibility, then the player's visibility score increases per the visibility model (stealth trade-off).
- AC-6 unit Given a Snuff/darkness effect overlapping the wisp, when resolved, then the wisp is extinguished early.
Waits on
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e06.14 Make spells emit light and noise that stealth and AI perceive done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.18Add Prism Ray: a light beam that reflects off reflective surfaces
Description
Why
Puzzle constitution: redirecting light. A reflecting beam turns mirrors and polished shields into puzzle pieces.
What
As a sorcerer, I want a channeled beam of focused light that bounces off reflective surfaces so that I can power light mechanisms around corners and dazzle creatures.
Beam with light op and small heat; reflects up to 3 times off reflective-tagged objects; dazzles (blinded 2 s) creatures that look into it.
Scope
- In: Prism Ray spell
- In: blinded status
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Light. Placeholder cues: vfx-spell-prism-ray-beam, vfx-spell-prism-ray-reflect, sfx-spell-prism-ray-loop. Icon: icon-spell-prism-ray. Book cover: ui-book-cover-prism-ray.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a reflective object angled at 45°, when the beam hits it, then the reflected beam continues at 90° and can activate a lightActivated mechanism on the reflected path.
- AC-3 integration Given any object tagged reflective including a Frostglass ice sheet, when hit, then the beam reflects (property-based, not object-based).
- AC-4 integration Given a creature facing the caster within the beam, when hit, then it gains blinded for 2 s and its sight perception is disabled for that time.
- AC-5 unit Given more than 3 reflective surfaces in a row, when channeled, then the beam stops after the third reflection.
Waits on
- mw-e06.14 Make spells emit light and noise that stealth and AI perceive done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.20Add Grasping Vines: roots burst up and restrain creatures
Description
Why
Constitution example; restraint as a verb — and vines are organic, so fire and frost interact naturally.
What
As a sorcerer, I want roots to erupt in a 3 m area and bind creatures for up to 6 s so that I can pin enemies, hold doors or stop a fleeing thief.
Spawns vine entities (tagged organic, flammable) that apply rooted to creatures inside; rooted breaks when its vine entity is destroyed or burns; only works on soil/stone ground, not water or wood floors above empty space.
Scope
- In: Grasping Vines spell
- In: vine entity archetype
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Nature. Placeholder cues: vfx-spell-grasping-vines-{erupt,hold,wither}, sfx-spell-grasping-vines-{erupt,creak}. Icon: icon-spell-grasping-vines. Book cover: ui-book-cover-grasping-vines.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given 3 creatures inside the area, when cast, then each is rooted for 6 s unless tagged too-large-to-root.
- AC-3 integration Given the vines are hit by any heat stimulus above ignition threshold, when they burn, then they are destroyed within 2 s and rooted ends early.
- AC-4 integration Given a strong creature with the break-free trait, when rooted, then it breaks free after 2 s.
- AC-5 unit Given cast onto a water surface, when resolved, then the cast fails with reason "invalid-surface" and mana is refunded.
- AC-6 integration Given a life stimulus source nearby (e.g. a second Vines cast), when overlapping, then vine duration refreshes (organic growth rule).
Waits on
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.21Add Animate Bones: raise nearby remains as a temporary servant
Description
Why
Constitution example; summoning grounded in the world — no bones nearby, no servant.
What
As a sorcerer, I want to assemble skeletal remains within 6 m into a temporary skeletal servant for 45 s so that the dungeon's dead fight for me.
Summon op requiring remains-tagged material; servant follows/attacks; crumbles back into remains (reusable) on expiry; the spell comes from a forbidden book.
Scope
- In: Animate Bones spell
- In: skeletal servant archetype (reuses bestiary skeleton data if available)
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Necromancy. Placeholder cues: vfx-spell-animate-bones-{assemble,crumble}, sfx-spell-animate-bones-{assemble,rattle,crumble}. Icon: icon-spell-animate-bones. Book cover: ui-book-cover-animate-bones.
Related: bestiary skeleton archetype from the creatures group; use a fixture archetype until it exists. Book is forbidden (e07-forbidden-books).
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a remains-tagged object within 6 m, when cast, then a servant with caster allegiance spawns at the remains location and the remains are consumed.
- AC-3 integration Given no remains in range, when cast, then the cast fizzles with a SpellFizzled event and full refund.
- AC-4 integration Given the servant expires, when 45 s pass, then a remains-tagged object is left at its position (reusable).
- AC-5 integration Given a hostile skeleton creature, when targeted with Animate Bones, then it is not converted (only inert remains qualify).
- AC-6 integration Given a lawful NPC witness, when the player casts Animate Bones in view, then a ContrabandWitnessed event fires (forbidden school).
Waits on
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e07.6 Give forbidden books social risk and whispering side effects open
area:contentclass:sorcererepic:e08milestone:m2mw-e08.22Add Polymorph: temporarily turn a creature into a harmless animal
Description
Why
Constitution example; transformation that solves fights, social problems and puzzles in unexpected ways.
What
As a sorcerer, I want to turn a creature into a small animal (e.g. a chicken-like critter) for 20 s so that I can neutralise threats, sneak past guards or make a heavy creature light enough to Mage Hand.
Transform op to a small-creature archetype; large/boss creatures tagged immune-transform resist; the critter form has its own properties (small, light, flammable) that other spells interact with.
Scope
- In: Polymorph spell
- In: critter archetype for the transformed form
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Alteration. Placeholder cues: vfx-spell-polymorph-{poof-in,poof-out}, sfx-spell-polymorph-{poof,critter-cluck}. Icon: icon-spell-polymorph. Book cover: ui-book-cover-polymorph.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a humanoid guard, when polymorphed, then it becomes the critter archetype for 20 s with its alert memory intact and restores exactly afterwards.
- AC-3 integration Given a polymorphed creature now tagged small and light, when grabbed by Mage Hand/Telekinetic Throw, then it can be lifted (properties of the new form apply).
- AC-4 integration Given a creature tagged immune-transform, when targeted, then it resists and emits SpellResisted.
- AC-5 integration Given the critter is killed and Polymorph uses onFormDeath=revert, when killed, then the original reverts with 1 HP and a stagger status.
- AC-6 unit Given Polymorph cast on the caster via a reflective surface bounce (if a beam reflection returns), when resolved, then the caster is transformed — intentionally allowed.
Waits on
- mw-e06.11 Temporarily transform creatures into other archetypes and back open
- mw-e06.6 Deliver spells as physical projectiles with collision and arcs done
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.24Build the spell combination test suite and greybox spell gallery
Description
Why
Emergent combinations are the point of magic; a suite proves they emerge from properties and catches regressions when E03 rules change.
What
A generated integration test matrix: for every pair of stimulus types emitted by catalogue spells (heat, cold, force, light, moisture, charge, life, decay) applied in both orders to a fixture set of property-tagged generic objects and creatures, assert deterministic, rule-consistent outcomes (golden snapshots). Plus curated scenario tests (Wellspring → Arc Lash chain, Wellspring → Frostglass bridge, Gravity Well → Thunderclap scatter, Gust → Fire spread, Polymorph → Telekinetic Throw) expressed via properties. Plus a greybox "spell gallery" room exhibiting every property for e2e/manual play.
Scope
- In: combination matrix generator, golden snapshots, curated scenario tests, greybox gallery scene, e2e smoke of the gallery
- Out: E03 rule changes (Core group) — the suite reports, it does not fix
Notes
Golden updates require an explicit reviewed flag in the PR. Suite runs headless in < 60 s.
Acceptance criteria
- AC-1 integration Given every ordered pair of catalogue stimulus types and the fixture object set, when run, then outcomes match golden snapshots and two runs produce identical hashes.
- AC-2 integration Given Wellspring then Arc Lash on 3 creatures in the puddle, when resolved, then all receive charge damage (conduction through wet).
- AC-3 integration Given Wellspring then Frostglass on a pit filled with water, when resolved, then a walkable ice surface exists across the pit.
- AC-4 unit Given a new spell added to the catalogue emitting an unseen stimulus type, when the suite runs, then it fails listing the missing pair coverage.
- AC-5 perf Given the full suite, when run in CI, then it completes in ≤ 60 s.
- AC-6 e2e Given the greybox spell gallery, when a scripted run casts every P1 spell at its gallery station, then no console errors occur and each station reports its expected property change.
- AC-7 manual Given a 30-minute playtest in the gallery, when testers experiment freely, then they report at least 3 combinations nobody told them about.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e08.11 Add Telekinetic Throw & Catch: hurl objects, creatures and arrows open
- mw-e08.14 Add Gravity Well: pull creatures and loose objects to a point open
- mw-e08.2 Add Ember: precise ignition of flammable things done
- mw-e08.22 Add Polymorph: temporarily turn a creature into a harmless animal open
- mw-e08.5 Add Frostglass: freeze targets and create temporary ice surfaces open
- mw-e08.6 Add Wellspring: conjure a burst of water that soaks an area open
- mw-e08.7 Add Gust: blast creatures back, clear gas and propel objects open
- mw-e08.8 Add Thunderclap: a violent shockwave that breaks and deafens open
- mw-e08.9 Add Arc Lash: lightning that chains through conductive and wet open
area:qaclass:sorcererepic:e08milestone:m2mw-e08.4Add Fire Wall: a persistent line of flame that blocks and burns
Description
Why
Third step of the fire chain: area denial as a new verb — split a fight, block a corridor, burn a bridge.
What
As a sorcerer, I want to draw a 6 m line of fire on the ground that lasts 8 s so that I can block paths and ignite what crosses it.
Persistent line volume with heat stimulus; creatures with fear-of-fire disposition refuse to cross; it emits strong light.
Scope
- In: Fire Wall spell
- In: chain link Flame Jet → Fire Wall
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Fire. Placeholder cues: vfx-spell-fire-wall-{spawn,loop,end}, sfx-spell-fire-wall-{spawn,loop}. Icon: icon-spell-fire-wall. Book cover: ui-book-cover-fire-wall.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a Fire Wall placed across a corridor, when a creature whose disposition includes fears-fire paths to the player, then it routes around or waits rather than crossing.
- AC-3 integration Given a wooden floor tagged flammable under the wall, when the wall persists 8 s, then the floor ignites and continues burning after the wall ends.
- AC-4 integration Given a moisture stimulus (rain, Wellspring) applied to the wall, when resolved, then wall segments inside the moisture area are extinguished.
- AC-5 unit Given a placement line crossing a wall, when cast, then the fire line is clipped at the geometry.
Waits on
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
- mw-e08.3 Add Firebolt and its Flame Jet follow-on verb open
area:contentclass:sorcererepic:e08milestone:m2mw-e08.16Add Arcane Sight: reveal hidden mechanisms and magical properties
Description
Why
Magic "can reveal secrets" — a sorcerer-flavoured alternative to the thief finding hidden passages.
What
As a sorcerer, I want a 15 s self buff that outlines objects tagged hidden, magical or trapped within 12 m so that I discover secret doors, traps and enchanted items.
Revealed status applied to matching entities; revealed traps become visible to the player only.
Scope
- In: Arcane Sight spell
- In: revealed status rendering hook (placeholder outline)
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Arcane. Placeholder cues: vfx-spell-arcane-sight-overlay, sfx-spell-arcane-sight-cast. Icon: icon-spell-arcane-sight. Book cover: ui-book-cover-arcane-sight.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given any object tagged hidden within 12 m, when Arcane Sight is active, then it gains revealed for the duration.
- AC-3 integration Given a trap tagged trapped behind a wall with no line of sight, when active, then it is revealed (sight through walls up to 12 m).
- AC-4 integration Given a hidden object at 13 m, when active, then it is not revealed until the player comes within 12 m.
- AC-5 unit Given Arcane Sight ends, when duration expires, then all revealed statuses it applied are removed.
Waits on
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
area:contentclass:sorcererepic:e08milestone:m2mw-e08.19Add Snuff: extinguish all light sources in a radius
Description
Why
Shadow magic that serves stealth: the sorcerer can create darkness like the thief extinguishing lights.
What
As a sorcerer, I want to snuff every flame and magical light within 6 m so that I can plunge a room into darkness and use it to hide or disorient.
AoE that removes lit state from light-source objects and ends light ops; smothers small fires (not large blazes); quiet cast.
Scope
- In: Snuff spell
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Shadow. Placeholder cues: vfx-spell-snuff-pulse, sfx-spell-snuff-hush. Icon: icon-spell-snuff. Book cover: ui-book-cover-snuff.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given 5 lit candles and 1 torch within 6 m, when Snuff is cast, then all become unlit and the light field drops accordingly.
- AC-3 integration Given a burning object larger than the small-fire threshold, when Snuff is cast, then it remains burning.
- AC-4 integration Given guards in the darkened room, when they evaluate visibility of the player, then the reduced light level lowers the player's visibility score.
- AC-5 unit Given Snuff noise loudness, when cast, then emitted loudness is ≤ 20 (quiet).
Waits on
- mw-e06.14 Make spells emit light and noise that stealth and AI perceive done
- mw-e06.7 Deliver spells as AoE volumes, cones and reflecting beams open
area:contentclass:sorcererepic:e08milestone:m2mw-e08.23Add Stasis: freeze one object or creature in time
Description
Why
Time magic creates novel puzzle and combat verbs: hold a falling gate, stop a swinging blade, pause an enemy.
What
As a sorcerer, I want to lock a single target in time for 5 s so that I can freeze moving traps, suspend falling objects or stop an attacker mid-swing.
Stasis status: zero velocity, immune to damage and forces, mechanisms paused; stored impulses from hits apply on release (capped).
Scope
- In: Stasis spell
- In: stasis status with stored impulse
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Time. Placeholder cues: vfx-spell-stasis-{lock,release}, sfx-spell-stasis-{lock,release}. Icon: icon-spell-stasis. Book cover: ui-book-cover-stasis.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given a moving mechanism (swinging blade, falling portcullis), when stasis is applied, then it halts for 5 s and resumes its prior motion afterwards.
- AC-3 integration Given a creature in stasis struck by forces totalling 300 N·s, when stasis ends, then the stored impulse (capped at 200 N·s) applies at once.
- AC-4 unit Given a creature in stasis, when damage is applied, then health is unchanged during stasis.
- AC-5 unit Given a creature tagged boss or immune-time, when targeted, then duration is reduced to 1 s.
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e06.9 Apply buffs, debuffs and statuses with stacking and dispel done
area:contentclass:sorcererepic:e08milestone:m2too-useful
E09Systemic StealthVisibility, lighting, sound, suspicion, searching, last-known-position and hiding work as coherent, deterministic systems. Stealth is not crouching + an eye icon: players read light exposure, noise and each enemy’s awareness, and failure escalates into new situations rather than a reload.6/23 done · 26%
M2 Class fantasies 15
mw-e09.6Author stealth tuning data: light weights, stance, noise, doors
Description
Why
Stealth feel must be tunable by designers without code, and defaults pinned by tests.
What
A
StealthTuningschema and default file: light-term curve, stance multipliers, motion bands, distance falloff, contrast bonus, gait base dB, surface table link, door-state and wall/floor transmission defaults, global audibility floor, hiding-place concealment defaults. Hot reload in dev; difficulty presets can override a whitelisted subset.Scope
- In: Schema, defaults, hot reload, pinned default tests
- In: Difficulty override whitelist (consumed by e09-stealth-assists)
- Out: AI thresholds and timers (e11-ai-tuning-data)
- Out: Sense ranges (e12-sense-profiles)
Notes
Curves are piecewise-linear point lists for easy editing.
Acceptance criteria
- AC-1 content Given the default file, when validated, then all curves are monotonic in the documented direction.
- AC-2 unit Given a difficulty preset overriding a non-whitelisted key, when loaded, then loading fails naming the key.
- AC-3 integration Given a dev build, when the stance multiplier is edited, then the visibility HUD reflects the change within 1 s.
- AC-4 unit Given the shipped defaults, when the pinned-default test runs, then documented values match.
Waits on
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e09.5 Emit footstep noise from speed, stance, surface and equipment weight done
area:contentclass:thiefepic:e09milestone:m2mw-e09.7Verticality rules: limited upward vision and height advantage
Description
Why
Verticality is a stealth input: rafters, balconies and rooftops should be real thief highways because guards rarely look up.
What
As a thief, I want to be harder to notice above or below a guard’s eye line so that rafters and ledges become routes. Vertical sight: per-profile vertical half-angles up/down (humanoids look up poorly by default, e.g. +20° up, −45° down) with an extra falloff; creatures with
looks-uptag (e.g. spiders, fliers) ignore it; Searching agents perform occasional look-up scans. Height difference affects noise via floor transmission (already in propagation) and adds a visibility penalty when the target is above a lit area but itself in dark rafters.Scope
- In: Vertical cone limits + falloff
- In: Look-up scan in Searching/Investigating when LKP is elevated
- In: Tuning data
- Out: Climbing movement (e10-climbing)
- Out: Flying creature navigation (post-MVP)
Notes
LKP elevation > 2 m above agent eye triggers look-up scan behaviour.
Acceptance criteria
- AC-1 unit Given a humanoid guard with vertical up half-angle 20°, when the target is 30° above horizontal at 8 m, then no sight stimulus is produced while the guard is Unaware.
- AC-2 unit Given a creature tagged
looks-up, when the same target is evaluated, then a sight stimulus is produced. - AC-3 unit Given a Searching guard whose LKP is 4 m above its eye height, when it reaches the LKP area, then it performs a look-up scan that widens its up half-angle to 60° for 3 s.
- AC-4 integration Given a scenario where the player crosses rafters above 2 patrolling guards in a lit hall, when simulated, then neither guard exceeds Suspicious.
- AC-5 unit Given the target directly overhead (90°), when evaluated, then no division-by-zero or NaN occurs and visibility follows the vertical rule.
Waits on
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e11.11 Search patterns around last-known position, including hiding spots open
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
area:simclass:thiefepic:e09milestone:m2mw-e09.8Hiding places: wardrobes, barrels, tall grass and shadowed alcoves
Description
Why
Hiding places are a constitution stealth input and give failed stealth a recovery move rather than a reload.
What
As a thief, I want to duck into a wardrobe, crouch in tall grass or press into an alcove so that I can break pursuit if I am quick and unseen. Two kinds: enter-able containers (wardrobe, barrel, under-bed — interact to enter/exit, player hidden, limited view) and concealment volumes (tall grass, curtains, alcoves — visibility multiplier while inside and stationary/crouched). Data: concealment factor, entry noise dB, entry time, capacity,
searchableflag. AI that saw you enter knows; AI searching may check (e11-search-behaviour). Being found inside = instant Combat at close range with a brief stagger window to flee (failure creates a story).Scope
- In: Hiding place component + data
- In: Enter/exit via interaction with timing and noise
- In: Concealment volumes feeding visibility model
- In: Being-discovered outcome
- Out: Hiding bodies in containers (e10-body-carry-hide)
- Out: Hiding prop art (placeholder boxes)
Notes
Entering while observed must be tracked as a memory
saw-enter:<hidingPlaceId>.Acceptance criteria
- AC-1 unit Given the player inside a wardrobe, when any observer computes visibility, then the result is 0 unless the wardrobe is opened by that observer.
- AC-2 unit Given the player crouched still in tall grass (concealment 0.3), when visibility is computed, then it is multiplied by 0.3; moving at run speed removes the bonus.
- AC-3 unit Given a guard observes the player entering a barrel, when the guard searches, then the barrel is checked first (via memory
saw-enter). - AC-4 unit Given an occupied single-capacity hiding place, when a second entity attempts to enter, then entry is refused with a prompt reason.
- AC-5 integration Given a guard opens the wardrobe the player is in, when discovered, then the guard enters Combat and the player gets a 0.6 s stagger window during which exiting is possible.
- AC-6 e2e Given the stealth grey-box course, when the player enters and exits a wardrobe via the interact action, then the camera switches to the hiding view and back without errors.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.23 Hide in wardrobes, barrels and haystacks and search containers done
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e11.11 Search patterns around last-known position, including hiding spots open
- mw-e11.8 Target memory and last-known position without omniscience done
area:gamearea:simclass:thiefepic:e09milestone:m2mw-e09.9Guards notice world anomalies: bodies, open doors, dark torches
Description
Why
A thief’s actions leave traces; guards noticing a doused torch or a missing colleague creates tension and new situations.
What
As a thief, I want guards to notice what I changed so that covering my tracks is part of the craft. Anomalies are world facts compared against an agent’s expected state (routine knowledge): unconscious/dead bodies, doors open that are normally closed, lights extinguished that are normally lit, missing valuables from a watched spot, blood/broken objects, a colleague missing from post for > N s. Each anomaly type has data: detection via sight (uses visibility of the anomaly), suspicion weight, resulting state (Suspicious for an open door, Alerted for a body).
Scope
- In: Anomaly registry + expected-state baseline per area/agent
- In: Perception integration (seen-anomaly stimulus)
- In: Data-driven anomaly types + weights
- Out: Relighting/closing on return (e11-return-to-routine)
- Out: Body carrying (e10-body-carry-hide)
Notes
Expected state captured at level load and updated when guards themselves change things. Hidden bodies (in containers or darkness below visibility threshold) are not perceived.
Acceptance criteria
- AC-1 unit Given a door normally closed is open, when a guard perceives it, then it becomes Suspicious and investigates the door.
- AC-2 unit Given a guard perceives an unconscious body, when evaluated, then it enters Alerted, investigates the body and may wake it (unconscious ally wakes after 3 s interaction).
- AC-3 unit Given a body placed in a container or in light ≤ 0.1 beyond 6 m, when a guard walks its route, then the body is not perceived.
- AC-4 unit Given a torch normally lit is extinguished, when a guard perceives the dark torch, then it becomes Suspicious (not Alerted) with cause
light-out. - AC-5 unit Given a guard itself opened a door on its route, when it later perceives that door open, then no anomaly is raised.
- AC-6 integration Given a scenario where the player knocks out guard A and leaves the body in a lit corridor on guard B’s route, when guard B passes, then guard B enters Alerted within 2 s of the body entering its view.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simclass:thiefepic:e09milestone:m2mw-e09.10Stealth HUD: light exposure, noise ring and stance readout
Description
Why
Stealth cannot be an eye icon: the player needs to read the inputs — how lit they are and how loud they are — to make decisions.
What
As a thief, I want to see how exposed and how loud I am so that I can make informed choices. Elements: light-exposure gem (continuous, 5 visual bands, driven by self-visibility), noise ring around the character footprint that pulses at each emitted noise scaled to its audible radius for a typical guard, stance/gait indicator, and optional diegetic cue (character material darkens in shadow). All toggleable and scalable via settings.
Scope
- In: HUD components in
src/ui, data binding from sim - In: Placeholder art (CSS/SVG shapes)
- In: Settings toggles
- Out: Per-enemy awareness (e09-awareness-indicators)
- Out: Final UI art (asset beads)
Notes
Noise ring radius = distance at which the emitted noise drops to the baseline humanoid hearing threshold in open space.
Acceptance criteria
- AC-1 unit Given self-visibility 0.05, when the HUD model is computed, then the gem shows band 1 of 5; at 0.95 band 5.
- AC-2 unit Given a 40 dB footstep and a 30 dB baseline threshold, when the noise ring is computed, then its radius is ≈ 3.2 m (10^(10/20)).
- AC-3 integration Given the settings toggle
hud.stealth=false, when the HUD renders, then no stealth elements are present. - AC-4 e2e Given the stealth grey-box course, when the player walks from a lit area into shadow, then the gem band decreases within 250 ms.
- AC-5 manual Given a playtest with 3 players, when asked to explain the gem and ring without instructions after 5 min, then at least 2 describe both correctly.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e09.5 Emit footstep noise from speed, stance, surface and equipment weight done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:gamearea:uiclass:thiefepic:e09milestone:m2- In: HUD components in
mw-e09.11Directional awareness indicators showing each enemy’s alert state
Description
Why
Players must see who is noticing them, from where, how close to detection, and why — without the omniscient radar that kills tension.
What
As a thief, I want directional indicators for enemies becoming aware of me so that I can react before being caught. For each agent with awareness > 0 of the player: an indicator near the agent (on-screen) or screen-edge chevron (off-screen) that fills with awareness and changes shape per alert state (6 distinct shapes, not colour alone), plus a cause glyph (eye = saw, ear = heard, hand = anomaly). Indicators only appear for agents the player could plausibly perceive (on-screen or heard), preserving uncertainty.
Scope
- In: Indicator model from awareness + state + dominant cause
- In: On-screen and off-screen rendering
- In: Colour-blind-safe shapes
- Out: Text barks (e09-alert-barks)
- Out: Last-known-position ghost (e09-lkp-ghost)
Notes
Indicators for agents the player has not perceived (behind walls, silent) are hidden unless an accessibility assist enables them (e09-stealth-assists).
Acceptance criteria
- AC-1 unit Given an agent at awareness 0.45 in Suspicious caused by hearing, when the indicator model is built, then fill = 0.45, shape =
suspicious, glyph =ear. - AC-2 unit Given an agent behind a wall that has not been seen or heard by the player, when the model is built with assists off, then no indicator is produced.
- AC-3 unit Given each of the six states, when shapes are resolved, then six distinct shape ids are returned.
- AC-4 e2e Given the stealth grey-box course, when a guard off-screen to the left becomes Suspicious from a noise, then a left-edge chevron appears within 1 frame of the state change.
- AC-5 unit Given 12 aware agents, when the model is built, then at most 6 indicators are shown, prioritised by awareness × proximity.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e11.6 Accumulate and decay awareness from stimuli per perceived target done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
area:gamearea:uiclass:thiefepic:e09milestone:m2mw-e09.13Text barks and audio stingers on alert state transitions
Description
Why
Players read enemy minds through barks ("Who’s there?", "Must be rats…"). With no voice in MVP, text barks plus audio stingers carry this.
What
As a thief, I want guards to mutter and call out as their state changes so that I understand what they think without a UI meter. Bark tables per behaviour profile map (transition, cause) → weighted lines, shown as floating subtitle text near the speaker within hearing range; audio stinger per escalation (placeholder tones) via the audio engine; cooldowns prevent spam. Barks also enable stealth-layer music hooks.
Scope
- In: Bark table schema + selection (seeded)
- In: Floating text renderer with distance fade
- In: Stinger cue ids + cooldowns
- Out: Voice acting (no voice in MVP)
- Out: Adaptive music system (Related: E28/E38 music layering)
- Out: Final bark writing (narrative pass, Related: e39-story-bible tone)
Notes
Stingers:
sfx-stealth-suspicious-01,sfx-stealth-alerted-01,sfx-stealth-combat-01,sfx-stealth-lost-01— placeholder beeps until asset beads land.Acceptance criteria
- AC-1 unit Given a Unaware→Suspicious transition with cause
heard, when a bark is selected with a fixed seed, then it comes from thesuspicious.heardtable and is deterministic. - AC-2 unit Given the same agent barked 2 s ago with cooldown 5 s, when a new transition occurs, then no bark is shown.
- AC-3 unit Given a speaker 40 m away beyond hearing range, when a bark would show, then it is suppressed for the player.
- AC-4 integration Given an escalation to Alerted, when it occurs, then exactly one stinger cue is played via the audio engine.
- AC-5 content Given a bark table referencing an unknown transition key, when validated, then validation fails.
Waits on
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
area:audioarea:gamearea:uiclass:thiefepic:e09milestone:m2mw-e09.14Failed stealth escalates into new situations, not reload screens
Description
Why
Failure creates stories. Being spotted should cause lockdowns, reinforcements, changed patrols or a chase — situations the player can still play through.
What
As a thief, I want getting caught to change the level rather than end my run so that I can improvise an escape. Area alarm levels (0 calm, 1 wary, 2 alarm, 3 lockdown) per zone driven by alarms, bodies found and combat. Data-defined responses per zone and level: lock specified doors, light extra lights, reinforce posts from a reserve pool (pre-placed, activated — not spawned from nowhere), release hounds, shift patrols; decay back after N minutes of calm to a stable “wary” level, never fully reset. Responses recorded in world state.
Scope
- In: Zone alarm model + response data + decay
- In: Integration with doors, lights, reserve creatures
- In: World state persistence
- Out: Crime/bounty system across settlements (Related: E22/E27 crime; not in this epic)
- Out: Specific dungeon zone authoring (E26)
Notes
Reserve pool creatures are placed in level data with
reserve: <zone>; activation paths them in from their barracks so the player can see them come.Acceptance criteria
- AC-1 unit Given zone Z at level 0, when an alarm device activates in Z, then Z becomes level 2 and all level-2 responses fire once.
- AC-2 unit Given a level-3 response
lock-doors: [d1, d2], when triggered, then d1 and d2 are locked via the door system and pathfinding reflects it. - AC-3 unit Given no alarm triggers for 5 sim minutes at level 2, when the decay rule runs, then Z drops to level 1 (never to 0 once an alarm has occurred).
- AC-4 unit Given a reserve creature already killed or knocked out by the player, when reinforcements are called, then it is not activated and no replacement is spawned.
- AC-5 integration Given a save while Z is at level 2, when loaded, then Z is at level 2 with its responses applied.
- AC-6 integration Given the stealth grey-box course, when the player is spotted and the alarm rung, then the course remains completable via at least one alternate route (scripted e2e path succeeds).
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e11.12 Allies share alerts by shouts, sight signals and alarm devices open
- mw-e12.4 Spawn data-defined creatures into the sim and grey-box scenes done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:contentarea:simclass:thiefepic:e09milestone:m2mw-e09.15Build the stealth grey-box course covering every stealth input
Description
Why
Stealth systems need one playable space that exercises light, sound, surfaces, doors, verticality and hiding for e2e tests and tuning playtests.
What
A grey-box level
greybox/stealth-coursewith labelled stations: light/dark gradient hall, surface strip (carpet/stone/gravel/creaky wood/water), door maze (open/closed/locked), vertical hall with rafters and ledges, hiding place gallery, distraction room, patrol yard with 3 fixture guards, alarm bell zone with reserve guards. Includes a baked navmesh, room/portal graph and patrol routes.Scope
- In: Level data, placeholder props, patrol routes, spawn points
- In: Console
tp station <n> - Out: Final dungeon content (E26)
- Out: Art
Notes
Used by e09 e2e ACs, e09-infiltration-scenarios and e10 thief challenge.
Acceptance criteria
- AC-1 e2e Given the course, when loaded in the browser, then all 8 stations are reachable and no console errors appear.
- AC-2 content Given the course data, when validated, then navmesh, portal graph and patrol routes pass validation.
- AC-3 integration Given
tp station 3, when entered, then the player is at the door maze start. - AC-4 perf Given the course with all guards active, when measured on the reference machine (High), then frame time p95 ≤ 16.7 ms.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
area:contentarea:qaclass:thiefepic:e09milestone:m2mw-e09.16Deterministic infiltration replay scenarios with asserted guard states
Description
Why
Releases must be trusted without reading code. Scripted infiltrations with asserted guard states prove the stealth systems work together and stay working.
What
A suite of ≥ 10 headless scenarios using the AI scenario harness on the stealth course layouts, each with a scripted player path and asserted guard states over time. Scenarios: shadow sneak (never above Suspicious), sprint on gravel (Suspicious ≤ 1 s), closed-door muffling, thrown distraction lure, break LOS and hide (guard ends Searching then postAlert Unaware), body discovery → Alerted, alarm → zone level 2, rafters crossing, hiding-place seen-enter, ally communication chain. Each records a state-hash sequence as a regression baseline.
Scope
- In: Scenario files, baselines, CI job
- In: Readable failure timelines
- Out: Browser e2e (course e2e ACs)
Notes
Scenarios reference fixture creatures only so bestiary tuning never breaks them.
Acceptance criteria
- AC-1 integration Given the shadow-sneak scenario, when run, then no guard exceeds Suspicious for the full 60 s.
- AC-2 integration Given the break-LOS scenario, when run, then the guard sequence is Combat → Searching → Unaware(postAlert) with the Searching phase starting within 5 s of LOS break.
- AC-3 integration Given the closed-door scenario, when the same noise is made with the door open vs closed, then the guard enters Suspicious only in the open case.
- AC-4 integration Given every scenario, when run twice, then state-hash sequences are identical to each other and to the committed baseline.
- AC-5 integration Given a deliberately broken rule (test-only flag doubling light weight), when the suite runs, then at least 3 scenarios fail (the suite actually detects regressions).
- AC-6 perf Given the full suite, when run in CI, then it completes in ≤ 30 s.
Waits on
- mw-e09.14 Failed stealth escalates into new situations, not reload screens open
- mw-e09.7 Verticality rules: limited upward vision and height advantage open
- mw-e09.8 Hiding places: wardrobes, barrels, tall grass and shadowed alcoves open
- mw-e09.9 Guards notice world anomalies: bodies, open doors, dark torches open
- mw-e10.2 Throw objects to create noise distractions guards investigate open
- mw-e11.3 Scenario harness for scripted AI tests with asserted states over time done
area:qaarea:simclass:thiefepic:e09milestone:m2mw-e09.12Show a last-known-position ghost where enemies think you are
Description
Why
Showing where guards believe you are turns last-known position into a tool — the player can see the gap between belief and reality and exploit it.
What
As a thief, I want to see a faint silhouette where the enemies last saw me so that I can plan to double back or set up an ambush. When at least one hunting agent (Searching/Alerted) holds a player LKP and the player is out of its perception, render a translucent silhouette at the LKP in the stance last observed. Disappears when all agents lose the memory or re-acquire. Toggleable.
Scope
- In: Ghost model from aggregated agent memories
- In: Placeholder translucent capsule silhouette
- In: Settings toggle
- Out: Accurate player mesh silhouette (asset integration later)
Notes
Aggregate: show the most recent LKP among hunting agents; if two diverge by > 10 m, show both.
Acceptance criteria
- AC-1 unit Given one Searching agent with an LKP and no perception of the player, when the ghost model is built, then one ghost at that LKP is returned.
- AC-2 unit Given the agent re-acquires the player, when the model is built, then no ghost is returned.
- AC-3 unit Given two hunting agents with LKPs 15 m apart, when built, then two ghosts are returned; if 5 m apart, one.
- AC-4 e2e Given the stealth grey-box course, when the player breaks LOS from an Alerted guard, then a ghost appears at the break point within 250 ms.
Waits on
- mw-e11.8 Target memory and last-known position without omniscience done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:gamearea:renderarea:uiclass:thiefepic:e09milestone:m2mw-e09.19Derive collider sight occlusion from world properties (opaque/transparent + partial factor)
Description
Why
mw-e09.1 gives line of sight its own per-collider Occlusion (opaque | transparent | partial:<factor>) supplied through LineOfSight's occlusionOf callback; unregistered colliders are opaque. The world-property vocabulary (v2, mw-e03.31) has opaque/transparent flags but no partial-cover factor, and colliders do not yet map to entities.
What
Map physics colliders to their entities and derive occlusionOf from world properties: opaque -> opaque, transparent -> transparent, neither -> partial with a factor (add a sightTransmittance-style property or material default). Level geometry without properties stays opaque.
Notes
Adding a property is a schema change: bump WORLD_PROPERTIES_VERSION.
area:simclass:thiefepic:e09milestone:m2mw-e09.20Supply equipment brightness, cloak and disguise to the visibility profile
Description
Why
The visibility model (mw-e09.2) has a profile term (equipment brightness × cloak × disguise), and selfVisibilityOf reads a stealth.visibilityProfile component, but nothing writes it: items have no brightness and there is no cloak or disguise flag, so every character reads neutral.
What
Add an optional item stealth field (brightness 0–2, conceals: cloak) and have equipment derive each actor's VisibilityProfileComponent (product of worn brightness, cloaked when a concealing item is worn); disguise from the disguise system when it exists.
Scope
• In: Item schema field, equipment-derived profile, register the component in the game world
• Out: Disguise gameplay (its own bead)
Acceptance criteria
• AC-1 unit Given polished plate (brightness 1.3) equipped, when the profile is derived, then brightness is 1.3 and self-visibility rises by that factor.
• AC-2 unit Given a cloak equipped, when derived, then cloaked is true and the tuning's cloak modifier applies.
• AC-3 content Given an item brightness outside 0–2, when validated, then loading fails.
area:simclass:thiefepic:e09milestone:m2mw-e09.21Reconcile controller stance×gait visibility with the stealth tuning
Description
Why
Two tables now describe how visible movement is: the controller's stealth.profiles[stance][gait].visibility (mw-e02.10, published on MovementProfile) and the stealth tuning's stance × motion bands (mw-e09.2), which the visibility model uses. The shipped values agree today (stance × motion reproduces the controller table) but can drift.
What
Pick one source: drop visibility from the controller profiles (MovementProfile.visibility derived from the stealth tuning) or document why both exist. Move the AI stand-in senses onto the visibility model.
Scope
• In: Remove or derive the duplicate, update the stand-in senses, docs
• Out: New tuning values (mw-e09.6)
Acceptance criteria
• AC-1 unit Given the shipped content, when a movement profile and the visibility model are computed for every stance × gait, then they read from one table.
• AC-2 content Given the controller file, when validated, then no second visibility table exists (or a check pins them equal).
area:contentarea:simclass:thiefepic:e09milestone:m2mw-e09.23Creature footsteps read the ground collider's material, not just tagged regions
Description
Why
Creature footstep noise (mw-e09.5) has no collider to read, so it uses the scene's tagged surface regions and otherwise stone. A skeleton walking on untagged gravel steps as stone (0 dB instead of +8), so it is quieter than the player on the same ground.
What
Give creatures the ground probe the player has: a downward query (or the nav mesh's surface material) that yields the ground material, wet and frozen state for a creature's feet, fed to installFootstepNoise's creatureSurface in startSceneNoise (src/game/creatures/ai.ts).
Acceptance criteria
- AC-1 integration Given a creature walking on an untagged gravel collider, when it steps, then its footstep noise carries the gravel offset (+8 dB).
- AC-2 unit Given a creature on a tagged carpet region over gravel, when it steps, then the region wins, as for the player.
- AC-3 [bench] Per-tick cost with 100 walking creatures stays within the footstep-noise bench budget.
area:simclass:thiefepic:e09milestone:m2
M3 MVP content 1
mw-e09.17Stealth accessibility assists: slower detection, stronger cues
Description
Why
Stealth must be approachable for players with different reaction speeds, hearing or vision without removing its systemic nature.
What
As a player, I want stealth assist options so that I can enjoy infiltration at my own pace. Options: detection speed (50–150%), show indicators for unperceived agents, visual captions for noise events (“Footsteps — left”), stronger visibility gem contrast, extended hiding stagger window. Applied via the whitelisted stealth/AI tuning overrides.
Scope
- In: Settings entries, override application, captions for noise events
- Out: Global accessibility framework (E31)
Notes
Assists alter tuning inputs only; rules stay identical so scenario tests still hold at 100%.
Acceptance criteria
- AC-1 unit Given detection speed 50%, when a constant stimulus accumulates, then time to Suspicious doubles versus 100%.
- AC-2 integration Given noise captions on, when a 40 dB noise is perceived by the player from the left, then a caption with direction
leftappears within 100 ms. - AC-3 unit Given an assist value outside the allowed range in settings storage, when loaded, then it clamps to the nearest bound.
- AC-4 integration Given all assists off, when the infiltration suite runs, then baselines are unchanged.
Waits on
- mw-e09.11 Directional awareness indicators showing each enemy’s alert state open
- mw-e09.6 Author stealth tuning data: light weights, stance, noise, doors open
- mw-e31.1 Create typed settings store and options menu skeleton done
- mw-e31.10 Provide difficulty presets and granular assists read by the sim done
area:simarea:uiclass:thiefepic:e09milestone:m3
M4 Fun validation 1
mw-e09.18Playtest and tune stealth feel on the grey-box course
Description
Why
A perfectly executed infiltration should feel as satisfying as a hard fight; only playtesting confirms that.
What
Run 5 playtest sessions on the stealth course and first dungeon grey-box, record telemetry (detections, causes, time in each state, restarts), adjust tuning data, document changes in
docs/design/stealth-tuning-log.md.Scope
- In: Sessions, telemetry capture, tuning edits, log
- Out: New mechanics (file new beads)
Notes
Tuning changes that alter scenario baselines must update them in the same PR with rationale.
Acceptance criteria
- AC-1 manual Given 5 sessions, when players complete the course, then ≥ 4 of 5 rate stealth ≥ 4/5 for “I understood why I was seen”.
- AC-2 integration Given telemetry events for alert transitions, when exported, then each includes cause, state, and time-in-state fields.
- AC-3 manual Given the tuning log, when reviewed, then each change lists the metric that motivated it.
Waits on
- mw-e09.10 Stealth HUD: light exposure, noise ring and stance readout open
- mw-e09.11 Directional awareness indicators showing each enemy’s alert state open
- mw-e09.16 Deterministic infiltration replay scenarios with asserted guard states open
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:designarea:qaclass:thiefepic:e09milestone:m4
E10Thief ToolkitLockpicking, pickpocketing, thrown distractions, extinguishing lights, climbing, hiding bodies, disguises, silent movement, takedowns, traps and escape tools produce a complete thief fantasy built on shared world systems (properties, light, sound, AI perception).1/17 done · 6%
M2 Class fantasies 15
mw-e10.1Define thief tool item data: lockpicks, throwables, smoke, traps
Description
Why
Thief tools are data-driven items so shops, loot and progression can hand them out without code.
What
Item definitions (in the inventory schema) for: lockpick set (durability), coin/pebble/bottle throwables (impact noise, breakable), smoke bomb, flash powder, noisemaker trap, tripwire kit, caltrops, rope, water flask (extinguish), sap/blackjack (non-lethal), disguise items referencing faction. Each carries world properties (weight, fragile, noise) and tags consumed by E10 features.
Scope
- In: Item data files + tests
- In: Tags:
tool:lockpick,throwable,trap:*,disguise:<faction> - Out: Loot/shop placement (E18/E20)
- Out: Icons (asset beads)
Notes
Placeholder icon ids:
icon-tool-lockpick,icon-throw-coin,icon-throw-bottle,icon-smoke-bomb,icon-trap-noisemaker,icon-trap-tripwire,icon-caltrops,icon-water-flask,icon-sap.Acceptance criteria
- AC-1 content Given each thief item file, when validated, then it passes the inventory item schema and carries its required tags.
- AC-2 content Given a throwable without an impact noise value, when validated, then validation fails.
- AC-3 unit Given a lockpick set with durability 10, when resolved, then durability is 10 and it stacks to max 5 sets.
- AC-4 content Given every thief item, when the content harness runs, then each has at least one automated test (spawn into inventory + use).
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
area:contentclass:thiefepic:e10milestone:m2mw-e10.2Throw objects to create noise distractions guards investigate
Description
Why
Thrown distractions are the thief’s first verb in the progression ladder (Sneak → distractions → …) and a classic stealth tool.
What
As a thief, I want to throw a coin or bottle so that guards look away or walk over to investigate. Any
throwableitem or physics object can be aimed (trajectory preview arc) and thrown; on impact it emits a noise event = item impact dB + surface offset;fragileitems shatter (louder, leave shards asglass-shardssurface). Guards perceive the propagated noise and investigate the perceived position. Thrown object remains in the world (anomaly if found near a post repeatedly — hook).Scope
- In: Throw aim + arc preview + release
- In: Impact noise via sound propagation + surface
- In: Pickup-able thrown objects (non-fragile)
- Out: Combat throwing damage (E04)
- Out: Arrows (E05)
Notes
Uses physics objects for the throw; the noise rule is property-based (
throwable,fragile), never item-specific.Acceptance criteria
- AC-1 unit Given a coin (impact 45 dB) landing on stone 10 m from a guard with threshold 30 dB, when propagated, then the guard receives a heard-noise stimulus and enters Suspicious.
- AC-2 unit Given a bottle tagged
fragilelanding, when it hits any surface, then it shatters, emits the fragile-break loudness and adds aglass-shardssurface patch. - AC-3 unit Given a coin landing on carpet, when propagated to the same guard, then perceived loudness is reduced by the carpet offset and the guard stays Unaware.
- AC-4 integration Given the distraction-room scenario, when the player throws a bottle behind a guard, then the guard turns and investigates the impact location within 2 s.
- AC-5 unit Given a throw into water, when it lands, then a
splashnoise kind is emitted and the object sinks (not retrievable unless shallow). - AC-6 e2e Given the stealth grey-box course, when the player aims, then a trajectory arc renders and the landing marker matches the actual landing point within 0.5 m.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.25 Emit noise events from world interactions with material loudness done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e11.10 Guards investigate suspicious stimuli at their perceived location open
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.3Extinguish and relight light sources to reshape the shadows
Description
Why
Extinguishing lights lets thieves create their own darkness, and any system that affects light (water, wind, frost magic) should work too.
What
As a thief, I want to snuff candles and douse torches so that I can create shadows where I need them. Light sources with property
extinguishablego out from: interact (pinch/snuff, quiet), anywet/water effect via element propagation (thrown water flask, rain), strong wind/gust effects, frost.ignitablelight sources relight from any fire source. Light-field updates immediately; extinguishing emits a small noise and creates an anomaly (e09-anomaly-detection).Scope
- In: Light source state + property-based rules
- In: Interact snuff action
- In: Light-field update + anomaly record
- Out: Light-field implementation (e03-light-field)
- Out: Water arrows (E05) — works automatically via property
Notes
Magical lights may be tagged non-extinguishable; that is data.
Acceptance criteria
- AC-1 unit Given any
extinguishablelight within a water/wet effect volume, when the effect applies, then the light turns off and the light-field level at 2 m drops to the ambient value within 1 tick. - AC-2 unit Given an
ignitableunlit torch in contact with any fire source, when 0.5 s passes, then it relights. - AC-3 unit Given a light tagged non-extinguishable, when water hits it, then it stays lit.
- AC-4 integration Given the player snuffs a candle via interact, when complete, then a ≤ 15 dB noise is emitted and an anomaly
light-outis registered for that light. - AC-5 integration Given a guard walking past a torch the player extinguished, when perceived, then the guard enters Suspicious with cause
light-out.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.16 Extinguish and relight torches, candles and braziers systemically done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e09.9 Guards notice world anomalies: bodies, open doors, dark torches open
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.4Backstabs and non-lethal takedowns on unaware targets
Description
Why
Opportunistic combat is the thief’s combat fantasy: ambushes and takedowns reward patience and positioning.
What
As a thief, I want to backstab or knock out an unaware enemy from behind so that a clean infiltration can thin the guard without a fight. Conditions: target alert state ∈ {Unaware, Suspicious}, player within rear arc (±60°) and 1.5 m, target does not perceive the player this think. Lethal backstab: damage multiplier from damage model (lethal on humanoids by default). Non-lethal (sap/choke): target becomes
unconsciousfor a data-defined duration (default 10 min sim) unless woken. Creature schema flags (immune-to-knockoutfor undead/constructs,no-backstabfor huge creatures) gate the verbs. Takedown emits noise (choke 20 dB, backstab 35 dB). Failed attempts (target turns) cause a scuffle: target enters Combat staggered.Scope
- In: Prompt availability, execution, outcomes, noise
- In: Creature flags gating
- In: Failure scuffle outcome
- Out: Environmental takedowns (e10-environmental-takedowns)
- Out: Animations (placeholder snaps)
Notes
Unconscious bodies are carriable (e10-body-carry-hide) and anomalies (e09-anomaly-detection).
Acceptance criteria
- AC-1 unit Given an Unaware humanoid and the player 1 m behind within 60°, when takedown is evaluated, then both backstab and knockout verbs are available.
- AC-2 unit Given the same target in Investigating or higher, when evaluated, then no takedown verb is available.
- AC-3 unit Given a creature flagged
immune-to-knockout, when evaluated, then only backstab is available. - AC-4 unit Given a knockout, when executed, then the target is
unconsciousfor 600 s sim and a 20 dB noise is emitted at its position. - AC-5 unit Given the target perceives the player during the 0.5 s wind-up, when the takedown resolves, then it fails, the target enters Combat and is staggered for 0.8 s.
- AC-6 integration Given the patrol-yard scenario, when the player knocks out a guard out of sight of others, then the other guards remain Unaware.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.5Carry and hide unconscious or dead bodies
Description
Why
Hiding bodies is how thieves cover their tracks; a body left in the light should cause the alarm the player earned.
What
As a thief, I want to pick up and hide bodies so that patrols do not discover what I did. Pick up unconscious/dead humanoid-size creatures (size class limit), carry at reduced speed (−40%) and no sneak-sprint, higher visibility (+profile), drop or place into hiding places with
body-capacity(chests, wardrobes, deep shadow, water). Placed bodies inside containers are invisible to anomaly detection; bodies in darkness depend on visibility. Unconscious creatures carried keep their wake timer.Scope
- In: Carry state, movement modifiers, drop/place
- In: Body containers
- In: Integration with anomaly detection
- Out: Ragdoll physics beyond placeholder (Related: physics/animation)
- Out: Looting bodies (e17 / E18)
Notes
Dropping a body emits a thud noise scaled by surface (e.g. 35 dB on stone).
Acceptance criteria
- AC-1 unit Given an unconscious guard, when the player picks it up, then player speed is ×0.5 (ADR-0003, matches mw-e02.17) and the visibility profile term increases by the configured amount.
- AC-2 unit Given the player carrying a body, when placed into a container with body capacity 1, then the body is hidden and the container is full.
- AC-3 unit Given a body placed in a container, when a guard perceives the container, then no body anomaly is raised unless the guard opens it.
- AC-4 unit Given a creature larger than the carry size limit, when pickup is attempted, then it is refused with reason
too-heavy. - AC-5 unit Given the unconscious carried guard’s wake timer expires, when it wakes while carried, then it is dropped, enters Alerted and the player is staggered 0.5 s.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e09.8 Hiding places: wardrobes, barrels, tall grass and shadowed alcoves open
- mw-e09.9 Guards notice world anomalies: bodies, open doors, dark torches open
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.6Spike: systemic lockpicking design that stays in the world
Description
Why
The constitution prefers world systems over disconnected minigames. Lockpicking must keep the player in the world, exposed to guards and noise, while still involving skill.
What
Time-box 1 day. Question: how can lockpicking be an in-world, interruptible action with a light skill layer (e.g. timing tension clicks while the camera stays third-person) instead of a full-screen minigame? Prototype 2 variants in the grey-box (A: timed hold with pin-click timing windows; B: pure duration by lock quality vs tool/skill with noise risk), evaluate readability, tension with nearby guards, accessibility (a no-skill option), and progression hooks. ADR with recommendation.
Scope
- In: Two prototypes in
spikes/lockpicking/ - In: ADR with lock data shape (quality, pins, noise per failure, jam chance)
- Out: Production lockpicking (e10-lockpicking)
Notes
Must remain compatible with sorcerer/knight alternatives (Unlock spell, bashing) via the same lock component from e03-doors-locks-switches.
Acceptance criteria
- AC-1 manual Given the ADR, when reviewed, then it records the choice, rejected alternatives, and a data shape for lock quality.
- AC-2 manual Given 3 internal testers, when trying both prototypes near a patrolling guard, then preferences and time-to-open are recorded in the ADR.
- AC-3 manual Given the ADR, when read, then it specifies an accessibility mode that removes timing requirements.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
area:designarea:simclass:thiefepic:e10milestone:m2- In: Two prototypes in
mw-e10.7Pick locks as an in-world, interruptible, noisy action
Description
Why
Locked doors and chests are the thief’s signature alternate routes; picking them in the world while guards patrol creates tension.
What
As a thief, I want to pick locks while staying aware of my surroundings so that opening a door is a tense, skilful choice rather than a menu. Implements the ADR: locks carry quality (1–5), pin count, jam chance; picking is an in-world action (player stays visible and hearable, camera third-person) consuming lockpick durability; skill layer per ADR with an accessibility no-timing mode. Failures emit small noises and may break a pick; jamming a lock makes it unpickable (bash/magic still work). Progression hooks raise max quality.
Scope
- In: Lock component extension, pick action, noise, durability, jam
- In: In-world UI overlay (placeholder)
- In: Accessibility mode
- Out: Door/lock base mechanics (e03-doors-locks-switches)
- Out: Unlock spells/bashing (E06/E04, same component)
Notes
Default noise per failed pin 18 dB; broken pick 25 dB.
Acceptance criteria
- AC-1 unit Given a quality-2 lock and one lockpick set, when the pick action completes successfully, then the lock is unlocked and durability decreases by 1.
- AC-2 unit Given a failed pin attempt, when resolved, then an 18 dB noise event is emitted at the lock.
- AC-3 unit Given a lock jams (seeded chance), when evaluated, then further pick attempts are refused with reason
jammedwhile other unlock methods still work. - AC-4 unit Given the player is attacked or moves > 0.5 m while picking, when resolved, then the action is interrupted and progress is lost.
- AC-5 unit Given lock quality above the player’s unlocked max, when the prompt is evaluated, then picking is not offered and the prompt says why.
- AC-6 integration Given accessibility no-timing mode, when picking a quality-3 lock, then it completes after the documented duration without input timing.
- AC-7 e2e Given the stealth grey-box door maze, when the player picks a locked door, then the door opens and the navmesh link becomes traversable.
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e10.6 Spike: systemic lockpicking design that stays in the world open
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e19.10 Grant thief verbs from tools and tricks learned from contacts open
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:gamearea:simarea:uiclass:thiefepic:e10milestone:m2mw-e10.8Pickpocket and plant items on creatures from behind
Description
Why
Pickpocketing creates emergent solutions: steal a key instead of picking a lock, plant evidence, lift a guard’s disguise pass.
What
As a thief, I want to lift items from, or plant items on, unaware creatures so that I can solve problems without violence. Available when target is Unaware/Suspicious, not perceiving the player, within 1.2 m behind/side arc. Opens a compact in-world list of visible pockets (creature inventory items flagged
pocketable). Each attempt takes time by item weight; success chance falls with target awareness and item weight; a failed attempt raises target awareness sharply (Alerted if it turns and sees you). Theft is recorded (disposition/faction events for witnesses).Scope
- In: Pickpocket and plant actions, success model, witness events
- In: Creature inventory integration
- In: Compact UI (placeholder)
- Out: Creature loot tables (e18-loot-tables)
- Out: Crime/bounty (Related: crime system)
Notes
Success probability formula documented in tuning data; seeded RNG.
Acceptance criteria
- AC-1 unit Given an Unaware guard with a
pocketablekey, when the player attempts from behind at 1 m, then the item transfers on success and the attempt takes the weight-scaled duration. - AC-2 unit Given a failed attempt, when resolved, then the target’s awareness increases by the configured spike and it turns toward the player.
- AC-3 unit Given a witness guard perceives the theft, when resolved, then a
witnessed-theftevent fires applying disposition and faction modifiers. - AC-4 unit Given a target with no pocketable items, when the prompt is evaluated, then the verb is offered but the list shows “Nothing to take” (still allows planting).
- AC-5 unit Given the target walks out of range mid-attempt, when resolved, then the attempt cancels without a transfer or awareness spike.
- AC-6 integration Given the player plants an item on a guard, when saved and loaded, then the guard’s inventory contains the planted item.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.8 Faction table and relationship queries between creatures done
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e18.7 Drop what creatures actually carry, plus data-driven extras open
- mw-e19.10 Grant thief verbs from tools and tricks learned from contacts open
area:gamearea:simarea:uiclass:thiefepic:e10milestone:m2mw-e10.12Detect and disarm world traps; failures trigger them
Description
Why
Triggered traps should create new situations rather than reloads; spotting and disarming them is thief expertise.
What
As a thief, I want to notice and disarm pressure plates, tripwires and dart traps so that I can open safe routes — and accept the chaos when I fail. Traps have a
concealmentvalue; the player perceives them via a proximity/light-based check (thieves have a bonus; sneaking slower improves it) which reveals a subtle highlight. Disarm is an in-world timed action (like lockpicking) with a failure chance by trap quality; failure triggers the trap. Disarmed traps can be picked up (become placeable items where data allows).Scope
- In: Trap detection check, reveal highlight, disarm action, fail-trigger
- In: Salvage to item
- Out: Trap mechanics/effects themselves (e03-triggers / e03-doors-locks-switches)
- Out: Dungeon trap placement (E26)
Notes
Detection uses light-field at trap position: dark traps are harder to spot.
Acceptance criteria
- AC-1 unit Given a trap with concealment 0.5 in light 0.8, when a thief sneaks within 3 m, then it is detected within 1 s.
- AC-2 unit Given the same trap in light 0.1, when a knight walks within 3 m, then it is not detected.
- AC-3 unit Given a disarm attempt that fails (seeded), when resolved, then the trap triggers its normal effect.
- AC-4 unit Given a successfully disarmed pressure plate, when a creature steps on it, then nothing triggers.
- AC-5 integration Given a disarmed tripwire, when salvaged, then a tripwire kit item is added to inventory.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e19.10 Grant thief verbs from tools and tricks learned from contacts open
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.13Smoke bombs and flash powder break line of sight for escapes
Description
Why
Escape tools turn failed stealth into a chase instead of a death — the thief’s disengagement fantasy.
What
As a thief, I want to throw a smoke bomb so that I can break line of sight and slip away. Smoke bomb creates a gas volume (via element propagation, drifts and dissipates over 12 s) tagged
occlusion: partial:0.1for LOS; creatures inside have sight disabled and cough (noise). Flash powder: instant stun for creatures facing it within 5 m (sight-dependent — blind/life-sense creatures unaffected). Losing LOS feeds LKP rules, so guards search where you vanished.Scope
- In: Smoke gas volume + LOS occlusion + dissipation
- In: Flash stun rule by sense profile
- In: Integration with memory/LKP
- Out: Gas propagation engine (e03-element-propagation)
- Out: VFX art (placeholders)
Notes
Smoke is a gas: a Gust effect should clear it via the same gas rules (property-based).
Acceptance criteria
- AC-1 unit Given a smoke volume between guard and player, when LOS is queried, then visible fraction ≤ 0.1.
- AC-2 unit Given a smoke volume, when 12 s pass without wind, then it has dissipated and LOS is restored.
- AC-3 unit Given flash powder within 5 m and a guard facing it, when detonated, then the guard is stunned 3 s; a creature without sight senses is not stunned.
- AC-4 integration Given a Combat guard chasing the player, when the player throws smoke and runs 10 m sideways, then the guard enters Searching with LKP at the smoke cloud, not the player’s position.
- AC-5 unit Given any gas-clearing wind effect through the smoke volume, when applied, then the volume moves/dissipates per gas rules.
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e09.1 Multi-point line-of-sight queries with partial cover and see-through done
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e11.8 Target memory and last-known position without omniscience done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.17Grey-box thief infiltration challenge with multiple scripted routes
Description
Why
Milestone m2 requires the thief fantasy proven in grey-box; a perfect infiltration must feel as good as a hard fight.
What
A grey-box vault infiltration
greybox/thief-vault: guarded courtyard, lit halls, a locked vault with a key carried by a patrolling guard, climbable exterior wall, alarm bell, hiding places. At least 3 distinct solution routes (pickpocket key; climb + pick window lock; distraction + extinguish + takedown), each covered by a scripted e2e/scenario test. Records a “ghost” (never detected) flag on completion.Scope
- In: Level data, routes, scripted solution tests, completion flag
- Out: Final dungeon (E26)
- Out: Art
Notes
Fixture creatures only.
Acceptance criteria
- AC-1 integration Given route A (pickpocket), when the scripted scenario runs, then the vault opens and no guard exceeds Suspicious.
- AC-2 integration Given route B (climb + lockpick window), when run, then the vault is reached and the completion flag is set.
- AC-3 integration Given route C (distract + extinguish + takedown), when run, then the vault opens with one unconscious guard hidden and no alarm.
- AC-4 integration Given a scripted failure (player spotted in the courtyard), when run, then the alarm escalates the zone and the scripted escape via smoke + hiding place still completes the level.
- AC-5 manual Given 3 playtesters, when completing the vault undetected, then ≥ 2 rate satisfaction ≥ 4/5.
Waits on
- mw-e09.14 Failed stealth escalates into new situations, not reload screens open
- mw-e09.15 Build the stealth grey-box course covering every stealth input open
- mw-e10.13 Smoke bombs and flash powder break line of sight for escapes open
- mw-e10.3 Extinguish and relight light sources to reshape the shadows open
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
area:contentarea:qaclass:thiefepic:e10milestone:m2mw-e10.10Disguises that fool factions until behaviour gives you away
Description
Why
Disguises are a later thief verb in the progression ladder and create social infiltration moments.
What
As a thief, I want to wear a faction’s uniform so that I can walk among guards if I behave. Equipping a
disguise:<faction>item makes that faction’s members treat the player asdisguisedinstead of intruder: sight stimuli accumulate a separate suspicion meter driven by proximity (< 3 m), suspicious behaviour (running, crouching, climbing, being in restricted zones, being seen committing crimes, carrying bodies), and observer traits (officers and dogs see through faster;sees-through-disguiseflag). Suspicion full → disguise blown for that zone (remembered).Scope
- In: Disguise state, per-observer suspicion rules, restricted zone data, blown-state memory
- Out: Dialogue with disguised player (E22)
- Out: Uniform art (placeholder recolour)
Notes
Suspicion rules are tuning data; restricted zones are level regions.
Acceptance criteria
- AC-1 unit Given the player wears a disguise of faction A and walks at normal speed 8 m from an A guard in a public zone, when 30 s pass, then the guard remains Unaware.
- AC-2 unit Given the same player sprints within 3 m, when evaluated, then disguise suspicion accumulates at the running rate and the guard enters Suspicious within 3 s.
- AC-3 unit Given an observer flagged
sees-through-disguise, when it perceives the player, then the disguise is ignored. - AC-4 unit Given the disguise is blown in zone Z, when the player re-enters Z still disguised, then Z’s guards treat the player as an intruder.
- AC-5 unit Given a faction B guard, when perceiving the player in faction A disguise, then normal intruder rules apply.
Waits on
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.6 Accumulate and decay awareness from stimuli per perceived target done
- mw-e12.8 Faction table and relationship queries between creatures done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.11Place traps: noisemakers, tripwires and caltrops
Description
Why
Traps let the thief shape the battlefield ahead of time — dirty tricks and disengagement are the thief combat fantasy.
What
As a thief, I want to place traps so that I can lure, slow or warn about enemies. Place trap items on valid surfaces with a placement preview: noisemaker (timed or triggered noise event), tripwire (trigger volume between two anchors → alarm noise for player, or trip/stagger), caltrops (slow + damage zone, noise when stepped). Traps are world objects with properties (flammable tripwire burns, etc.), perceivable by AI (visibility check); perceiving an armed trap makes AI avoid it and become Suspicious.
Scope
- In: Placement preview + validity, trap objects, trigger logic via e03-triggers
- In: AI avoidance of perceived traps (nav cost)
- Out: World/dungeon traps and disarming (e10-trap-disarm)
- Out: Trap art (placeholders)
Notes
Creatures can trigger player traps; the player can too (friendly fire).
Acceptance criteria
- AC-1 unit Given a caltrops item and a flat valid surface, when placed, then a slow zone (×0.5 speed) with damage 2/s exists at the location.
- AC-2 unit Given a placement on a slope > 30° or in water, when previewed, then placement is invalid with reason.
- AC-3 unit Given a tripwire between two anchors, when any creature crosses it, then its trigger fires once and it becomes spent.
- AC-4 unit Given a guard perceives an armed caltrops zone, when pathing, then its path avoids the zone if an alternative exists and it enters Suspicious.
- AC-5 unit Given a tripwire tagged
flammablein a fire volume, when 1 s passes, then it burns and is removed without triggering.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
area:gamearea:simclass:thiefepic:e10milestone:m2mw-e10.14Silent movement unlocks: soft steps, muffled gear, shadow dash
Description
Why
Anti-grind rule: progression unlocks verbs, not +12%. Silent movement should grow into new capabilities.
What
As a thief, I want to unlock ways to move more silently so that I can take bolder routes. Capability unlocks via the progression model:
soft-step(sneak gait ignores creaky-board creaks and gravel offset),muffled-gear(equipment noise halved while sneaking),shadow-dash(short burst of sneak-speed movement at walk noise while in light ≤ 0.2). Each is a data-defined modifier applied to the footstep/visibility rules.Scope
- In: Three capability modifiers as data + rule hooks
- Out: Progression UI and unlock sources (E19)
Notes
Modifiers alter inputs to e09-footstep-noise and e09-visibility-model only; rules stay pure.
Acceptance criteria
- AC-1 unit Given
soft-stepunlocked, when sneaking onwood-creaky, then no creak event is ever emitted over 1,000 seeded steps. - AC-2 unit Given
muffled-gearand equipment noise +10 dB, when sneaking, then the footstep includes +5 dB equipment noise. - AC-3 unit Given
shadow-dashin light 0.5, when activated, then it is refused because light exceeds 0.2. - AC-4 unit Given no unlocks, when evaluated, then footstep results equal the base rule exactly.
Waits on
- mw-e09.5 Emit footstep noise from speed, stance, surface and equipment weight done
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:contentarea:simclass:thiefepic:e10milestone:m2mw-e10.15Lean around corners and peek through keyholes
Description
Why
Information is the thief’s currency; peeking lets players scout routines before committing.
What
As a thief, I want to lean around corners and peek through keyholes so that I can scout without exposing myself. Lean shifts camera and exposes only the head sample point to LOS (reduced visibility). Keyhole peek on doors with a
keyholeflag gives a narrow view of the next room with the player fully hidden from that room.Scope
- In: Lean input + camera offset + LOS sample override
- In: Keyhole peek interaction
- Out: Camera system (e02-camera)
Notes
During lean, only the head sample point counts for LOS (1 of 4).
Acceptance criteria
- AC-1 unit Given the player leaning, when LOS is queried by an observer, then only the head sample point is considered.
- AC-2 unit Given a door without a keyhole, when interaction options are built, then peek is not offered.
- AC-3 integration Given a keyhole peek, when active, then the camera renders from the keyhole position and visibility to observers in the other room is 0.
- AC-4 unit Given the player is attacked while peeking, when damage applies, then peek ends immediately.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e09.1 Multi-point line-of-sight queries with partial cover and see-through done
area:gamearea:simclass:thiefepic:e10milestone:m2
M3 MVP content 1
mw-e10.16Environmental takedowns: drop, push and douse via world properties
Description
Why
The progression ladder ends at environmental takedowns; the world itself becomes the thief’s weapon, and it must be systemic.
What
As a thief, I want to drop a chandelier, cut a hanging load or push a crate from a ledge onto an enemy so that the world does my work. Any object with
suspendedproperty released (cut rope, shot chain, lever) falls under physics; impact damage = f(mass, fall height) via the damage model. Deaths/knockouts caused by environment are taggedaccidentso witnesses raise lower suspicion (Suspicious/Investigating instead of Alerted) unless they perceived the player causing it.Scope
- In: Falling-object damage rule by mass/height
- In: Accident attribution + AI response weights
- Out: Specific set-pieces (E26 content)
- Out: Rope cutting by arrows (E05, works via property)
Notes
Property-based: never "chandelier" in rules.
Acceptance criteria
- AC-1 unit Given any object tagged
suspendedwith mass 80 kg released from 5 m onto a creature, when impact resolves, then damage equals the documented mass×height curve. - AC-2 unit Given a creature killed by a falling object with no witness perceiving the player within 3 s, when attributed, then the event is tagged
accident. - AC-3 unit Given a witness to an
accident, when evaluated, then it enters Investigating (not Alerted). - AC-4 unit Given a witness perceived the player releasing the object, when evaluated, then it enters Alerted with the player LKP.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e09.9 Guards notice world anomalies: bodies, open doors, dark torches open
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
area:simclass:thiefepic:e10milestone:m3
E11Enemy AIEnemies patrol, perceive, investigate, communicate, fight, retreat, search and return to believable routines through a deterministic, data-driven behaviour runtime and the six-state alert machine (Unaware → Suspicious → Investigating → Searching → Alerted → Combat). Enemies never know the player position except through their senses.14/26 done · 54%
M1 Grey-box slice 5
mw-e11.22Owner review: accept or reject ADR-0006 (pure-TS navmesh vs recast-navigation-js)
Description
Why
mw-e11.4 (PR #162) shipped a pure-TypeScript navmesh bake and A* and recorded the choice as ADR-0006, status Proposed.
What
Owner reads docs/adr/0006-navmesh-and-pathfinding.md and accepts (flip status to Accepted) or rejects (file a migration bead to recast-navigation-js).
Acceptance criteria
AC-1 manual ADR-0006 status is Accepted or Rejected with the owner's decision recorded.
area:designarea:simepic:e11milestone:m1needs-humanmw-e11.20Bake one navmesh per agent size class (small, humanoid, large)
Description
Why
Briar Wolves (0.4 m) and Brother Horn are wider than the humanoid radius (0.35 m) every scene is baked for (mw-e11.4, ADR-0006), so they can clip corners and squeeze through gaps they should not fit; goblins (0.3 m) path more conservatively than they need to.
What
Bake each navigated scene for a small set of agent size classes (e.g. 0.3 / 0.35 / 0.6 m radius, matching heights) and pick the navmesh by a creature's NavAgent radius at query time.
Scope
- In: bake settings per size class in the navmesh data or a sibling file, query-time selection, pnpm nav:check coverage
- Out: crowd simulation
Notes
Related: src/sim/nav/bake.ts, src/tools/navmesh/bake.ts, ADR-0006 consequence 'One agent size per navmesh'.
Acceptance criteria
- AC-1 unit Given a 0.6 m gap, when a 0.35 m agent and a 0.8 m-radius agent request a path through it, then only the smaller one's path uses the gap.
- AC-2 integration Given the testbed, when the bake runs for every size class, then all navmeshes are produced in <= 10 s and content validation passes.
area:simepic:e11milestone:m1milestone:m2mw-e11.24Smell and magic-sense channel handlers for perception
Description
Why
Sense profiles can give creatures smell and a magic-sense channel, but perception (mw-e11.5) has no handler for either, so a wolf cannot smell the thief and a warded creature cannot feel a spell: those senses silently perceive nothing.
What
Register a smell handler (range, wind sensitivity, scent trails from the smell profile) and a magic-sense handler (active spells and enchanted items as sources) in the special-sense channel registry, producing typed percepts. Needs scent sources and magic sources in the sim.
Scope
• In: Smell and magic-sense handlers; their percept kinds or reuse of sensed-life
• Out: Wind simulation; scent trail simulation beyond a hook
Notes
Channel registry is src/sim/perception/channels.ts; content channels are SPECIAL_SENSE_CHANNELS.
Acceptance criteria
• AC-1 unit Given a creature with smell range 10 m downwind of the player 8 m away, when evaluated, then a percept from smell is produced; upwind it is not.
• AC-2 unit Given a creature with magic-sense and a lit spell 5 m away behind a wall, when evaluated, then a percept is produced when requiresLineOfSight is false.
Waits on
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
area:simepic:e11milestone:m1milestone:m2mw-e11.25Draw claimed search points in the AI debug overlay
Description
Why
mw-e11.17's AI debug overlay lists claimed search points per agent, but no search system claims points yet (search patterns are mw-e11.11), so the snapshot has nothing to draw.
What
When mw-e11.11 lands, add each agent's claimed search points to the AI introspection snapshot (src/sim/debug/ai-snapshot.ts) and draw them in src/render/debug/ai-overlay.ts (markers tinted by alert state, with the claiming agent in the label).
Acceptance criteria
- AC-1 unit Given an agent searching with claimed points, when the snapshot is built, then it lists those points.
- AC-2 e2e Given a searching guard and ai.debug on, then its claimed points render with no console errors.
Waits on
- mw-e11.11 Search patterns around last-known position, including hiding spots open
area:renderarea:simarea:toolsepic:e11milestone:m1mw-e11.26Persist the nav path queue so a save mid-request resumes identically
Description
Why
mw-e12.14 made a loaded world continue exactly as one never saved (creature components, brains and perception bookkeeping are all saved). One gap remains, accepted in mw-e11.4: the navmesh path request queue (src/sim/nav/queue.ts) lives in the navigation port, not the world. A save taken while an agent's path request waits or is half-searched loads with its nav.route 'lost': the agent resubmits it (same start and goal), under a new request id, and the search starts over, so the resumed run's state hash differs from the uninterrupted run's for a few ticks onward. Nothing breaks for the player, but the resume-determinism property (and any golden that saves mid-chase) does not hold with navmesh travel.
What
Make the queue's state part of the world so saves carry it: its next request id, the waiting requests in order (request + the budgets each search step was given, so a search can be rebuilt by replaying its steps) and finished results not yet taken. Load rebuilds the queue from that state, and the 'lost route resubmits' path stays only for saves from before.
Acceptance criteria
- AC-1 integration Given navmesh AI travel with a tight per-tick budget and a save taken while a request is half-searched, when loaded into a fresh world and run on, then the state hash at the end equals the uninterrupted run's.
- AC-2 unit Given a save from before this change with a pending route, when loaded, then the agent resubmits and reaches its goal (no regression).
area:simepic:e11milestone:m1milestone:m2
M2 Class fantasies 7
mw-e11.10Guards investigate suspicious stimuli at their perceived location
Description
Why
Distractions only work if guards go and look. Investigation is the core lever for thrown objects, noises and glimpses.
What
As a thief, I want a guard to walk over and check a noise or glimpse so that I can lure him away from his post. In Suspicious: stop, turn toward stimulus, text bark cue. In Investigating: path to stimulus position at walk (or cautious) gait, look around on arrival with a scan pattern, inspect the source object if perceived (e.g. a thrown bottle), then decide: nothing found → de-escalate; more stimuli → escalate. Multiple stimuli: most recent strong one wins; weaker stimuli queue up to 3.
Scope
- In: Investigate behaviour, scan pattern, source inspection, stimulus queue
- In: Cautious gait when postAlert
- Out: Group investigation roles (e11-communication)
- Out: Search around LKP (e11-search-behaviour)
Notes
Investigation position is the perceived position (may be offset by sound routing), not the true source.
Acceptance criteria
- AC-1 unit Given an Investigating guard with target position 12 m away, when simulated, then it arrives within (12 / walkSpeed + 1) s and performs a scan of ≥ 3 look directions.
- AC-2 unit Given a second, louder stimulus while investigating, when perceived, then the guard retargets to the new position and the first is queued.
- AC-3 unit Given an unreachable stimulus position (on a roof), when investigating, then the guard moves to the closest reachable point, looks toward the position for the scan duration, then de-escalates.
- AC-4 integration Given a scenario where a noise occurs behind a closed door, when the guard investigates, then it opens the door (if unlocked) before scanning the room.
- AC-5 unit Given a queue already holding 3 stimuli, when a 4th weaker stimulus arrives, then the weakest is dropped.
Waits on
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e11.8 Target memory and last-known position without omniscience done
area:simepic:e11milestone:m2mw-e11.11Search patterns around last-known position, including hiding spots
Description
Why
A convincing search — fanning out, checking corners and wardrobes — makes escaping feel earned, and gives up plausibly so failure does not force a reload.
What
As a thief, I want searching guards to check plausible places near where they lost me so that hiding well matters and hiding badly gets me caught. Searching generates candidate points around LKP (+ bounded prediction): nav-reachable points ranked by distance, cover/darkness, and whether the point is out of sight from LKP; plus hiding spots within radius. Agents claim points (no two agents search the same point), check hiding spots with a probability based on whether they saw the target enter it, expand radius over time, then give up.
Scope
- In: Candidate generation, ranking, claiming, radius expansion
- In: Hiding-spot checks with probability rules
- In: Diligence trait scaling of duration
- Out: Hiding place mechanics (e09-hiding-places)
- Out: Alarm raising (e11-communication)
Notes
Probability of checking a hiding spot: 1.0 if target was seen entering; otherwise base 0.25 scaled by proximity to LKP. Seeded RNG.
Acceptance criteria
- AC-1 unit Given a Searching guard with LKP L, when candidates are generated, then all lie within the current radius, are nav-reachable and the first 3 are sorted by the ranking function.
- AC-2 unit Given two guards searching the same LKP, when they claim points, then no point is claimed by both.
- AC-3 unit Given the guard saw the target enter a hiding spot, when searching, then it checks that spot first with probability 1.0.
- AC-4 unit Given a hiding spot not seen being entered, 10 m from LKP, when evaluated with a fixed seed, then the check decision matches the documented probability formula.
- AC-5 unit Given no reachable candidates (LKP on an isolated ledge), when searching, then the guard looks toward LKP from the nearest point, then de-escalates at the search timeout without error.
- AC-6 integration Given a scenario where the player hides in darkness 8 m from LKP and stays still, when a single guard searches for 60 s, then the guard ends Searching without entering Combat in the scripted seed.
Waits on
- mw-e03.23 Hide in wardrobes, barrels and haystacks and search containers done
- mw-e11.10 Guards investigate suspicious stimuli at their perceived location open
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e11.8 Target memory and last-known position without omniscience done
area:simepic:e11milestone:m2mw-e11.12Allies share alerts by shouts, sight signals and alarm devices
Description
Why
Enemies that communicate make a single mistake escalate believably, and give players targets — silence the shouter, cut the bell rope.
What
As a thief, I want guards to call for help through the world rather than telepathy so that I can prevent or exploit alarms. Channels: shout (emits a loud noise event via sound propagation carrying a report: LKP, state), visual signal (allies in LOS within 25 m receive the report), alarm device (agents path to a nearest
alarmobject and activate it via the interaction/mechanism system, broadcasting Alerted to a faction zone). Only allies (faction relation ally/friendly) accept reports. Group roles in Alerted/Searching: one caller, others flank/search.Scope
- In: Report message type, shout/visual/alarm channels
- In: Faction filtering
- In: Basic role assignment (caller, searcher, guard-post-holder)
- Out: Reinforcement spawning (e09-failed-stealth-outcomes)
- Out: Speech/voice (text barks only — e09-alert-barks)
Notes
Alarm objects are world mechanisms with an
alarmproperty; if destroyed or jammed, the AI must fall back to shouting. Related: alarm bell mechanism may be authored with E03 switches.Acceptance criteria
- AC-1 unit Given a guard entering Alerted, when it shouts, then a noise event of the configured loudness is emitted and allies whose perceived level exceeds their hearing threshold receive the report.
- AC-2 unit Given a guard behind 2 closed doors whose shout is attenuated below an ally’s threshold, when shouting, then that ally receives nothing.
- AC-3 unit Given a creature of a hostile faction within hearing, when a shout occurs, then it does not receive the report (it may still perceive the noise as a stimulus).
- AC-4 unit Given an alarm object that has been disabled (jammed or destroyed), when a guard attempts to use it, then it falls back to shouting within 2 s.
- AC-5 integration Given a scenario with 3 guards and one alarm bell, when the first guard spots the player and reaches the bell, then all guards in the bell’s zone enter Alerted within 1 tick of activation.
- AC-6 unit Given a guard is knocked out mid-path to the alarm, when the tick runs, then no alarm activation occurs.
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e11.8 Target memory and last-known position without omniscience done
- mw-e12.8 Faction table and relationship queries between creatures done
area:simepic:e11milestone:m2mw-e11.14Creatures retreat, flee to allies and cower based on morale
Description
Why
Enemies that retreat, regroup or flee make fights and scares feel alive and allow non-lethal victories.
What
As a player, I want frightened or wounded creatures to disengage so that encounters end in more ways than death. Consumes
Flee/Cowerintents from e12-fears-morale and health thresholds: flee away from the fear source toward allies, safe zones or a lair point; cower when cornered; regroup and re-enter Alerted/Searching when morale recovers. Fleeing creatures may shout (communication) and drop carried items (hook).Scope
- In: Flee destination scoring, cower, regroup
- In: Surrender intent event (hook for E14 dialogue)
- Out: Morale model (e12-fears-morale)
- Out: Surrender dialogue (E14/E22)
Notes
Flee destination scoring: distance from threat, ally proximity, darkness preference per creature.
Acceptance criteria
- AC-1 unit Given a creature with a
Fleeintent and threat at T, when fleeing, then after 3 s its distance from T has increased and its path does not pass within 2 m of T. - AC-2 unit Given a fleeing creature with no escape path (dead end), when cornered, then it enters
Cowerwithin 1 s. - AC-3 unit Given morale recovers above the regroup threshold, when the intent clears, then the creature enters Searching with the threat’s last-known position.
- AC-4 unit Given a creature tagged
fearless(morale disabled), when at 1 health, then it never flees.
Waits on
- mw-e11.12 Allies share alerts by shouts, sight signals and alarm devices open
- mw-e11.13 Combat behaviour: engage, space, select attacks and share targets done
- mw-e12.10 Creature fears and morale that trigger fleeing and cowering open
area:simepic:e11milestone:m2mw-e11.15Guards return to routines, visibly changed by what happened
Description
Why
Failure creates stories: after an alert guards should go back to work, but the world should remember — lit torches, doubled posts, grumbling barks.
What
As a thief, I want guards to settle back down after an alert but stay changed so that my mistakes reshape the level instead of resetting it. On de-escalation: path back to routine, apply post-alert adjustments from data (e.g. relight extinguished lights on route, close opened doors, patrol in pairs, add a checkpoint post, carry a torch), keep heightened awareness for a duration. Adjustments are data-driven per behaviour profile and recorded as world facts.
Scope
- In: Return behaviour, post-alert adjustment list, persistence via world state
- In: Barks cue on return
- Out: Level lockdowns/reinforcements (e09-failed-stealth-outcomes)
Notes
Adjustments list example:
relight-seen-extinguished-lights,close-seen-open-doors,pair-up,carry-torch.Acceptance criteria
- AC-1 unit Given a guard de-escalates with
postAlert=true, when simulated, then it resumes its route within 5 s and awareness rate multiplier is 1.5 for 300 s, then 1.0. - AC-2 unit Given a guard with the
relight-seen-extinguished-lightsadjustment that perceived an extinguished torch during the alert, when returning, then it paths to the torch and relights it before resuming. - AC-3 unit Given the torch was destroyed (not merely extinguished), when the adjustment runs, then the guard skips it and switches to
carry-torchif declared. - AC-4 integration Given a save after an alert with
pair-upapplied, when loaded, then the paired patrol persists.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simepic:e11milestone:m2mw-e11.16Author AI tuning data: thresholds, decay timers, search durations
Description
Why
Stealth feel is tuned constantly; all numbers must be data so designers can iterate without code and tests can pin defaults.
What
A
AiTuningschema and default files per behaviour profile: awareness thresholds per state, accumulation rates per sense, grace and decay timers, state timeouts (Suspicious 6 s, Investigating 20 s, Searching 60 s, Alerted 120 s defaults), post-alert multiplier and duration, search radius growth, token budgets, think rates. Profiles inherit (guard-default→guard-lazy). Hot-reload in dev.Scope
- In: Schema, inheritance, defaults, hot reload in dev builds
- In: Pinned default-value tests
- Out: Sense ranges (e12-sense-profiles)
- Out: Stealth emitter tuning (e09-stealth-tuning-data)
Notes
Changing defaults requires updating pinned tests deliberately — that is the point.
Acceptance criteria
- AC-1 content Given the default tuning files, when validated, then every state has a positive timeout and thresholds are strictly increasing Suspicious < Investigating < Detected.
- AC-2 unit Given
guard-lazyinheriting fromguard-defaultoverriding only Searching timeout 30 s, when resolved, then all other values equal the parent. - AC-3 unit Given an inheritance cycle, when loaded, then loading fails naming the cycle.
- AC-4 integration Given a dev build, when a tuning file is edited, then running agents use the new values within 1 s without reload.
- AC-5 unit Given the shipped defaults, when the pinned-default test runs, then each documented default matches.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
area:contentarea:simepic:e11milestone:m2mw-e11.18Perf budget: 24 active AI agents within sim frame budget
Description
Why
The hardware baseline requires steady 60 fps; AI perception, pathing and sound propagation are the likeliest sim hot spots.
What
Add an AI stress scene and budget tests: 24 agents (mixed fixtures) in Alerted/Searching with continuous noise events and a moving target. Budgets: total AI+perception+sound sim cost p95 ≤ 3 ms per tick on the reference machine; ≤ 5 ms on the Low-preset class machine. Includes LOS cache and level-of-detail think rates (agents > 40 m from player think at 2 Hz).
Scope
- In: Stress scene, budget assertions, AI LOD think rates, LOS result caching
- Out: Render perf (E32)
Notes
Numbers cite the contract hardware baseline.
Acceptance criteria
- AC-1 perf Given the AI stress scene with 24 agents, when run for 60 s headless on the reference machine, then AI+perception+sound cost p95 ≤ 3 ms per tick.
- AC-2 unit Given an agent 50 m from the player with LOD enabled, when 60 ticks run, then it thinks 2 times; within 40 m it thinks 10 times.
- AC-3 unit Given AI LOD, when an out-of-range agent receives a noise stimulus above threshold, then it is promoted to full think rate on the next tick (no missed alerts).
- AC-4 perf Given the stress scene in the browser (High), when measured, then frame time p95 ≤ 16.7 ms.
Waits on
- mw-e11.11 Search patterns around last-known position, including hiding spots open
- mw-e11.13 Combat behaviour: engage, space, select attacks and share targets done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfarea:simepic:e11milestone:m2
E12Creature FrameworkCreatures are data: unique senses, locomotion, attacks, property-driven vulnerabilities, factions/relationships, dispositions, fears/morale, personalities, needs and non-hostile interaction affordances are all declared in validated content and consumed by AI and stealth systems — ready for the bestiary (E13) and social creatures (E14).10/20 done · 50%
M1 Grey-box slice 4
mw-e12.18Creatures stand on the ground and get knocked back through a character controller
Description
Why
Spawned creatures (mw-e12.4) are placed entities: they have no body in the controller or physics world, so they do not fall to the ground from a raised spawn, and hit reactions compute knockbacks and launches for them that nothing applies (the pushers act on the player's CharacterController and physics objects only). The constitution wants creatures that are creatures, not target dummies.
What
Give each spawned creature a kinematic character body sized from its nav agent (radius, height, step height, max slope) so it stands on and collides with the level, and apply hit-reaction pushes to it like the player's. Locomotion speeds stay AI's (e11) business.
Scope
- In: per-creature controller capsule from its locomotion; gravity; knockback/launch pushes.
- Out: pathfinding (mw-e11.4), AI steering.
Acceptance criteria
- AC-1 integration Given fixture-hound spawned 2 m above the floor, when 60 ticks pass, then it stands on the floor.
- AC-2 integration Given a knockback-tier hit on fixture-guard, when the reaction plays, then the guard moves away from the attacker and never through a wall.
- AC-3 unit Given the same seed and inputs, two runs hash identically.
area:contentarea:simepic:e12milestone:m1mw-e12.17Creature projectiles collide with level geometry
Description
The mw-e12.5 projectile executor flies straight and only tests living hurtboxes (placement + health); walls and props do not stop it. Once the physics layer (e03.10) exposes sim-side raycasts/sweeps, stop projectiles at geometry and emit an impact event.
area:contentepic:e12milestone:m1mw-e12.19Creatures name their own lock-on targetable profile in data
Description
Why
Spawned creatures are lock-on targets (mw-e12.4), but their lock points are derived from their nav agent height (chest 60 %, head 90 %, base 25 %) because the creature schema has no way to name a targetable profile, though src/content/types/targetable.ts says creatures will. A spider's weak point or a wraith's head should be data, not a formula.
What
Add an optional targetable ref to CreatureDef; the spawner uses it when present and keeps the derived profile as the default.
Scope
- In: schema field, docs regen, spawner use, fixture untouched (default).
- Out: bow weak-point targeting (e05).
Acceptance criteria
- AC-1 unit Given a creature naming a targetable profile, when spawned in a world with lock-on, then its Targetable points equal the profile's.
- AC-2 unit Given a creature naming none, then its points are the derived chest/head/base profile.
area:contentarea:simepic:e12milestone:m1mw-e12.20Spawn creature hurtboxes from content regions and toggle weak points by state
Description
Follow-up from mw-e05.6 (PR #327). The creature schema now has a regions array (region, multiplier, armor, enabledIn, underlying) and the sim resolves weakpoints via setRegionState/effectiveHurtbox, but the spawner does not build hurtboxes from regions and no creature AI/state calls setRegionState. No shipped creature declares regions yet.
AC-1 unit A creature with authored regions spawns hurtboxes carrying those multipliers, armor and enabledIn.
AC-2 unit A creature entering a state listed in a weakpoint's enabledIn (e.g. roaring) enables it via setRegionState; leaving disables it.
AC-3 content At least one slice creature (e.g. the forgotten shield) declares an armored region so deflection can be seen in play.
area:contentepic:e12milestone:m1
M2 Class fantasies 6
mw-e12.7Drive creature vulnerabilities from world properties, not pairings
Description
Why
The constitution requires systemic interactions: a creature tagged
flammableshould burn from any fire source, andundeadshould respond to any light effect, without scripted spell/creature pairs.What
As a player, I want creatures to react to the same world properties as objects so that my experiments transfer. Creatures carry world-property tags (from e03-world-properties) plus
vulnerabilities/resistanceskeyed by damage type or property effect (e.g.heat: x2,frozen: immune,light: stagger). A sim rule applies element-propagation effects to creatures exactly as to objects, then applies the creature multiplier.Scope
- In: Property tags on creatures participate in element propagation
- In: Multiplier + immunity + special reaction (stagger, flee trigger) data
- In: Resolution order documented
- Out: New element types (E03)
- Out: Specific creature weaknesses (E13)
Notes
Express everything as properties; tests must use property names, never spell names. Reactions can emit a
FearStimulusconsumed by e12-fears-morale.Acceptance criteria
- AC-1 unit Given any creature tagged
flammableinside an active fire volume, when 1 s of sim time passes, then it gains theburningstate. - AC-2 unit Given a creature with
resistances.heat: immune, when inside a fire volume, then it takes 0 heat damage and does not gainburning. - AC-3 unit Given a creature with
vulnerabilities.cold: 2.0, when it receives a 10-point cold packet, then it loses 20 health. - AC-4 unit Given a creature with reaction
light: staggerand light level at its position rising above the configured threshold within 1 tick, then it enters stagger once and not again until light falls below the threshold (no re-trigger spam). - AC-5 content Given a vulnerability keyed by an unknown property or damage type, when validated, then validation fails listing known keys.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:contentarea:simepic:e12milestone:m2mw-e12.10Creature fears and morale that trigger fleeing and cowering
Description
Why
Creatures have fears. Frightening a wolf pack with fire or breaking goblin morale by felling their leader are non-lethal solutions the constitution wants.
What
As a player, I want to exploit what creatures fear so that I can win encounters without killing everything.
fears: list of {stimulus property or faction or event, intensity}. Morale 0–100 per creature, reduced by fear stimuli, ally deaths, low health, leader death; restored over time and near allies. Below thresholds the creature emitsCowerorFleeintents for the AI.Scope
- In: Fear stimulus matching via properties (e.g. anything
burningor light level > X) - In: Morale model + thresholds + recovery
- In: Group morale shock when a
leader-tagged ally dies - Out: Flee movement behaviour (e11-retreat-flee)
- Out: Intimidation dialogue (E14)
Notes
Fear stimuli are perceived — the creature must sense the burning object (via e11-perception) for it to count.
Acceptance criteria
- AC-1 unit Given a creature with fear {property
burning, intensity 30} and morale 50, when it perceives a burning object within 5 m, then morale drops to 20 and aFleeintent is emitted (flee threshold 25). - AC-2 unit Given a fear stimulus the creature cannot perceive (behind a wall), when evaluated, then morale is unchanged.
- AC-3 unit Given a
leader-tagged creature dies, when allies within 20 m perceive it, then each ally loses the configured leader-shock morale once. - AC-4 unit Given morale 10 and no stimuli, when 30 s pass near 2 allies, then morale recovers by the configured rate and the
Fleeintent clears once above 35 (hysteresis). - AC-5 content Given a fear with intensity outside 0–100, when validated, then validation fails.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:contentarea:simepic:e12milestone:m2- In: Fear stimulus matching via properties (e.g. anything
mw-e12.13Declare non-hostile creature interactions (feed, talk, calm) in data
Description
Why
Not everything exists solely to be killed. Social creatures (E14) need a framework hook: feeding, talking, intimidating, petting or trading must be declarable per creature.
What
As a designer, I want to declare which non-hostile verbs a creature accepts and under what conditions so that E14 can build social creatures without new engine code.
interactions: list of {verb (talk,feed,intimidate,calm,pet,trade,give), conditions (disposition range, alert state ∈ {Unaware, Suspicious}, item tag required), outcome event id}. Available verbs surface as contextual prompts via the interaction system; executing emits a typed sim event consumed by dialogue/disposition rules.Scope
- In: Schema, condition evaluation, prompt surfacing, outcome events
- In: Alert-state gating (cannot pet a creature in Combat)
- Out: Dialogue trees (e22-dialogue-runtime)
- Out: Specific social creatures and their outcomes (E14)
Notes
Outcome events feed e12-dispositions modifier rules, e.g.
feedwith anyedibleitem applies thefedmodifier.Integration note: Promoted to P1 — befriending/helping creatures (Brother Horn, the goblins, Clatter) is an MVP constitution pillar and depends on this.
Acceptance criteria
- AC-1 unit Given a creature declaring
feedrequiring item tagedible:meat, when the player holds no such item, then thefeedverb is not offered. - AC-2 unit Given the same creature in Combat state, when the player holds meat, then
feedis not offered. - AC-3 integration Given the grey-box testbed and a fixture creature declaring
feed, when the player feeds it, then the item is removed from inventory, aCreatureInteracted{verb: feed}event fires and disposition rises by thefedmodifier. - AC-4 content Given an interaction with an unknown verb, when validated, then validation fails listing allowed verbs.
- AC-5 unit Given two verbs with overlapping conditions, when prompts are built, then both are offered in declared order.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
area:contentarea:gamearea:simepic:e12milestone:m2mw-e12.11Personality traits that weight AI decisions per individual
Description
Why
Personalities make creatures memorable: a lazy guard who gives up searching early, a curious goblin who investigates every noise.
What
Trait vector (bravery, curiosity, aggression, diligence, sociability, greed) 0–1 per creature, def default plus seeded per-spawn jitter within a declared range. Traits are exposed as inputs to the behaviour runtime (utility weights or BT conditions) and to tuning curves (e.g. diligence scales search duration).
Scope
- In: Schema, deterministic jitter, accessor API
- In: Documented list of which AI behaviours read which trait
- Out: Dialogue personality/voice (E22)
- Out: Named-character authored personalities (E13/E14 content)
Notes
Jitter must come from the seeded sim RNG keyed by entity id so replays match.
Acceptance criteria
- AC-1 unit Given a def with curiosity 0.5 ± 0.2, when 1,000 spawns are sampled with a fixed seed, then all values lie within 0.3–0.7 and the sequence is identical across runs.
- AC-2 unit Given diligence 1.0 vs 0.0 with base search duration 60 s and diligence scale 0.5–1.5, then search durations are 90 s and 30 s respectively.
- AC-3 content Given a trait outside 0–1, when validated, then validation fails.
- AC-4 unit Given a creature with no personality section, when resolved, then all traits default to 0.5 with zero jitter.
Waits on
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:contentarea:simepic:e12milestone:m2mw-e12.12Creature needs (hunger, sleep, fatigue) with behaviour hooks
Description
Why
A hungry beast that can be lured with meat or a guard who dozes at 3am turns creatures into systems the player can read and exploit.
What
As a thief, I want creatures to eat, sleep and tire so that I can time or engineer my approach. Needs (hunger, sleep, fatigue, plus extensible custom needs) rise over sim time at data-defined rates; satisfiers are world objects tagged with properties (
edible:meat,bed) or schedule windows. Crossing thresholds emits intents (SeekFood,Sleep) the behaviour runtime may choose. Sleeping creatures use a reduced sense profile.Scope
- In: Need decay/satisfaction model
- In: Property-based satisfier discovery via perception
- In: Sleep modifies senses (e.g. sight off, hearing −20 dB)
- Out: Day/night world clock (owned by world/time systems — Related: world clock)
- Out: Poisoned bait (e10 / alchemy post-MVP)
Notes
Needs must be cheap: update at 1 Hz, not per tick. Related: a world time-of-day clock is needed for schedule windows; if none exists use sim time with a configurable day length.
Acceptance criteria
- AC-1 unit Given hunger rate 10/min and threshold 70, when 7 sim minutes pass from 0, then a
SeekFoodintent is emitted exactly once. - AC-2 unit Given a hungry creature that perceives any object tagged
edible:meatwithin 15 m, when it choosesSeekFood, then its target is the nearest perceived edible object. - AC-3 unit Given a sleeping creature, when a 30 dB noise occurs 5 m away and its awake threshold is 25 dB with sleep penalty 20 dB, then it does not wake; a 50 dB noise wakes it into Suspicious.
- AC-4 unit Given an edible object that is removed while a creature walks to it, when it arrives, then the intent fails gracefully and the creature returns to its routine within 3 s.
- AC-5 unit Given 200 creatures with needs, when 1 sim hour elapses, then needs update 3,600 times per creature (1 Hz) and never per tick.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:contentarea:simepic:e12milestone:m2mw-e12.16Write the creature authoring guide with a worked example
Description
Why
The world-content group will author the bestiary in parallel; a single guide keeps creatures consistent and systemic.
What
A designer guide
docs/content/creatures.md: every schema section explained, how senses/fears/needs/personality affect AI, property-based vulnerability rules, a worked example built from fixtures (not a real bestiary creature), checklist for a new creature (data, smoke test, placeholder asset ids, prompt bead).Scope
- In: Guide + checklist + links to generated schema reference
- Out: Bestiary entries (E13)
Notes
Link from docs/backlog-contract.md is out of scope for this bead; content group will reference the guide.
Acceptance criteria
- AC-1 content Given the guide, when its worked-example JSON is extracted by a doc test, then it validates against
CreatureDef. - AC-2 manual Given a designer unfamiliar with the code, when following the checklist, then they can add a new creature that passes the smoke harness in ≤ 30 min.
- AC-3 unit Given the generated schema reference, when a schema field is added without documentation, then the doc-coverage check fails.
Waits on
- mw-e12.13 Declare non-hostile creature interactions (feed, talk, calm) in data open
- mw-e12.15 Auto-generate smoke tests for every creature data file done
area:contentarea:designepic:e12milestone:m2
E13BestiaryThe ten MVP creature types from the story bible (the Forgotten, Rootcellar Goblins, Brother Horn, Loom Spiders, Briar Wolves, Hushlings, Tallow Ooze, Mimics, Hollow Sentinels, the Sleepless Abbot) exist as data on e12-creature-schema with behaviours, property-driven vulnerabilities, readable telegraphed attacks and at least one avoid/trick option each. Horn and the Abbot have authored boss encounters.1/19 done · 5%
M2 Class fantasies 2
mw-e13.2Write the MVP bestiary design sheet for all ten creatures
Description
Why
Creatures are creatures, not target dummies; each needs behaviours, fears and non-combat options designed before data is authored.
What
docs/design/bestiary.md: for each of the ten MVP creatures — role, senses, locomotion, faction, move list with telegraph type and timings, vulnerabilities as world properties (never pairings), fears, avoid/trick/social options, placement areas, song-intensity behaviour, and asset needs pointer.
Scope
- In: Design sheet for all ten creatures
- In: Telegraph table
- In: Property-vulnerability table
- Out: Creature data (per-creature items)
- Out: Creature lore text (e39-item-creature-text)
Notes
Names and lore must match docs/narrative/story-bible.md §8 exactly.
Acceptance criteria
- AC-1 content Given the sheet, when reviewed, then every creature lists >= 1 avoid option and >= 1 trick or social option.
- AC-2 content Given the sheet, when reviewed, then every vulnerability is expressed as a property or stimulus (e.g. "frozen and fragile shatters"), with zero spell- or item-name pairings.
- AC-3 content Given the telegraph table, when reviewed, then every unblockable or grab move lists windup >= 500 ms and a distinct audio+VFX cue id.
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:designepic:e13milestone:m2mw-e13.3Forgotten crumble and reassemble unless bones are scattered or burned
Description
Why
Bible §8: the Forgotten crumble and reassemble — understanding that rule, and breaking it systemically, is the fight.
What
As a player, I want downed skeletons to rise again unless I deal with their bones so that I learn to fight smart (scatter, burn, smash).
On Died, the skeleton becomes a bone-pile entity (physics, flammable after oil, fragile). After 8 s it reassembles if >= 60% of bones remain within 2 m. Blocked by: bones scattered by force/impulse, bones burning or burnt, crushing blunt hit on the pile, or holy water (tag consecrated). Animate Bones may claim a pile.
Scope
- In: Bone-pile entity, reassembly timer and rules, property hooks
- Out: Animate Bones spell (e08-spell-animate-bones)
Notes
Rules via properties only: impulse, burning, blunt impact, consecrated. Reassembly emits a telegraph (bones rattle, 1 s) so players can react.
Acceptance criteria
- AC-1 unit Given a bone pile untouched, when 8 s pass, then the skeleton reassembles at 50% health with a 1 s telegraph beforehand.
- AC-2 unit Given a bone pile hit by an impulse that moves > 40% of bones beyond 2 m, when 8 s pass, then it does not reassemble and becomes inert.
- AC-3 unit Given a bone pile with burning = true for >= 2 s, when evaluated, then it becomes burnt and never reassembles, regardless of the heat source.
- AC-4 integration Given a pile reassembles while the player stands on it, when it completes, then the player is displaced safely with no clipping (edge).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e12.7 Drive creature vulnerabilities from world properties, not pairings open
- mw-e13.1 Add the Forgotten skeleton with the e04 three-move set done
area:contentepic:e13milestone:m2
M3 MVP content 15
mw-e13.4Add monk Forgotten and habit routines that ignore quiet players
Description
Why
The Forgotten are people who forgot themselves; they sweep, pray and haul — and can be avoided.
What
As a thief, I want skeletons absorbed in old habits so that I can slip past, and as a player I want the monk variant to fight differently.
forgotten-monk (staff sweep, censer swing that carries live embers) plus habit routines as patrol data: sweeping, praying at altars, hauling buckets/stone. Habit trance: perception reduced 50% while performing a habit; noise above a threshold breaks it. Calmed by hymn-tagged noise (see e14-hum-vesper-hymn); hostile regardless at song intensity 3.
Scope
- In: Monk variant data and moves, habit routine library, trance perception modifier, calm rule
- Out: Song-intensity state (e13-song-intensity)
- Out: Placement (E26)
Notes
The censer is a heat source: it ignites flammable things it touches, including the monk's own oil-soaked surroundings. Related: e03-gas-clouds (P2) — if it lands, the censer may also leave a smoke cloud.
Acceptance criteria
- AC-1 content Given forgotten-monk.json, when validated, then its moves pass readability and its censer move declares a heat stimulus via the stimulus API.
- AC-2 integration Given a sweeping Forgotten and a crouched player moving at walk speed 4 m behind it in dim light, when 10 s pass, then it stays Unaware.
- AC-3 integration Given a Forgotten in Combat, when a hymn-tagged noise reaches it at song intensity <= 2, then it drops to Unaware and resumes its habit within 3 s.
- AC-4 integration Given song intensity 3, when the hymn is heard, then the Forgotten ignores it and stays hostile (edge).
Waits on
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
- mw-e13.1 Add the Forgotten skeleton with the e04 three-move set done
- mw-e14.1 Let the player hum the Vesper hymn after reading the Vesperine Hours open
area:contentepic:e13milestone:m3mw-e13.5Drive creature behaviour from a global song-intensity world state
Description
Why
The Nightjar's song is the dungeon's weather: when it swells, the Forgotten turn, Hushlings multiply and Clatter forgets.
What
As a designer, I want one world fact song.intensity (0–3) that creature data can react to so that story beats reshape encounters without scripts.
Fact set by quest effects (e39-main-quest-data) and by events (sour bell ring = +1 for 5 minutes; Horn dead = +1 permanent). Creature data declares modifiers per intensity: aggression, perception, spawn weights, calm immunity.
Scope
- In: Fact registration, timed modifiers, creature-data modifier hook, debug command
- Out: Which quest beats set it (E39)
Notes
Also read by audio (music tension layer) — Related: e28-adaptive-music.
Acceptance criteria
- AC-1 unit Given intensity 1 and a +1 timed modifier for 300 s, when 300 s elapse, then intensity returns to 1 exactly once.
- AC-2 unit Given modifiers pushing intensity above 3, when evaluated, then it clamps to 3 (edge).
- AC-3 integration Given a Hushling spawner with weights per intensity, when intensity rises from 1 to 2, then spawn weights switch on the next spawner evaluation.
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e27.5 Query world facts with a shared condition API for all systems done
area:simepic:e13milestone:m3mw-e13.6Add Rootcellar goblin combatants: slings, nets and loud alarms
Description
Why
Goblins are clever scavengers, not fodder: they call for help, flee when outnumbered and can be talked to.
What
As a player, I want goblins that fight like a clan (slings, nets, alarms, retreat) so that fighting them feels different from undead.
goblin-scavenger and goblin-slinger data: sling stone (ranged, 300 ms windup), net throw (grab-class, 500 ms, entangles 2 s, flammable), kettle-helm (head region blunt resistance 0.5, pierce deflect). Alarm pots and shouts via e11-communication. Morale: flee when allies are outnumbered 2:1. Immune to song and Hushling mimicry (they do not dream).
Scope
- In: Two goblin variants, moves, head region armour, morale thresholds, alarm device entity
- Out: Talking, trade, capture-negotiation (E14)
- Out: Camp placement (e26-deepworks-encounters)
Notes
Surrender intent (e11-retreat-flee) opens E14 negotiation.
Acceptance criteria
- AC-1 content Given both goblin files, when validated, then the net throw declares a grab telegraph >= 30 ticks with distinct cue ids.
- AC-2 integration Given a goblin netted player, when any heat source touches the net, then the net burns off and the player is freed.
- AC-3 integration Given 3 goblins facing a player plus 2 allied creatures, when one goblin falls, then the rest flee toward the nearest alarm device.
- AC-4 unit Given song intensity 3, when goblin perception and aggression are evaluated, then they are unchanged (edge: goblins immune).
Waits on
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e05.6 Headshots, weak points and armored regions that deflect done
- mw-e11.12 Allies share alerts by shouts, sight signals and alarm devices open
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.10 Creature fears and morale that trigger fleeing and cowering open
area:contentepic:e13milestone:m3mw-e13.7Add Loom Spiders that weave readable webs and fear bright light
Description
Why
Loom spiders make the Outer Ring feel alive and teach "light and fire change the fight".
What
As a player, I want pale dog-sized spiders that climb walls, web routes and flee bright light so that I can drive them off instead of fighting.
loom-spider: climber locomotion (walls/ceilings), bite (poison), web spit (slows 40% for 3 s, 300 ms windup). Places web entities (flammable, blocks passage, can carry copied rune text). Fear: light level >= 0.7 at its position triggers flee. Fire: burning = flee + damage.
Scope
- In: Creature data, web entity prefab with readable text slot, fear-of-light rule
- Out: Web rune-clue puzzle placement (e26-knot-rune-navigation)
Notes
Webs readable by any class with a light source; sorcerers with Hornfolk fragments read more (content in E26/E39).
Acceptance criteria
- AC-1 content Given loom-spider.json, when validated, then it declares climber locomotion and a light-level fear threshold.
- AC-2 integration Given a spider in Combat, when the light level at its position rises to >= 0.7 from any source, then it flees to the darkest reachable point within 20 m.
- AC-3 integration Given a web in a doorway, when it ignites, then it burns away within 3 s and the doorway navmesh link opens.
- AC-4 integration Given no dark point is reachable, when fear triggers, then the spider cowers in place and its defence drops (edge).
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.10 Creature fears and morale that trigger fleeing and cowering open
- mw-e12.6 Support walker, climber, flier, swimmer and burrower locomotion data done
area:contentepic:e13milestone:m3mw-e13.8Add silent Briar Wolf packs that can be fed, scared or remembered
Description
Why
The wolves' silence is the first uncanny sign; a pack not fought should remember the player.
What
As a player, I want a wolf pack that circles, flanks and can be fed or frightened so that the Briarwood has a living threat with options.
briar-wolf: fast walker, smell + hearing; bite, lunge (300 ms), flank coordination. Fear of fire (burning within 4 m). Feeding: meat items (edible:meat) thrown near the pack pacify for 120 s and raise pack disposition. Pack memory world fact (wolves.fed, wolves.harmed) persists and changes greeting behaviour. Sleepwalkers are ignored (song stills animals).
Scope
- In: Wolf data, pack coordination tags, feeding rule, fear rule, pack-memory facts
- Out: Den placement (e25-wolf-den)
Notes
Silent: wolves have no howl cue while the breach is open; add a howl variant for the "Vespers Rung" ending state.
Acceptance criteria
- AC-1 integration Given a pack Suspicious of the player, when an edible:meat item lands within 5 m, then the pack eats and stays non-hostile for 120 s.
- AC-2 integration Given a burning object within 4 m of a wolf, when evaluated, then the wolf backs away and will not path through the fire.
- AC-3 integration Given fact wolves.fed >= 2, when the player next enters the den, then the pack starts at disposition Wary instead of Hostile.
- AC-4 integration Given a sleepwalking NPC passes through the pack, when perceived, then no wolf targets the NPC (edge).
Waits on
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.10 Creature fears and morale that trigger fleeing and cowering open
- mw-e12.13 Declare non-hostile creature interactions (feed, talk, calm) in data open
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
area:contentepic:e13milestone:m3mw-e13.9Add blind Hushlings that hunt by sound and break at loud noises
Description
Why
Hushlings are the thief's nightmare and playground: silence is survival, loudness is a weapon.
What
As a thief, I want blind fog-children that hunt purely by sound so that silence and distraction are my tools; as a knight, I want to learn that my clanking draws them.
hushling: sight disabled, hearing only, drifts; grasp attack (500 ms, unblockable, distinct whisper cue). Mimic barks using the names of the missing ("Da? I'm cold"). Any noise event with loudness >= 0.85 within 6 m tears them apart (dissipate). Ignore goblins.
Scope
- In: Creature data with hearing-only senses, loud-noise dissipation rule, mimic bark set ids
- Out: Bark text (e39-item-creature-text)
- Out: Placement (E25/E26)
Notes
Loud sources include bells, Thunderclap, shattered glass, shield bash on metal — all via noise loudness, never pairings.
Acceptance criteria
- AC-1 integration Given a crouched player moving silently in full light within 3 m, when 10 s pass, then the Hushling does not detect them (sight disabled).
- AC-2 integration Given a noise event of loudness >= 0.85 within 6 m, when propagated, then every Hushling in range dissipates.
- AC-3 integration Given a thrown distraction at 8 m, when heard, then the Hushling investigates the noise location, not the player.
- AC-4 content Given the grasp attack, when validated, then it is unblockable with windup >= 30 ticks and a unique audio cue (edge: readability for a blind hunter).
Waits on
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e11.10 Guards investigate suspicious stimuli at their perceived location open
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.2 Add sense profile data: sight cones, hearing, smell, special senses done
area:contentclass:thiefepic:e13milestone:m3mw-e13.10Add Tallow Ooze that melts into fire puddles and shatters when frozen
Description
Why
The ooze is a systemic toy: the best way to fight it is with the world's properties.
What
As a sorcerer, I want a slow wax slime that reacts to heat and cold so that experimenting pays off.
tallow-ooze: slow, sticky engulf (slows 50%), splits into two small oozes on heavy blunt hits. Heat: melts into a spreading flammable puddle (flammable liquid volume) that can then be ignited. Cold: frozen + fragile → shatters on impact.
Scope
- In: Creature data, split rule, melt-to-puddle and freeze-to-brittle property rules
- Out: Scriptorium placement (e26-priory-scriptorium)
Notes
Puddle cap: max 6 active puddles per room to protect the element-field budget.
Acceptance criteria
- AC-1 integration Given an ooze exposed to heat above its melt threshold for 2 s, when evaluated, then it becomes a flammable puddle entity covering >= 2 m².
- AC-2 integration Given an ooze with frozen = true, when any impact >= its fragile threshold hits it, then it shatters and dies.
- AC-3 integration Given a heavy blunt hit on a full-size ooze, when resolved, then two half-size oozes spawn; half-size oozes do not split again (edge).
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.7 Drive creature vulnerabilities from world properties, not pairings open
area:contentepic:e13milestone:m3mw-e13.11Add book, lectern and chest mimics with tells and feeding
Description
Why
Mimics reward curiosity and observation: a careful player spots the tell; a clever one feeds them.
What
As a player, I want mimics disguised as books, lecterns and chests that I can spot, feed, avoid or wake so that containers stay exciting.
Three variants (book-mimic, lectern-mimic, chest-mimic) presenting as interactables. Tell: subtle breathing motion + faint paper-rustle noise audible within 3 m. Bite on interact (500 ms, grab-class). Feeding: any item tagged edible:paper placed on/near it pacifies and yields a treat item.
Scope
- In: Three variants, disguise-as-interactable hook, tell cues, feed rule, drop tables
- Out: Mortimer the tame mimic (e14-mortimer)
Notes
Waking on purpose (strike first) is valid; mimic then fights normally but flees at 30% health.
Acceptance criteria
- AC-1 integration Given a disguised chest-mimic, when the player interacts, then a 500 ms telegraph plays before the bite and a dodge in that window avoids it.
- AC-2 integration Given an edible:paper item placed within 1 m, when 3 s pass, then the mimic eats it, becomes pacified and drops its treat.
- AC-3 integration Given the player listens within 3 m, when the tell noise plays, then it is captioned by the sound-caption system when captions are enabled (edge).
Waits on
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.13 Declare non-hostile creature interactions (feed, talk, calm) in data open
- mw-e18.3 Add lootable containers that persist their contents done
area:contentepic:e13milestone:m3mw-e13.12Add Hollow Sentinels bound to hold the tower until relieved
Description
Why
The Sentinels embody "keeping watch": honour-bound, relentless inside the tower, and able to be relieved rather than destroyed.
What
As a knight, I want armour that obeys a forty-year-old order so that speaking the old words ends the fight honourably.
hollow-sentinel: slow walker, sight + life-sense, never sleeps; halberd sweep, shield charge (500 ms, unblockable). Leash: never leaves the tower volume. Resistances pierce 0.3, blunt 1.5, conductive. Stand-down conditions (facts): watchword spoken, knight formal relief, "watch ended" bell rung, binding dispelled. Stood-down sentinels salute and fall still permanently.
Scope
- In: Creature data, leash volume, stand-down fact conditions, salute state
- Out: Dialogue lines for watchword and relief (e39-side-quests-wilds)
- Out: Tower placement (e25-mooring-watch-encounters)
Notes
Smashing them is valid but slow (high poise). Dispel uses a generic magic-binding property so any dispel-capable effect works.
Acceptance criteria
- AC-1 integration Given a sentinel in Combat, when the player leaves the tower volume, then it returns to its stand and does not path outside.
- AC-2 integration Given fact watch.ended is set, when evaluated, then all sentinels salute and become inert within 3 s and stay inert after save/reload.
- AC-3 unit Given a pierce hit of 40, when resolved, then 12 damage applies; given a blunt 40, then 60 (edge: armour identity).
- AC-4 integration Given a sentinel standing in water and a shock stimulus, when propagated, then it is staggered via conductivity.
Waits on
- mw-e03.8 Conduct electrical charge through conductive and wet objects done
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.7 Drive creature vulnerabilities from world properties, not pairings open
- mw-e27.5 Query world facts with a shared condition API for all systems done
area:contentepic:e13milestone:m3mw-e13.13Add Brother Horn's creature data: charges, grabs and throws to stop
Description
Why
Horn fights to stop, not to kill — his combat must say so.
What
As a player, I want the minotaur to fight like a warden restraining an intruder so that the fight hints he is not a monster.
npc-horn creature: 9 ft, walker; moves: axe sweep (parryable), charge (unblockable, 600 ms, bellow cue), grab-and-throw (grab-class, 500 ms), stomp (ground AoE). Non-lethal rule: damage from Horn cannot reduce the player below 1 HP; at <= 15% HP his next hit subdues (player.subdued event, not Died), routed to the Fold via e01-contextual-respawn.
Scope
- In: Creature data, move set, non-lethal clamp, subdue event
- Out: Encounter phases, yield and trial (e13-horn-encounter)
- Out: Befriend/help (e14-horn-befriend-help)
Notes
Horn is also a social creature — the same entity is shared with E14.
Acceptance criteria
- AC-1 content Given npc-horn.json, when validated, then the charge and grab moves are unblockable/grab with windups >= 30 ticks and distinct cues.
- AC-2 unit Given the player at 10 HP, when a Horn hit of 50 resolves, then the player ends at 1 HP and no Died event fires.
- AC-3 integration Given the player at <= 15% HP, when Horn's next hit lands, then player.subdued fires and the respawn resolver routes to the Fold.
- AC-4 integration Given environmental damage (a fall) while fighting Horn, when it would kill the player, then normal death applies (edge: clamp only on Horn's own hits).
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
area:contentepic:e13milestone:m3mw-e13.14Build the Warden boss encounter with yield, spare and Trial of Horns
Description
Why
The Warden encounter is the pivotal moment of the game (bible §4.2); combat must feed every resolution.
What
As a player, I want the Horn fight to end in a choice so that combat becomes a story decision.
Phases: guarding (circles, warns), fighting, yielding at 20% HP ("Enough. Hear me, or finish it.") — combat pauses; the player must explicitly attack again to kill (fact horn.killed) or lower weapon/interact to spare (horn.spared, wounded). Trial of Horns mode (knight capability): no-kill honour duel; first to three clean parries/blocks wins (horn.befriended via trial). Portcullis-trapped state and "rests one eye" state as encounter states for trick resolutions.
Scope
- In: Encounter state machine, yield/kill/spare gating, trial rules and counter, trick states exposed as facts
- Out: Horn's Hall level (e26-horns-hall-fold)
- Out: Dialogue (e39-dialogue-horn)
Notes
Killing requires an explicit second action after the yield (e39-main-quest-data note).
Acceptance criteria
- AC-1 integration Given Horn at 20% HP, when the threshold is crossed, then he yields, all his attacks stop and horn.yielded is set.
- AC-2 integration Given a yielded Horn, when the player lands a deliberate attack, then horn.killed is set and his horn item drops; when the player interacts instead, then horn.spared is set.
- AC-3 integration Given the Trial of Horns, when the knight lands 3 clean parries/blocks before taking 3 clean hits, then the trial is won, horn.befriended is set and no damage below 1 HP ever applies.
- AC-4 integration Given the player loses the trial, when resolved, then Horn offers a rematch and the encounter does not become lethal (edge).
Waits on
- mw-e04.12 Parry window and riposte critical attack done
- mw-e13.13 Add Brother Horn's creature data: charges, grabs and throws to stop open
- mw-e27.5 Query world facts with a shared condition API for all systems done
area:contentepic:e13milestone:m3mw-e13.15Add the Sleepless Abbot wraith: solid only while singing
Description
Why
The abbot must be beatable by understanding him — sound and light break his concentration — not by damage race.
What
As a player, I want a wraith who is only vulnerable while singing and whose form is anchored to lanterns so that I defeat him with observation.
npc-abbot creature: fog body; states singing (solid, damageable, commands Hushlings) and drifting (immune). Loud noise (>= 0.85) or bright light (>= 0.9) within 5 m interrupts singing and staggers. Anchor lanterns: 4 linked lantern entities; each destroyed/extinguished removes 25% of his max health. Summons Hushlings while singing (cap 4).
Scope
- In: Creature data, sing/drift state rules, anchor link component, summon cap
- Out: Cradle encounter and resolutions (e13-abbot-encounter)
Notes
Interrupt rules by stimulus threshold only — bell, Thunderclap, shattered glass, Wisp Light all qualify systemically.
Acceptance criteria
- AC-1 integration Given the abbot drifting, when any damage packet hits, then 0 damage applies and immune feedback shows.
- AC-2 integration Given the abbot singing, when a noise >= 0.85 occurs within 5 m, then singing is interrupted, he is staggered for 2 s and becomes damageable during it.
- AC-3 integration Given two of four anchor lanterns extinguished, when his max health is read, then it is 50% of base.
- AC-4 integration Given the Hushling cap of 4 is reached, when he sings again, then no further Hushlings spawn (edge).
Waits on
- mw-e03.16 Extinguish and relight torches, candles and braziers systemically done
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
area:contentepic:e13milestone:m3mw-e13.16Build the Cradle encounter with fight, trick, persuade and join paths
Description
Why
The final confrontation must honour every class and the persuade path earned through Clatter.
What
As a player, I want to end the abbot by fighting, tricking, persuading or joining him so that my choices and class matter at the climax.
Fight: via e13-abbot-wraith rules. Trick: pickpocket the tongue from his belt while singing (thief; sets tongue.recovered); a Mirror Self decoy emitting hymn-tagged noise turns nearby Forgotten on him; shooting anchor lanterns. Persuade: available only if clatter.restored and Clatter present. Join: dialogue choice → ending 4 state. Tansy-hostage variant if tansy.reachedCradle.
Scope
- In: Encounter state machine, trick hooks (pickpocket target, faction flip on hymn), hostage variant, outcome facts
- Out: Cradle level (e26-cradle-arena)
- Out: Abbot dialogue (e39-dialogue-abbot)
Notes
Pickpocketing the tongue is "the single most satisfying theft in the game" — give it a 1.5 s pickpocket window during each sing phase.
Acceptance criteria
- AC-1 integration Given the abbot singing and the thief behind him, when pickpocket succeeds, then tongue.recovered is set and the abbot enters a fury phase.
- AC-2 integration Given a hymn-tagged noise from any source during the fight, when Forgotten are within 10 m, then they switch faction target to the abbot for 20 s.
- AC-3 integration Given clatter.restored is false, when the persuade option is evaluated, then it is not offered; given true with Clatter present, then it is.
- AC-4 integration Given the Tansy-hostage variant, when an area attack would hit Tansy, then she is protected (no damage) and the abbot moves her (edge).
Waits on
- mw-e08.15 Add Mirror Self: an illusory double that draws attention open
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e12.8 Faction table and relationship queries between creatures done
- mw-e13.15 Add the Sleepless Abbot wraith: solid only while singing open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
area:contentepic:e13milestone:m3mw-e13.17Author the MVP creature faction and relationship table
Description
Why
Creatures have relationships; setting them against each other is a valid solution.
What
As a designer, I want a faction table for the MVP roster so that goblins fight spiders, Hushlings ignore goblins and players can exploit it.
Factions: forgotten, rootcellar, warden, loom, briar-pack, dream (Hushlings + abbot), sentinels, mimic, ooze. Relationships: rootcellar ↔ loom hostile; dream ignores rootcellar; briar-pack avoids mine volumes; forgotten neutral to dream until intensity 3; warden protective of sleepers.
Scope
- In: Faction data, relationship matrix, content tests
- Out: Faction runtime (e12-factions-relationships)
Notes
Player standing with factions is disposition (E14/E22), not this table.
Acceptance criteria
- AC-1 content Given the faction table, when validated, then every MVP creature references exactly one existing faction.
- AC-2 integration Given a goblin and a loom spider in mutual sight, when perceived, then they enter Combat with each other.
- AC-3 integration Given a Hushling and a goblin within 2 m, when evaluated, then neither targets the other (edge).
Waits on
- mw-e12.8 Faction table and relationship queries between creatures done
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
area:contentepic:e13milestone:m3mw-e13.18Gate CI on creature readability, avoidability and arena smoke tests
Description
Why
Readable telegraphs and non-combat options are constitutional; they must not rot as creatures are tuned.
What
A CI audit over all shipping creature data: every attack passes the readability rule; every creature declares >= 1 avoid option and has a scenario test proving it; every declared vulnerability has a property-based test; a 12-creature mixed Ossuary scene runs in the perf suite.
Scope
- In: Audit script and report (docs/design/bestiary-audit.md), scenario tests, perf scene
- Out: Framework-level harness (e12-creature-content-harness)
Notes
Reuse e32-combat-stress-scene infrastructure for the perf scene.
Acceptance criteria
- AC-1 integration Given a creature move with a 12-tick windup, when the audit runs, then CI fails naming the creature and move.
- AC-2 integration Given a creature with no avoid-option scenario test, when the audit runs, then CI fails (edge).
- AC-3 perf Given 8 Forgotten, 2 Hushlings and 2 loom spiders active with fire, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) and p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat.
Waits on
- mw-e12.15 Auto-generate smoke tests for every creature data file done
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e32.13 Add combat stress scene with 12 creatures and heavy VFX open
area:qaepic:e13milestone:m3
M4 Fun validation 1
mw-e13.19Tune the bestiary from creature-arena sessions and playtest telemetry
Description
Why
Creatures should be memorable for behaviour; tuning must be evidence-led.
What
Run creature-arena sessions per class against every MVP creature and read playtest telemetry (deaths, abandon points). Adjust timings, health, perception within design bounds; record changes in docs/design/bestiary-tuning.md.
Scope
- In: Arena sessions, telemetry review, data tuning PR, tuning log
- Out: Framework changes (file beads in E11/E12)
Notes
Related: e34-analysis-scripts, e35-external-round-1.
Acceptance criteria
- AC-1 manual Given one tester per class, when each fights every creature once, then median time-to-kill for regular creatures is between 15 s and 60 s.
- AC-2 integration Given the tuned data, when the bestiary audit runs, then it still passes (tuning never breaks readability).
- AC-3 content Given the tuning log, when reviewed, then each change cites the observation or telemetry metric that motivated it.
Waits on
- mw-e13.18 Gate CI on creature readability, avoidability and arena smoke tests open
- mw-e33.12 Create a creature arena to spawn and pit creatures open
- mw-e34.3 Emit core gameplay events: deaths, encounters, spells, puzzles open
area:qaepic:e13milestone:m4
E14Social CreaturesThe Rootcellar goblins (Mother Kettleback, Snig), Clatter the skeleton who remembers, Brother Horn and Mortimer the book mimic can be talked to, fed, gifted, calmed, befriended and helped — not only fought — using e22 dialogue/disposition/gifts and e12 dispositions/affordances, with outcomes persisted as world facts.0/8 done · 0%
M3 MVP content 8
mw-e14.1Let the player hum the Vesper hymn after reading the Vesperine Hours
Description
Why
Humming the hymn calms the Forgotten, befriends Horn and rewards reading — a verb every class can learn from a book.
What
As a player of any class, I want to learn the Vesper hymn from The Vesperine Hours and hum it so that I can calm the dead and speak to the warden.
Reading the hymn page grants capability verb.hum.vesper-hymn. Hum is a held action: emits a noise event every 0.5 s tagged hymn (loudness 0.3), movement slowed to walk. Creatures declare responses in data (calmedBy: hymn).
Scope
- In: Capability grant on read, hum action and animation hook, hymn-tagged noise emission
- Out: The book item and shop placement (e24-lamplit-stacks)
- Out: Creature responses (E13/E14 creature items)
Notes
Mirror Self can emit hymn-tagged noise via the same tag (Cradle trick). Humming while sneaking still makes noise — a real trade-off.
Acceptance criteria
- AC-1 integration Given the player reads the hymn page, when complete, then verb.hum.vesper-hymn is granted with source book:vesperine-hours.
- AC-2 unit Given the player humming for 2 s, when noise events are inspected, then 4 events tagged hymn with loudness 0.3 are emitted.
- AC-3 integration Given the player without the capability, when the hum action is pressed, then nothing is emitted and no prompt shows (edge).
Waits on
- mw-e07.9 Read books in a page-turning reader with a study action open
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e12.13 Declare non-hostile creature interactions (feed, talk, calm) in data open
- mw-e19.2 Create capability registry with source-tracked grants done
area:gameclass:allepic:e14milestone:m3mw-e14.2Make the Rootcellar clan talkable, bribable and forgiving of capture
Description
Why
"A goblin camp might actually have people to talk to." Getting caught should open negotiation, not end the game.
What
As a player, I want to approach the goblin camp as a guest, a trader or a trespasser so that my approach changes how the clan treats me.
Clan disposition (faction-level) with modifiers: gifts of shiny/kettle items, returning salvage, harming goblins, freeing Duggan by force. Guests: goblins stay Wary, trade allowed. Capture: when the player is netted/subdued in the camp, they are dragged to Kettleback (fact goblins.capturedPlayer) and dialogue opens regardless of prior hostility. Thieves get a kin bonus.
Scope
- In: Clan disposition rules, gift preferences, capture-to-negotiation flow, kin bonus
- Out: Kettleback and Snig specifics (e14-mother-kettleback, e14-snig)
- Out: Dialogue text (e39-dialogue-labyrinth-folk)
Notes
Clan standing is one of the ending facts (goblin standing).
Acceptance criteria
- AC-1 unit Given clan disposition Neutral and a gift tagged kettle, when given, then disposition rises by the gift preference value and a goblins.gifted fact increments.
- AC-2 integration Given the player is subdued inside the camp volume, when the subdue resolves, then the player wakes in front of Kettleback and her dialogue starts.
- AC-3 integration Given the player killed >= 3 goblins, when entering the camp, then goblins start Alerted but capture still leads to negotiation (edge).
- AC-4 unit Given the thief class, when clan disposition is initialised, then it starts one band higher than for other classes.
Waits on
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e22.12 Let players give gifts and bribes that shift disposition open
- mw-e22.4 Track NPC disposition with bands, event modifiers and factions done
- mw-e22.9 Start conversations with NPCs in-world and handle interruptions done
area:contentepic:e14milestone:m3mw-e14.3Bring Mother Kettleback to life: trade, Duggan crate and carrying fees
Description
Why
Kettleback is a memorable monster-character: pragmatic, sarcastic and useful.
What
As a player, I want to bargain with the goblin matriarch so that I can trade, free Duggan Marl and hire her clan.
npc-kettleback: creature + merchant (goblin salvage stock), gift likes (kettles, brass, shiny), Duggan crate interaction states (trade for Dot's kettle, persuade to release, force), clan hire effects (wall the breach, hold the shaft), and the carrying-fee rule when she retrieves a dead player (post-Horn-death).
Scope
- In: Creature/merchant data, gift table, Duggan crate state entity, hire effects as facts, carrying fee rule
- Out: Framed in Brass quest graph (e39-side-quests-wilds)
Notes
Kettleback never fights unless the camp is attacked; she flees to the back tunnel at 50% health.
Acceptance criteria
- AC-1 content Given npc-kettleback.json and her merchant data, when validated, then both pass and stock references existing items.
- AC-2 integration Given the player gives Dot's kettle, when accepted, then duggan.released is set and the crate opens.
- AC-3 integration Given clan disposition Friendly and a hire request, when accepted, then goblins.hired.breach is set for the breach-sealing option.
- AC-4 integration Given the player attacks Kettleback, when her health reaches 50%, then she flees via the back tunnel and the clan becomes Hostile (edge).
Waits on
- mw-e14.2 Make the Rootcellar clan talkable, bribable and forgiving of capture open
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.8 Write dialogue for Clatter, Mother Kettleback and Snig done
area:contentepic:e14milestone:m3mw-e14.5Build Clatter: the apologetic skeleton who can remember his name
Description
Why
"A skeleton might remember fragments of its previous life." Clatter is the warmest quest in the game.
What
As a player, I want a skeleton that apologises while fighting, calms to the hymn and regains memories so that helping him feels like the heart of the story.
npc-clatter: Forgotten variant; hostile-apologetic (reduced aggression, apology barks) unless hymn heard; tends mushroom garden idle. Memory pieces as facts (psalter, pear-bloom, sister's letter); song intensity 3 removes the most recent unanchored memory. Sorcerer memory-reading: success skips a piece; failure removes one permanently. Outcomes: clatter.restored → follower to Cradle; or rest (knight rites / psalter on priory altar).
Scope
- In: Creature data, memory facts and loss rule, reading attempt check, follower/rest outcomes
- Out: Name on the Bones quest graph (e39-side-quests-wilds)
- Out: Clatter dialogue (e39-dialogue-labyrinth-folk)
Notes
Clatter must never be killable by accident while calm: friendly-fire off when disposition >= Friendly.
Acceptance criteria
- AC-1 integration Given Clatter hostile, when a hymn-tagged noise reaches him, then he stops attacking and opens dialogue.
- AC-2 unit Given 2 memories recovered and song intensity rising to 3, when evaluated, then the most recent memory not yet anchored is removed and the fact is cleared.
- AC-3 unit Given a failed memory-reading attempt, when resolved, then one memory is permanently lost and cannot be recovered by its piece (edge).
- AC-4 integration Given clatter.restored, when the player enters the Cradle, then Clatter follows and is present for the persuade option.
Waits on
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e14.1 Let the player hum the Vesper hymn after reading the Vesperine Hours open
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:contentepic:e14milestone:m3mw-e14.6Befriend and help Brother Horn without fighting
Description
Why
"A minotaur guarding an ancient labyrinth might be fought, tricked, befriended or helped."
What
As a player, I want to lower my weapon, hum, return his mother's horn-ring or have friends vouch so that Horn becomes my ally.
Befriend triggers: player sheathed and stationary for 6 s in Horn's sight (he circles three times, then speaks); hymn heard; horn-ring or the Hornfolk name-stone (from The Crate for Mr. S—, e40-crate-heist) given; vouch (snig.lessons or clatter present). Help steps: bread for sleepers, healing (salve, Nature spell, rest in Fold), breach sealed. Ally mode: follows to key points, opens the crypt shortcut, holds the shaft foot in the climb.
Scope
- In: Befriend trigger rules, help-step facts, ally behaviours (open crypt, hold shaft)
- Out: Horn dialogue (e39-dialogue-horn)
- Out: Combat encounter (e13-horn-encounter)
Notes
Befriend is combinable with spared/tricked outcomes (bible §4.2).
Acceptance criteria
- AC-1 integration Given the player sheathed and stationary for 6 s in Horn's sight, when evaluated, then Horn circles the player 3 times and opens dialogue instead of attacking.
- AC-2 integration Given the player draws a weapon during the circling, when detected, then the befriend attempt aborts and Horn resumes guarding (edge).
- AC-3 integration Given horn.befriended and the player at the crypt door, when Horn is asked, then the crypt shortcut opens and shortcut.crypt is set permanently.
- AC-4 integration Given horn.befriended and horn.healed, when the climb begins, then Horn holds the shaft foot and Hushlings target him first.
Waits on
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
- mw-e13.13 Add Brother Horn's creature data: charges, grabs and throws to stop open
- mw-e14.1 Let the player hum the Vesper hymn after reading the Vesperine Hours open
- mw-e22.12 Let players give gifts and bribes that shift disposition open
- mw-e39.7 Write Brother Horn's dialogue across all Warden resolutions done
area:contentepic:e14milestone:m3mw-e14.8Prove every social creature path in an automated scenario suite
Description
Why
Social paths are easy to break silently; every one must stay reachable.
What
Scenario tests (e11-ai-scenario-harness + quest/dialogue test runners) for each P1 path: goblins guest/trade/capture; Kettleback Duggan trade/persuade/force; Clatter hymn/memories/loss/restore/rest; Horn wait/hymn/ring/vouch/trial/help. The suite is a registry: Snig and Mortimer (P2) register their own scenarios in their items.
Scope
- In: Scenario suite, CI job, coverage report of social paths
- Out: Quest-level validation (e23-quest-validator)
Notes
Report lists each path id and its outcome fact. Related: e14-snig, e14-mortimer add scenarios to this registry when they land.
Acceptance criteria
- AC-1 integration Given the suite, when run in CI, then every path listed in docs/design/bestiary.md social options reaches its outcome fact.
- AC-2 integration Given a path whose outcome fact is never set, when the suite runs, then CI fails naming the path (edge).
- AC-3 integration Given each social failure path (capture, ban, lost memory), when run, then the resulting state offers at least one further action (no dead end).
Waits on
- mw-e11.3 Scenario harness for scripted AI tests with asserted states over time done
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
- mw-e14.6 Befriend and help Brother Horn without fighting open
area:qaepic:e14milestone:m3mw-e14.4Bring Snig to life: pocket-picking, reading lessons and vouching
Description
Why
Snig is the game's comic heart and the goblin who wants to read.
What
As a player, I want Snig to steal from me, follow me and vouch for me so that he becomes a recurring ally.
npc-snig: attempts to pickpocket the player (thief can notice and allow — snig.trusted); Bookshop Burglar outcomes as facts (jailed, lessons, scared of the reading one); follower mode in town and mine; vouch flag used by Horn befriend; fear of Mortimer (flees if within 5 m).
Scope
- In: Creature data, pickpocket-the-player behaviour, follower mode, outcome facts
- Out: Bookshop Burglar quest graph (e39-side-quests-town)
Notes
If jailed, the thief can bust him out through the jail drain (e24-watch-house-jail).
Acceptance criteria
- AC-1 integration Given Snig adjacent and the player idle, when his pickpocket attempt succeeds, then one low-value item moves to his inventory and the thief class receives a notice prompt.
- AC-2 integration Given snig.lessons is set, when Horn's befriend conditions are evaluated, then the vouch option is available.
- AC-3 integration Given Mortimer within 5 m of Snig, when evaluated, then Snig flees and follower mode pauses until distance > 10 m (edge).
Waits on
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e14.2 Make the Rootcellar clan talkable, bribable and forgiving of capture open
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:contentclass:thiefepic:e14milestone:m3mw-e14.7Add Mortimer, the tame book mimic who guards the rare shelf
Description
Why
Mortimer makes the bookshop memorable and makes shoplifting a story rather than a menu.
What
As a player, I want Mirela's tame mimic to bite shoplifters and be won over with paper so that the bookshop has a guardian with personality.
npc-mortimer: stationary mimic on the rare shelf; detects witnessed theft from the shop (e27-ownership-theft) within 6 m and bites (non-lethal); sets mirela.banned for 1 in-game day; bringing biscuits to Mirela lifts it early; can be fed paper for a treat (Mortimer's tooth rare drop if provoked).
Scope
- In: Creature data, theft-detect-and-bite rule, ban fact with timeout, feed rule
- Out: Bookshop stock (e24-lamplit-stacks)
- Out: Snig-meets-Mortimer beat (e39-side-quests-town)
Notes
Mortimer never leaves his shelf; theft from other shelves out of his view is not detected by him.
Acceptance criteria
- AC-1 integration Given the player takes an owned book within Mortimer's view, when theft is recorded, then Mortimer bites for non-lethal damage and mirela.banned is set.
- AC-2 unit Given mirela.banned set at day 3 08:00, when world time reaches day 4 08:00, then the ban clears.
- AC-3 integration Given a theft outside Mortimer's view, when recorded, then Mortimer does not react (edge).
Waits on
- mw-e13.11 Add book, lectern and chest mimics with tells and feeding open
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e27.9 Track object ownership and record thefts as world facts done
area:contentepic:e14milestone:m3
E15Puzzle FrameworkDesigners compose systemic puzzles from reusable world objects and data — not custom code. A puzzle is a data file naming world entities, a goal condition over live properties and world facts, declared solutions per capability set, and reset/hint policies. Tooling proves each declared solution is reachable, detects softlocks, and reports which classes can solve each puzzle, so "several solutions" is verified rather than hoped for.12/33 done · 36%
M2 Class fantasies 19
mw-e15.14Let solved puzzles grant item and loot rewards
Description
Why
mw-e15.1 puzzle outputs can only write facts (solvedFact, set). Rewards such as an item or a loot table on solve have no field yet because items are not content.
What
As a designer, I want to declare a puzzle's item or loot reward in its file so that solving it grants the reward exactly once without a quest wrapper.
Scope
- In: outputs.rewards referencing item/loot content ids, checked at load; runtime grant once on solve
- Out: item schema (mw-e17.2), puzzle runtime (mw-e15.3)
Acceptance criteria
- AC-1 content Given a puzzle reward naming an unknown item id, when validated, then validation fails naming the id.
- AC-2 unit Given a one-shot puzzle with an item reward, when solved, saved and reloaded, then the item was granted exactly once.
Waits on
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
- mw-e17.2 Define item definition schema and content validator done
area:contentarea:simarea:toolsepic:e15milestone:m2size:smw-e15.18Model signal graphs, plates and facts in the puzzle solver's abstract state
Description
mw-e15.6 left goals that read a signal, fact or count of facts (and the cold, water and charge elements) inconclusive in the abstract layer (src/tools/puzzle/abstract.ts, unmodelled()). Add switch, plate and door state and signal-graph propagation so those goals are searched, and add rules for cold/water/charge so fewer solutions rely on the concrete run alone (abstraction gaps). Acceptance: a puzzle whose goal is a signal/door driven by a plate and crate is proven reachable abstractly with its minimum action count; unit tests for each new rule.
area:simarea:toolsepic:e15milestone:m2size:mmw-e15.21Puzzle reset volume and reset lever prefab
Description
mw-e15.7 shipped reset recovery triggered by using a listed entity (lever). Add the reset volume trigger (walking into a volume resets, as the bead's 'reset lever/volume' says) and a reset lever prefab composed from existing components, plus a demo in prefab-gallery. Reset currently restores listed doors and switches only; consider signal-graph latches, plates and liquid drains.
area:simarea:toolsepic:e15milestone:m2mw-e15.22Softlock search: model stuck mechanisms and alternate routes
Description
mw-e15.7 softlock search covers irreversible losses of listed entities (burn, break, fall, melt) in the abstract lit/place state. Extend it to mechanism states (a door jammed or a latch thrown that cannot be undone, signal-graph latches) and honour a declared alternate route or a reversible-only flag as a covering recovery. Also: a critical object that falls while its puzzle is still dormant is not returned (the runtime only marks critical entities once a puzzle is in play, to keep dormant worlds hashing as before); revisit if designers hit it.
area:simarea:toolsepic:e15milestone:m2mw-e15.26Timed door: decide whether the window includes the door's close time
Description
mw-e15.5 (#396) AC-3: with an 8 s window the door's signal goes off at 8 s, but the sliding door takes 1.5 s to close, so a player passing at 8.1 s can still slip through. Owner decision: should a timed window mean 'shut by N s' (start closing at N minus close time, or close faster), or is 'starts closing at N s' fine? Adjust plate-timed-door prefab and its test accordingly.
area:simarea:toolsepic:e15milestone:m2mw-e15.33Draw puzzle hint barks and highlight pulses in the HUD
Description
The sim now emits puzzleHint (mw-e15.11) with a class-aware bark, the spawn ids and entities to pulse, and a rumour fact. Today the game only publishes it as #app[data-puzzle-hint] (hintReadout in src/game/puzzles/index.ts, wired in src/main.ts onHint). Needs: a bark line (subtitle style, accessible, respects text size) and a glint/outline pulse on the highlighted entities. Styling is mw-e31 scope. AC: firing a hint shows its bark for a few seconds and pulses each entity; hints off shows nothing.
area:simarea:toolsarea:uiepic:e15epic:e31milestone:m2milestone:m3mw-e15.15Liquid layer entities outlive a scene unload
Description
Scene spawns can now be liquid volumes (mw-e15.4). A volume's layer entities are separate entities from the spawn, so unloading the scene (unloadScene destroys the spawn entity) leaves its liquid layers behind in the world. Destroy a volume's layers when its LiquidVolume component is removed (world.onRemove in installLiquids), with a test that unloads a scene with a basin.
area:simarea:toolsepic:e15milestone:m2mw-e15.16Puzzle prefab e2e and gallery screenshot
Description
mw-e15.4 shipped 15 prefabs and ?scene=prefab-gallery with integration tests only. Add a Playwright smoke that loads the gallery (CI software GL ~1 fps: pace on state, 30 s polls) and a screenshot for the docs; add a measured weight to e2e/shards.json.
area:simarea:toolsepic:e15milestone:m2mw-e15.17Prefab goals: let puzzle goals read a prefab's outputs by name
Description
A puzzle goal reads a prefab output today as signal "<instance>/<node>" or the entity <instance>-<local>. Consider a checked reference ("prefab:<instance>.<output>") so a renamed instance or output fails at load, and the solver (mw-e15.6) can list them.
area:simarea:toolsepic:e15milestone:m2mw-e15.19Give the testbed-room-lever puzzle a live goal the solver can confirm
Description
pnpm puzzle:validate reports all three solutions of testbed-room-lever as diverged in the concrete run: its goal reads a stand-in fact (testbed.room-lever.thrown) that nothing sets (the lever prop has no live switch state). It is not mvp so CI passes, but the report is noisy. Make the goal read the lever's live state (mw-e15.2 leaves) or give it a real switch, then confirm by-hand, mage-hand and crate-shove with the solver. Acceptance: pnpm puzzle:validate testbed-room-lever exits 0 with every solution OK.
area:simarea:toolsepic:e15milestone:m2size:smw-e15.20Path-aware concrete steps for the puzzle solver (walk, push and carry instead of teleport)
Description
mw-e15.6's concrete layer (src/tools/puzzle/concrete.ts) teleports the player beside a reach target and a moved thing beside its destination, so it cannot catch a puzzle whose route is blocked in the real level. Drive the player controller along the navmesh and push/carry through the interaction path, with timeouts reported as the step index that stalled. Acceptance: a fixture level with a wall across the route makes the solution diverge at the reach step; brazier-hall still passes.
area:simarea:toolsepic:e15milestone:m2size:mmw-e15.23Respawn chute: visible and audible replacement
Description
mw-e15.7 respawner drops a copy at the spawn point when due; there is no chute prop, drop animation or sound, and the player is not told a copy is coming. Add a chute prefab and cue (puzzleRecovered kind respawn) so recovery is diegetic and legible, and a telemetry sink for puzzleRecovered.
area:simarea:toolsepic:e15milestone:m2mw-e15.24Sequence validator tolerance: timing window and near-miss leniency for the sequence node
Description
mw-e15.5 shipped the ordered mechanism with the existing strict sequence node (any out-of-order edge fails and restarts). The bead text also says sequence validators 'with tolerance': add an optional per-node tolerance (for example a maximum gap between steps after which the run restarts, and/or a number of forgiven wrong steps) as a general signal-node option, plus prefab parameters on bell-sequence and rune-tile-sequence.
area:simarea:toolsepic:e15milestone:m2mw-e15.25Wire the book reader to readClue so reading a bound tome records its puzzle clue
Description
mw-e15.5 added item book.clueFor, the clueRead event and readClue(world, actor, item, puzzles) in src/game/puzzles/clues.ts (attachBookClues returns read(actor,item)); scroll reads are already wired. When the book reader UI (mw-e07.9) opens a page, call BookClues.read so tomes and notes count too. Also consider emitting it on StudyEnded completed.
area:simarea:toolsepic:e15milestone:m2mw-e15.27Scene inventory tests hard-code every scene id (scene-loader.test.ts, scene.test.ts), so any new scene edits src/*.test.ts
Description
Found building mw-e15.9 (data-only reference puzzles): src/game/scene/scene-loader.test.ts and src/content/types/scene.test.ts list every scene by name, so adding a scene file forces edits to TypeScript under src/. Derive the expectation from the content directory (or assert membership) so content-only PRs touch no src/*.ts.
area:simarea:toolsepic:e15milestone:m2mw-e15.28Concrete puzzle solver teleports the player (reach, move player), so traversal puzzles such as the frozen channel validate without proving the crossing
Description
Found in mw-e15.9: src/tools/puzzle/concrete.ts reach/move put the player beside the target without pathing, and a cold stimulus step on a liquid volume spawn (no placement) is not checked for effect. The frozen-channel freeze solution is therefore 'confirmed' even if nothing froze; the real proof is tests/integration/reference-puzzles.test.ts, which walks the player over the ice. Add walking/pathing or an expected-effect check for cold (ice thickness) and weight steps.
area:simarea:toolsepic:e15milestone:m2mw-e15.30Puzzle validator abstraction does not model the facing predicate: Watchers reports abstract and softlock inconclusive
Description
Found in mw-e15.10: the Watchers goal reads 'facing north' on rotatable statues, which the abstract search does not model, so every solution is abstract-inconclusive and the softlock search cannot prove a destroyed statue leaves the puzzle solvable (only the concrete run and an integration test do). Add facing (quarter-turn state per rotatable entity, rotate and break actions) to the abstraction and softlock search.
area:simarea:toolsepic:e15milestone:m2mw-e15.31Puzzle solver cannot strike impact switches: bells cannot be rung by an arrow or spell step, so a balcony-arrow solution cannot be declared
Description
Found in mw-e15.10: concrete.ts applies stimuli by contact, and a contact stimulus is never an impact (src/sim/mechanisms/impacts.ts), so a 'shoot the bells in order' step diverges. Chimes declares crate-lob and Mage Hand routes instead and tests the arrow feed with deliverImpact. Let a stimulus/strike step deliver an impact (damage type and energy) at the target, and let the abstraction ring bells in sequence.
area:simarea:toolsepic:e15milestone:m2mw-e15.32Chimes: author the keeper's ledger readable (book.clueFor chimes) so the find-it-in-a-book solution reads the clue
Description
Found in mw-e15.10: the Chimes by-the-book solution is modelled as skipping the button because no lore book item exists (a book needs cover art ui-book-cover-<id>, legality and rarity data, which that data-only bead could not add) and the reader is not wired (mw-e15.25). Add the ledger item with clueFor ['chimes'], place it in the scene, and change the solution to read it so clue.<item> lands on the trail.
area:contentarea:simarea:toolsepic:e15milestone:m2
M3 MVP content 2
mw-e15.12Inspect puzzle goals, wiring and solver results in an in-game tool
Description
Why
Designers iterate fastest when they can see why a puzzle is not solving (developer tooling pillar).
What
Debug panel listing puzzles in the level with lifecycle state, live goal explain() tree, wiring graph highlight, attribution trail, and buttons to reset, force-solve, and run the solver for the current capability set.
Scope
- In: Panel in e33 debug console; goal tree view; reset/force-solve/solve commands
- Out: Standalone level editor — out of scope
Notes
Force-solve must set facts exactly as a real solve would (for save testing).
Acceptance criteria
- AC-1 integration Given a level with a puzzle, when the inspector opens, then it lists the puzzle with current state and goal explain tree.
- AC-2 integration Given force-solve, when executed, then the solved fact and outputs match a real solve (compared via fact store diff).
- AC-3 integration Given the solver button on a puzzle whose search is inconclusive, when run, then the panel shows inconclusive without freezing the game for > 100 ms (runs in a worker) (edge).
Waits on
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
- mw-e15.6 Prove declared puzzle solutions reachable with a headless solver done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e15milestone:m3size:smw-e15.13Emit puzzle discovery, attempt, solve and abandon telemetry events
Description
Why
E34 wants puzzle completion signals; undeclared solutions are gold for designers.
What
Emit e34 telemetry events: puzzleDiscovered, puzzleAttemptFailed, puzzleHintShown{tier}, puzzleSolved{solutionId|undeclared, durationS, class}, puzzleAbandoned (left region > 10 min unsolved after discovery).
Scope
- In: Event schema entries + emission from runtime
- Out: Dashboards/export — see e34-telemetry-events
Notes
Local-first; no network by default.
Acceptance criteria
- AC-1 unit Given a puzzle solved via an undeclared route, when solved, then puzzleSolved carries solutionId=undeclared and the capability trail.
- AC-2 unit Given discovery then leaving the region for 10 min unsolved, when the tick runs, then puzzleAbandoned fires once.
- AC-3 unit Given telemetry disabled, when events occur, then nothing is recorded (edge).
Waits on
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:gameepic:e15milestone:m3size:s
E16Class-Specific SolutionsEvery mandatory obstacle in the MVP has deliberate routes for knight, archer, sorcerer and thief (at least three of four, target four), documented in a route matrix, declared as data, proved by replays and enforced by a CI audit.1/7 done · 14%
M3 MVP content 5
mw-e16.2Declare obstacles and class routes as validated content data
Description
Why
Routes must be data so they can be audited and replayed, not tribal knowledge.
What
Schema content/obstacles/*.json: {id, scene, mandatory, routes: [{id, classes, requiredCapabilities, entities (ids of the anchor points, weak walls, vents…), replay?}]} with a validator that checks scene/entity/capability references.
Scope
- In: Zod schema, validator, entries for every matrix obstacle
- Out: Audit gate (e16-route-audit)
Notes
Entities referenced must carry the property that makes the route work (e.g. climbable, weakened, shootable) — the validator checks the property exists.
Acceptance criteria
- AC-1 content Given an obstacle entry, when validated, then every referenced scene, entity and capability id exists.
- AC-2 content Given a route referencing a weak wall entity lacking the weakened property, when validated, then validation fails (edge).
- AC-3 content Given the registry, when compared to the route matrix, then every matrix obstacle id has an entry.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e15.1 Define the puzzle data schema with goals, solutions and policies done
- mw-e16.1 Write the MVP obstacle × class route matrix done
- mw-e19.2 Create capability registry with source-tracked grants done
area:contentepic:e16milestone:m3mw-e16.3Fail CI when a mandatory obstacle lacks three class routes
Description
Why
"Problems support multiple solutions" must be enforced, not hoped for.
What
CI audit reusing e15-solution-audit class capability profiles: for each obstacle, evaluate each route against each class's MVP capability profile; fail if a mandatory obstacle has < 3 classes with a valid route. Generates docs/design/route-coverage.md and a PR diff summary.
Scope
- In: Audit script, CI gate, generated report
- Out: Puzzle audits (e15-solution-audit)
Notes
Shares profile data with e15; do not duplicate profiles.
Acceptance criteria
- AC-1 integration Given a mandatory obstacle with valid routes for only knight and thief, when CI runs, then it fails naming the obstacle and the missing classes.
- AC-2 integration Given a route requiring a capability absent from every class profile, when audited, then the route is reported invalid (edge).
- AC-3 integration Given all obstacles pass, when CI runs, then the report is regenerated and the PR summary lists coverage deltas versus main.
Waits on
- mw-e15.8 Report and enforce per-class solution coverage for puzzles in CI done
- mw-e16.2 Declare obstacles and class routes as validated content data open
area:qaepic:e16milestone:m3mw-e16.4Replay every declared class route to prove it is traversable
Description
Why
A declared route that does not actually work is worse than none.
What
Each route in the registry gets a short recorded replay (from a start marker to the obstacle's exit fact) run headlessly in CI; failing replays mark the route invalid in the audit.
Scope
- In: Replays per route, runner, audit integration
- Out: Full critical-path replays (e26-dungeon-playtest)
Notes
Replays start from committed save fixtures near each obstacle to stay short. Related: e33-teleport-bookmarks (P2) can generate those fixtures faster if it lands.
Acceptance criteria
- AC-1 integration Given each declared route, when its replay runs, then the obstacle exit fact is set.
- AC-2 integration Given a replay that fails, when the audit runs, then that route no longer counts toward coverage.
- AC-3 integration Given level geometry changes that break a replay, when CI runs, then the failure names the obstacle, route and first diverging checkpoint (edge).
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e16.3 Fail CI when a mandatory obstacle lacks three class routes open
- mw-e30.1 Define versioned save format with section registry and migrations done
area:qaepic:e16milestone:m3mw-e16.5Close class-route gaps in Briar Glen, the wilds and Wendmouth
Description
Why
The audit will find gaps; closing them is authoring work with a clear owner.
What
As a player of any class, I want every surface obstacle to have my class's route so that I never feel my class was forgotten.
For every E24/E25/E40 obstacle flagged by the audit: add anchor points, weak walls, climbable ivy, vents, shootable counterweights or spell affordances until >= 3 (target 4) classes pass.
Scope
- In: Level data edits in town and wilderness scenes, registry updates, replays
- Out: Dungeon gaps (e16-gap-pass-dungeon)
Notes
Prefer adding a route over widening an existing one.
Acceptance criteria
- AC-1 integration Given the audit after this pass, when run, then every E24/E25/E40 mandatory obstacle passes with >= 3 classes.
- AC-2 integration Given each newly added route, when replayed, then it passes.
- AC-3 perf Given the added props, when the town and Briarwood perf scenes run, then budgets from the hardware baseline still hold (edge: no perf regression).
Waits on
- mw-e16.4 Replay every declared class route to prove it is traversable open
- mw-e24.17 Author class routes for Briar Glen's obstacles open
- mw-e25.17 Author class routes and ability-gated shortcuts across the wilderness open
- mw-e40.21 Declare Wendmouth obstacles and class routes in the registry open
area:contentclass:allepic:e16milestone:m3mw-e16.6Close class-route gaps in the Deepworks, priory and the Knot
Description
Why
The dungeon is the constitution's showcase: "the knight forces, the thief infiltrates, the archer manipulates, the sorcerer alters."
What
As a player of any class, I want every dungeon obstacle to have my class's route so that the Labyrinth plays differently for me.
For every E26 obstacle flagged by the audit: add routes until >= 3 (target 4) classes pass, preferring routes that express the class fantasy lines in the constitution MVP scenario.
Scope
- In: Dungeon level data edits, registry updates, replays
- Out: Puzzle solutions (E15 audit)
Notes
Target: all 4 classes for the mine gate, Horn's Hall and the Cradle.
Acceptance criteria
- AC-1 integration Given the audit after this pass, when run, then every E26 mandatory obstacle passes with >= 3 classes and the mine gate, Horn's Hall and Cradle pass with 4.
- AC-2 integration Given each newly added route, when replayed, then it passes.
- AC-3 integration Given a route that consumes a one-shot resource (e.g. breaking the only weak wall), when used, then the obstacle stays passable for the remaining classes (edge).
Waits on
- mw-e16.4 Replay every declared class route to prove it is traversable open
- mw-e26.21 Author first-pass class routes through every dungeon area open
area:contentclass:allepic:e16milestone:m3
M4 Fun validation 1
mw-e16.7Playtest the MVP critical path once per class for route fantasy
Description
Why
Only players can tell whether a route feels like their class or like a checkbox.
What
One tester per class plays the MVP critical path; observers log each obstacle, the route taken and whether the tester found their class route unprompted. Findings to docs/playtests/class-routes.md with beads.
Scope
- In: 4 sessions, observation sheet, findings, beads
- Out: External playtests (e35-external-round-1)
Notes
Run with builds from e35-playtest-builds.
Acceptance criteria
- AC-1 manual Given each class tester, when they finish, then they found their class route unprompted at >= 60% of mandatory obstacles.
- AC-2 manual Given each tester, when surveyed, then they name >= 3 "wait, I can do that?" moments.
- AC-3 content Given the findings doc, when complete, then every obstacle where the class route was missed has a bead or an explicit "accepted" note (edge).
Waits on
- mw-e16.5 Close class-route gaps in Briar Glen, the wilds and Wendmouth open
- mw-e16.6 Close class-route gaps in the Deepworks, priory and the Knot open
- mw-e35.3 Deploy playtest builds to GitHub Pages with build IDs open
area:qaclass:allepic:e16milestone:m4
E17Inventory & EquipmentWeapons, armor, books, keys, consumables, quest objects and unusual artifacts are manageable without inventory busywork. Equipment changes what the player can *do* (new verbs, altered stealth/noise, light, traversal), not only numbers. All rules live in `src/sim` with data in `src/content`.13/19 done · 68%
M1 Grey-box slice 3
mw-e17.16Grant item capabilities while carried through the capability registry
Description
Why
Tools such as the climbing hook declare grants with
while: carried(ADR-0004), but nothing grants them yet: mw-e17.3's inventory model holds items without touching capabilities (discovered while implementing mw-e17.3).What
When the first unit of a definition with
while: carriedgrants enters an actor's inventory, grant each capability through CapabilityRegistry (src/sim/progression/capabilities.ts) with sourcetool:<defId>; revoke when its last unit leaves. Driven by item.added / item.removed or inside InventoryRules.Acceptance criteria
- AC-1 unit Given an actor without verb.climb.rough, when a climbing hook is added, then the actor has it from source tool:<id> and capability.gained is emitted once.
- AC-2 unit Given two hooks carried, when one is removed, then the capability stays; when the second is removed, capability.lost is emitted.
- AC-3 unit Given an inventory with carried grants, when saved and loaded, then capability sources and inventory agree and the state hash matches.
Waits on
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
area:simepic:e17milestone:m1mw-e17.17Bind the four quick slots to default keys and pad buttons
Description
Why
mw-e17.6 shipped four quick slots (sim: Consumables.useSlot, useQuickSlotCommand) but no buttons use them: keys 1-4 and the d-pad are already ability1-4 (docs/design/controls.md), so binding them needs an owner layout decision.
What
Pick the default keyboard and pad bindings for quick slots 1-4 (e.g. a modifier + 1-4, F1-F4, or d-pad with abilities moved), add quickSlot1-4 actions to the ActionFrame registry, and have the consumables system read them (consumablesSystem input option).
Scope
- In: actions, default bindings, remapping conflicts, controls doc
- Out: quick-slot HUD (mw-e17.10)
Acceptance criteria
- AC-1 unit Pressing the quick slot 1 binding uses quick slot 1 (item.used with slot 0).
- AC-2 unit The default bindings have no conflicts in the gameplay context.
area:gamearea:simepic:e17milestone:m1mw-e17.19Bind render models for objects spawned after scene load (recovered quest objects)
Description
Why
mw-e17.18 (PR #308) respawns a destroyed quest-critical object as an intact copy at its anchor, but the game's render binding is made only at scene load, so the copy exists in the sim with no visible model.
What
Have the render layer bind models for physics/world objects created after scene load (listen for spawns or object.recovered), reusing the original's model id.
Acceptance criteria
AC-1 e2e Given an anchored quest-critical breakable in a test scene, when it is broken, then a visible model appears at the anchor.
area:simepic:e17milestone:m1
M2 Class fantasies 3
mw-e17.9Let equipment grant verbs and systemic properties
Description
Why
Zelda-style meaningful items: equipment should unlock verbs (a lantern makes light, a grappling hook enables anchors, soft boots quiet footsteps), not just add stats.
What
As a player, I want gear that changes what I can do so that finding a new item feels like finding a new ability.
Equipped items with
grantsregister capabilities as sourceequipment:<instanceId>in the capability registry; unequipping revokes them. Items may also emit properties:lightRadius(feeds e03-light-field),noiseModifier,carriesFire, etc. Interaction prompts query capabilities so new options appear on world objects.Scope
- In: Grant/revoke on equip change, property emitters, prompt refresh event
- In: Three grey-box sample items: lantern (light), soft boots (noise), climbing hook (anchor verb)
- Out: The verbs' mechanics themselves (owned by their epics, e.g. climbing in mw-e10)
Notes
Same registry as progression so dialogue/quests/puzzles check "can the player do X" regardless of source.
Acceptance criteria
- AC-1 unit Given an item granting
verb.anchor-climb, when equipped, thenhasCapability("verb.anchor-climb")is true; when unequipped, it is false. - AC-2 unit Given the player already has a capability from progression and equips an item granting the same one, when the item is unequipped, then the capability remains (source-counted).
- AC-3 integration Given an equipped lantern, when the light field is sampled at the player position, then light level increases by the item's
lightRadiuscontribution. - AC-4 integration Given soft boots equipped, when the player walks on stone, then emitted footstep noise is reduced by the item's
noiseModifier. - AC-5 unit Given an item granting an unknown capability at runtime (content bypass), when equipped, then equip succeeds, the grant is skipped and an error event is logged.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
- mw-e19.2 Create capability registry with source-tracked grants done
area:simepic:e17milestone:m2mw-e17.11Add equipment paper-doll view with verb and stealth comparison
Description
Why
Equipment choices should be legible as trade-offs in verbs, noise and light, not just damage numbers.
What
As a player, I want to see what I'm wearing and how swapping gear changes my noise, light and available verbs so that I can dress for the job (stealthy thief vs. armored knight).
Paper-doll with slot silhouettes, compare panel showing deltas for noise, weight class, light, granted/lost verbs, and key combat values. Non-proficiency penalties shown clearly.
Scope
- In: Equipment view, compare panel, slot filters, gamepad support
- Out: 3D character preview rendering (optional, render group later)
Notes
UI assets assumed: slot silhouette icons (head, body, hands, feet, main, off, trinket, ammo, tool), comparison up/down indicators.
Acceptance criteria
- AC-1 integration Given heavy armor equipped and soft leather hovered, when comparing, then the panel shows noise decrease and weight class change using derived EquipmentState values.
- AC-2 integration Given an item that grants a verb, when hovered, then the compare panel lists the verb under "Gains"; unequipping an item lists its verbs under "Loses".
- AC-3 integration Given a sorcerer hovering plate armor, when compared, then the non-proficiency penalty is shown with a warning style.
- AC-4 unit Given an empty slot, when rendered, then its silhouette placeholder is displayed and is focusable for gamepad navigation.
Waits on
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.9 Let equipment grant verbs and systemic properties open
area:uiepic:e17milestone:m2mw-e17.13Apply armor load class to speed, sprint, jump, climbing and armor swaps
Description
Why
ADR-0003 makes armor the only weight in the game: the heavy knight should be slower, tire sooner and lose rope and ivy routes, while the thief in leather keeps every verb.
mw-e04.16covers rolls, regen, poise and noise; nothing yet applies the load class to running, sprinting, jumping, climbing or armor swaps.What
As a knight, I want my armor's load class to change how I move and climb so that putting on plate is a real choice, not a bigger number.
From the derived
EquipmentState.loadClass(mw-e17.4), apply the ADR-0003 table: run/sprint/crouch speed multipliers (Medium ×0.95, Heavy ×0.9, Overloaded walk only ≤ 2.4 m/s), sprint drain (×1.15 / ×1.3 / no sprint), climb drain (×1.25 / ×1.5 / ×2.0), jump height (×0.95 / ×0.85 / ×0.7), climbing allowed surfaces (Heavy and Overloaded: ladders only, others refused "Too heavy to climb"). Armor and shield equip changes are refused while in Combat (reasonin-combat) or while climbing, hanging or carrying (reasonbusy); weapons, ammo and tools still swap.Scope
- In: Load class multipliers in controller, stamina and climb tuning as content data; climb surface gate; armor-swap refusal rules
- Out: Roll profiles, regen, poise and noise (
mw-e04.16); pull-up refusal (mw-e02.34); footstep noise (mw-e09.5)
Notes
Numbers and the table live in
docs/adr/0003-encumbrance-model.md. Load ratio = equipped armor kg / class armor capacity (knight 30, others 23).Acceptance criteria
- AC-1 unit Given a Heavy load class, when the player runs and sprints, then horizontal speed is 4.5 m/s and 6.75 m/s ± 0.01 and sprint drains stamina at 13/s.
- AC-2 unit Given an Overloaded load class, when sprint is held, then the player stays at walk speed ≤ 2.4 m/s and no stamina is drained for sprinting.
- AC-3 unit Given a Heavy load class, when the player walks into ivy or a rope, then the grab is refused with reason too-heavy; a ladder grab succeeds and drains climb stamina at 7.5/s.
- AC-4 unit Given a Medium load class, when the player jumps, then apex height is 0.95 × the Light apex height ± 1%.
- AC-5 unit Given a creature in Combat with the player, when body armor is equipped, then it fails with reason in-combat and equipment is unchanged; a weapon swap in the same state succeeds.
- AC-6 unit Given the player is climbing, when a shield is unequipped, then it fails with reason busy (edge).
- AC-7 content Given the load class tuning data, when validated, then every load class defines every multiplier and all multipliers are in (0, 1].
Waits on
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
area:simclass:allepic:e17milestone:m2
E18Loot & TreasureExploration produces interesting, handcrafted discoveries rather than endless statistically interchangeable equipment. Loot is data (tables + placements), deterministic under seeded RNG, persistent in world state, and aware of ownership so theft is a real, systemic act.4/10 done · 40%
M2 Class fantasies 5
mw-e18.5Support designer-placed and hidden treasure with discovery rules
Description
Why
The world rewards curiosity: wells hide tunnels, loose bricks hide stashes. Handcrafted placement beats random drops.
What
As a designer, I want to place specific items in the level and optionally hide them behind discovery conditions so that curious players find hand-authored rewards.
Placement data:
{id, itemOrTable, transform, hidden?: {revealWhen: property-based conditions}}. Reveal conditions are systemic: light level above threshold (e03-light-field), capability present (e.g.sense.detect-magic), object moved/broken (e03 events). Looted placements persist.Scope
- In: Placement schema, reveal condition evaluation, persistence, greybox examples: stash under a movable crate, item only visible in light
- Out: Hidden passages / secret doors (mw-e03 / mw-e15)
Acceptance criteria
- AC-1 unit Given a hidden placement with
revealWhen: lightLevel >= 0.6, when light at its position rises to 0.7, then it becomes interactable and atreasure.revealedevent fires once. - AC-2 unit Given a placement hidden under a movable object, when the object is moved off its footprint, then the placement is revealed.
- AC-3 unit Given a looted placement, when the level reloads, then the placement does not respawn.
- AC-4 unit Given a reveal condition referencing an unknown capability, when content is validated, then validation fails.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e19.2 Create capability registry with source-tracked grants done
area:simepic:e18milestone:m2mw-e18.6Track item ownership and flag stolen items on witnessed theft
Description
Why
Stealing must be a real systemic act with consequences (thief fantasy, Elder Scrolls living world) and feed shops, disposition and AI.
What
As a thief, I want taking things that belong to others to be tracked and only punished when someone notices so that careful theft pays off and sloppy theft creates stories.
Ownership on containers, placements and world items (
ownerIdNPC or faction). Taking an owned item marks the instancestolenwith owner. At the moment of taking, witnesses are evaluated via e11 perception/e09 visibility: witnessed theft emitscrime.theft(value, owner, witnesses) consumed by e11-alert-states and disposition; unwitnessed theft emits nothing but the item remains flagged. Owner discovers missing high-value items later (optional factowner.noticedMissing).Scope
- In: Ownership data, stolen flag assignment, witness check, crime event, returning stolen items clears the flag
- Out: Guard responses/bounty (mw-e11)
- Out: Fence pricing (e20-fences-stolen-goods)
- Out: Pickpocket minigame (mw-e10)
Acceptance criteria
- AC-1 unit Given an owned item in a container and no witness able to see the player, when taken, then the instance is stolen with the ownerId and no
crime.theftevent is emitted. - AC-2 unit Given an NPC with the player in sight cone and visibility ≥ its threshold, when an owned item is taken, then
crime.theftis emitted listing that NPC as witness. - AC-3 unit Given a stolen item given back to its owner (via dialogue effect or putting it back in the owner's container), when returned, then the stolen flag clears.
- AC-4 unit Given an unowned item, when taken in full view, then no stolen flag or crime event occurs.
- AC-5 integration Given the greybox guard watching a chest, when the player takes an item, then the guard enters at least the Suspicious alert state.
Waits on
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e17.7 Pick up, drop and throw items as physical world objects done
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e27.9 Track object ownership and record thefts as world facts done
area:simclass:thiefepic:e18milestone:m2mw-e18.7Drop what creatures actually carry, plus data-driven extras
Description
Why
Creatures are not target dummies or loot piñatas; what they carry should be visible and plausible (the goblin with the lantern drops the lantern).
What
As a player, I want defeated or robbed creatures to hold the gear I saw them using so that loot feels grounded.
Creature schema extension:
carriedinventory (equipped items shown on the creature) + optionaldropTablefor pouch contents. On death the corpse becomes a container; on pickpocket (mw-e10) the carried inventory is the source. Wolves drop nothing but pelts-as-quest-materials only if data says so.Scope
- In: Carried inventory on creatures, corpse-as-container, owned-by-creature flags (robbing a living goblin is theft)
- Out: Pickpocket mechanics (mw-e10)
- Out: Harvesting/crafting (not planned for MVP)
Acceptance criteria
- AC-1 unit Given a creature with carried [rusty-sword, lantern], when killed, then its corpse container holds exactly those plus the rolled drop table results.
- AC-2 unit Given a creature with no carried items and no drop table, when killed, then no corpse container is created (no empty loot prompt).
- AC-3 unit Given a living intelligent creature, when an item is taken from it, then the item is flagged stolen with the creature as owner.
- AC-4 content Given every creature data file, when validated, then all carried/dropTable references resolve.
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e18.3 Add lootable containers that persist their contents done
area:simepic:e18milestone:m2mw-e18.8Add unique artifacts with active verbs and lore
Description
Why
Handcrafted discoveries with verbs are the reward that replaces stat-sticks; artifacts should make players say "wait, I can do that?"
What
As a player, I want rare artifacts that do strange, useful things so that finding one changes how I play.
Artifact = item with
flags.uniqueand either passivegrantsor anactivateblock (cooldown/charges, effects via the shared effect vocabulary and world properties). Examples for grey-box tests only: a compass that points to the nearest hidden placement, a bell that emits a loud noise event at a distance, a cold stone that givesfrozento touched water. Uniqueness enforced world-wide.Scope
- In: Activate verb, charges/cooldown, uniqueness guarantee, lore text field, 3 grey-box test artifacts
- Out: The MVP artifact roster (world-content group)
Notes
Artifacts must express effects via properties (e03) — never artifact-specific code paths.
Acceptance criteria
- AC-1 unit Given an artifact with 3 charges and recharge-on-rest, when activated 4 times without rest, then the 4th activation fails with reason
no-charges. - AC-2 unit Given a unique artifact already obtained, when any table or placement tries to grant another, then no second instance is created.
- AC-3 integration Given the bell test artifact activated, when resolved, then a noise event with the configured loudness is emitted at the target point and nearby guards react via e11.
- AC-4 integration Given the cold-stone test artifact touching a
wet/water volume, when resolved, then the volume gainsfrozenvia e03 propagation. - AC-5 content Given all artifact data, when validated, then each has a non-empty lore text key and at least one grant or activate effect.
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e17.9 Let equipment grant verbs and systemic properties open
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:simepic:e18milestone:m2mw-e18.9Write treasure design guide for handcrafted loot
Description
Why
World-content authors need a shared rule set so loot stays meaningful across the town, wilderness and labyrinth.
What
docs/design/treasure.md: treasure tiers (junk-with-jokes, useful consumables, tools, books, artifacts), placement density guidelines ("interesting-looking places usually contain something"), ratio of handcrafted vs table-rolled, gold budget per area (links to economy doc), ownership conventions, humour guidelines for flavour text.
Scope
- In: Guide doc with examples and anti-patterns
- Out: Actual placements (world-content group)
Acceptance criteria
- AC-1 manual Given the guide, when reviewed, then it defines each treasure tier with one example and an anti-pattern.
- AC-2 manual Given the guide, when reviewed, then it states numeric density targets (e.g. discoveries per room/area) and the handcrafted:rolled ratio.
- AC-3 manual Given the guide, when reviewed, then it references the economy gold budget from e20-economy-design.
Waits on
- mw-e20.1 Write economy design: gold income, meaningful sinks, price bands done
area:designepic:e18milestone:m2
M3 MVP content 1
mw-e18.10Build treasure audit and loot distribution report tool
Description
Why
Designers need to see what a region yields before playtesting it, and to catch gold inflation or missing books.
What
CLI
pnpm content:loot-report [region]: enumerates placements, containers and creature drops; simulates each table N times (seeded) and outputs expected gold, item counts by category, unique artifact locations, and per-class availability of progression items (books, tools, schematics). Debug console commandloot.roll <table> [n].Scope
- In: CLI report (markdown + JSON), console command
- Out: Economy balance CI gate (e20-economy-balance-tool)
Acceptance criteria
- AC-1 unit Given a fixture region with 2 containers and known tables, when the report runs with seed 1 and N=1000, then expected gold equals the analytic expectation within ±1%.
- AC-2 unit Given a region where a class-gated progression item has no source, when the report runs, then it lists that item under "Unobtainable".
- AC-3 unit Given
loot.roll unknown-table, when executed in the console, then it reports an error and suggests close matches.
Waits on
- mw-e18.2 Validate loot tables: references, uniques, cycles, weights done
- mw-e18.7 Drop what creatures actually carry, plus data-driven extras open
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e18milestone:m3
E19ProgressionCharacter advancement primarily unlocks capabilities (verbs), specialization and new play styles — never Firebolt I → II (+12%). The player picks a class at the start; each class earns new verbs through its own fiction: books (sorcerer), techniques from trainers (knight), arrows and trick-shot techniques (archer), tools and tricks (thief). Supporting stats are minimal and subordinate.6/19 done · 32%
M1 Grey-box slice 1
mw-e19.6Implement minimal supporting stats and rare stat growth sources
Description
Why
Stats can exist but must support rather than define progression (constitution §3); keep them few and legible.
What
As a player, I want a small set of stats (e.g. health, stamina, focus) that grow from rare discoveries so that finding a heart-container-like relic feels special without turning into grind.
Stats per ADR (≤ 5), with base from class, modifiers from equipment and temporary effects, and permanent increments from data-defined sources (relic items, shrines, deeds). Hard caps. No per-kill XP.
Scope
- In: Stat model with base/modifier/permanent layers, caps, sources, events, save section
- Out: Combat consumption of stamina/poise (mw-e04)
- Out: Mana/focus spending (mw-e06)
Acceptance criteria
- AC-1 unit Given base health 100 and a relic granting +10 permanent max health, when consumed, then max health is 110 and current health increases by 10.
- AC-2 unit Given max health at its cap, when another permanent increment is applied, then it is clamped and the source is not consumed (returns
at-cap). - AC-3 unit Given an equipment modifier of -10 stamina, when unequipped, then stamina max returns to its prior value.
- AC-4 unit Given a creature is killed, when resolved, then no stat changes occur (no per-kill XP).
Waits on
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
area:simepic:e19milestone:m1
M2 Class fantasies 12
mw-e19.7Wire spellbook learning into progression prerequisites
Description
Why
Magic is learned primarily from books (constitution §4). Book learning must respect progression prerequisites and class rules defined in the ADR.
What
As a sorcerer, I want reading a spellbook to teach me its spell when I understand the prerequisites so that books become exciting and school knowledge matters.
Adapter from e07-spellbook-learning to
progression.learn(spell, channel: book). Handles: missing prerequisite (book stays readable, shows what knowledge is missing), non-sorcerer readers per ADR cross-learning rule, damaged/untranslated books gated by conditions.Scope
- In: Adapter, failure explanations, cross-class rule enforcement
- Out: Book reading UI and spell data (mw-e07, mw-e08)
- Out: Bookshop inventories (e21-bookshop-system)
Acceptance criteria
- AC-1 unit Given a sorcerer reading a book for a spell whose prerequisites are met, when read, then the spell capability is granted with source
learnedand channelbook. - AC-2 unit Given a knight reading a sorcerer-only spellbook, when read, then no capability is granted and the result explains per ADR (e.g. "The runes swim before your eyes.").
- AC-3 unit Given a book whose spell is already known, when read, then no duplicate grant occurs and the book is flagged
read. - AC-4 integration Given the greybox testbed, when a sorcerer picks up and reads a test spellbook, then the new spell appears in the capabilities list and can be cast.
Waits on
- mw-e07.2 Learn spells by reading and studying book items done
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:simclass:sorcererepic:e19milestone:m2mw-e19.8Let trainers teach knight techniques through dialogue services
Description
Why
Knights master techniques; learning them from a character (for gold, a deed, or a duel) is more memorable than a skill point.
What
As a knight, I want to learn new techniques from trainers who demand payment, proof or a test so that each new move has a story.
Trainer = NPC with
teaches: [{capability, price?, requires: conditions, test?: questId}]. Dialogue effectlearnCapability(via e22 DSL) and a service listing in the trainer's dialogue. Supports technique chains (shield bash → shield charge → shield vault) and non-gold prices (a favour quest).Scope
- In: Trainer data schema, dialogue effect binding, gold deduction via shop transactions, test-quest gating
- Out: Technique mechanics (mw-e04)
- Out: Specific trainer NPCs (world-content group)
Acceptance criteria
- AC-1 unit Given a trainer teaching
technique.shield-chargefor 50 gold and a knight with 60 gold and the prerequisite, when learned, then gold is 10 and the capability is granted with channeltrainer. - AC-2 unit Given insufficient gold, when the learn effect runs, then it fails atomically (no gold removed, no capability).
- AC-3 unit Given a technique gated by a test quest not yet resolved, when offered in dialogue, then the option is shown as locked with its requirement text.
- AC-4 content Given all trainer data, when validated, then every taught capability exists and every test quest id resolves.
Waits on
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:simclass:knightepic:e19milestone:m2mw-e19.9Unlock archer trick arrows via schematics and trick-shot techniques
Description
Why
Archers control space through specialized arrows and trick shots; new arrow types are new verbs.
What
As an archer, I want to find fletching schematics and learn trick shots so that my quiver gains new ways to solve problems (rope arrows, water arrows, noise arrows).
Fletching schematic item →
learn(arrow.<type>, channel: schematic); once known, arrows of that type can be assembled at rest points from simple materials (count-limited, no crafting tree). Trick-shot techniques (e.g. ricochet, pinning) learned from deeds or trainers via the shared channels.Scope
- In: Schematic item handling, simple arrow assembly (materials → arrows), trick-shot unlock via deed/trainer
- Out: Arrow physics and effects (mw-e05)
- Out: General crafting system (not MVP)
Acceptance criteria
- AC-1 unit Given an archer reading a rope-arrow schematic, when read, then
arrow.ropeis learned with channelschematicand the schematic is consumed. - AC-2 unit Given
arrow.ropeknown and 2 rope + 2 shafts, when assembling at a rest point, then 2 rope arrows are added and materials removed. - AC-3 unit Given
arrow.ropenot known, when assembling, then the recipe is unavailable even with materials. - AC-4 unit Given a non-archer reading a schematic, when read, then behaviour follows the ADR cross-learning rule and is covered by a test.
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:simclass:archerepic:e19milestone:m2mw-e19.10Grant thief verbs from tools and tricks learned from contacts
Description
Why
Thief growth is about tools and tricks (lockpicks, grapples, smoke, disguises); owning a tool is owning a verb.
What
As a thief, I want new tools and tricks to open new routes so that my arsenal grows in options, not numbers.
Tools are items with
grants(via e17-equipment-verbs on the toolBelt, or passive when carried per ADR). Tricks (e.g.trick.feign-death,trick.shadow-step) are learned from notes/contacts via the shared learn API with channeltrick. Tool degradation per ADR (e.g. lockpicks can break — failure creates stories).Scope
- In: Tool-carried capability grants, trick learning channel, optional breakage rule hook
- Out: Tool mechanics (mw-e10)
Acceptance criteria
- AC-1 unit Given lockpicks on the tool belt, when checked, then
tool.lockpickis granted; removing the lockpicks revokes it. - AC-2 unit Given a trick note read by a thief, when read, then the trick is learned with channel
trick. - AC-3 unit Given a breakable tool with 1 durability, when a breaking event occurs, then the tool is removed and its capability revoked with a
tool.brokeevent. - AC-4 unit Given two lockpick stacks, when one breaks, then the capability remains while any stack remains.
Waits on
- mw-e17.9 Let equipment grant verbs and systemic properties open
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:simclass:thiefepic:e19milestone:m2mw-e19.12Build capabilities screen and "new verb learned" feedback
Description
Why
Players must notice and remember new verbs, otherwise capability progression is invisible.
What
As a player, I want a screen listing what I can do, grouped by source, with hints about what I might learn next, and a clear moment when I learn something new so that I try my new verbs.
Screen: capabilities grouped (Class, Learned, From Gear, Temporary), each with icon, description and input hint. "Rumoured" section shows known-but-unlearned capabilities from rumors/books glimpsed. Learn moment: banner with verb name, one-line usage hint, and input glyph; optional short practice prompt.
Scope
- In: Capabilities screen, learn banner, input glyph binding, rumoured hints
- Out: Spellbook-specific UI (mw-e07)
Notes
UI assets assumed: capability/verb icons per class (placeholder shapes first), learn banner frame, input glyph set (KB/M + gamepad).
Acceptance criteria
- AC-1 integration Given capabilities from class, learning and an equipped lantern, when the screen opens, then each appears under the correct group with its icon.
- AC-2 integration Given
capability.gainedfrom a learn action, when fired, then the learn banner appears within 1 frame and shows the currently bound input glyph for that verb. - AC-3 integration Given the player remaps the verb's input, when the screen reopens, then the new glyph is shown.
- AC-4 unit Given a temporary capability expires while the screen is open, when it expires, then it is removed from the list without closing the screen.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e17.9 Let equipment grant verbs and systemic properties open
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:uiepic:e19milestone:m2mw-e19.15Validate progression content against the ADR-0004 anti-grind rules
Description
Why
ADR-0004 defines eight content rules (P1–P8) that keep progression about verbs.
mw-e19.3AC-6 covers numeric-only unlocks and cycles andmw-e07.8covers spell chains; the rest (narrowsupporting, one-time stat sources, cross-class bounds, no exclusive unlocks, reachability, no XP fields) have no validator yet, so content could quietly reintroduce grind.What
As a designer, I want
pnpm content:checkto reject progression data that breaks the anti-grind rule so that a bad unlock fails in CI with the rule id, entry and file.Implement rules P2 and P4–P8 from
docs/adr/0004-progression-model.mdand generalize P3 (each chain step is a new verb) from spells to techniques, arrows, tools and tricks. Every message names the rule id, the entry id and the file.Scope
- In: Validator rules over capability, unlock, trainer, schematic, tool, trick, deed, stat-source and class data; fixtures for each rule
- Out: The P1 numeric-only rule and cycle check already in
mw-e19.3; spell chain check inmw-e07.8; runtime learn API (mw-e19.16)
Notes
MVP stat budget is 8 shrines and 6 boon items; stat steps are exactly +10; caps 200 (ADR-0004 stats table).
Acceptance criteria
- AC-1 content Given a stat source entry granting +15 max health, when validated, then validation fails with rule P5 naming the entry and file.
- AC-2 content Given an unlock flagged supporting: true that targets spell.firebolt, when validated, then validation fails with rule P2.
- AC-3 content Given a technique chain step whose move, ops and stimuli equal its predecessor's and differ only in damage, when validated, then validation fails with rule P3.
- AC-4 content Given a crossClass: true unlock with a prerequisite, or one listed in its class's signature capabilities, when validated, then validation fails with rule P6.
- AC-5 content Given an unlock declaring excludes, or a replacing unlock with no subsumes, when validated, then validation fails with rule P7.
- AC-6 content Given a capability that no book, trainer, schematic, tool, trick, deed, item or class grants, when validated, then validation fails with rule P8 naming the capability.
- AC-7 content Given any content entry with an xp, level or skillPoints field, when validated, then validation fails with rule P4.
Waits on
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
area:contentepic:e19milestone:m2mw-e19.16Enforce the cross-class simple-capability rule in the learn API
Description
Why
ADR-0004 allows a knight to read an easy spellbook and a sorcerer to learn a basic shield bash, but only for *simple* capabilities. The rule must live once in the shared learn API, not be re-implemented in each channel (
mw-e19.7–mw-e19.10each say "per the ADR cross-learning rule").What
As a knight, I want to learn an easy spell from a book I found so that my curiosity is rewarded, while the sorcerer's deeper chains stay hers.
learn(capId, channel)checks, for a learner whose class differs from the capability's affinity: the capability is a chain root, its unlock setscrossClass: true, it is within the channel bound (book: difficulty ≤ 0.3, mana cost ≤ 20, licensed school; trainer: stamina cost ≤ 25, no proficiency; schematic: shop-sold materials, any bow; trick: no tool or trick required; tool: grant flagged crossClass) and it is not in the home class's signature list. A refusal returns reasonoff-classwith an in-fiction explanation key, and the source (book, schematic, note) is not consumed.Scope
- In: Cross-class check in the learn API, refusal reasons and text keys, class
signaturelist in class data - Out: Content validation of crossClass flags (
mw-e19.15); channel adapters' own tests (mw-e19.7–mw-e19.10)
Notes
See
docs/adr/0004-progression-model.md, "Cross-class learning: the simple rule".Acceptance criteria
- AC-1 unit Given a knight and a crossClass root spell with difficulty 0.2 and mana cost 15, when learned from a book, then the capability is granted with channel book.
- AC-2 unit Given a knight and spell.flame-jet (has prerequisites), when learned from a book, then learning fails with reason off-class and the book is not consumed.
- AC-3 unit Given a sorcerer and the knight's signature technique.parry, when offered by a trainer, then learning fails with reason off-class and no gold is deducted.
- AC-4 unit Given a thief and a crossClass schematic whose arrow needs shop-sold materials, when read, then the arrow is learned with channel schematic.
- AC-5 unit Given an off-class learner and an unlock without crossClass: true that otherwise meets every bound, when learned, then it fails with reason off-class (edge).
Waits on
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
area:simclass:allepic:e19milestone:m2- In: Cross-class check in the learn API, refusal reasons and text keys, class
mw-e19.17Offer a one-time health, stamina or mana boon choice at shrines
Description
Why
ADR-0004 grows health, stamina and mana only from one-time shrines and found boon items.
mw-e19.6implements the stat layers and fixed-stat sources; nothing yet offers the shrine's choice, which is the player's only say in how their supporting stats grow.What
As a player, I want to find a shrine and choose whether it strengthens my health, stamina or mana so that a rare discovery fits how I play.
A shrine is a world entity with
once: true. Interacting opens a choice of +10 health, +10 stamina or +10 mana; a stat at cap is shown but unavailable. Choosing applies the step throughmw-e19.6, records a world-state fact so the shrine stays spent across save and load, and emitsstat.boon. A shrine is a discovery, not a rest point.Scope
- In: Shrine component and schema, choice flow on the minimal UI, spent fact, events, a grey-box shrine in the testbed
- Out: Stat model and boon items (
mw-e19.6); shrine art and story names (mw-e37,mw-e39); world placement (content epics)
Notes
MVP budget: 8 shrines. Story names must not reuse "relic" (Vane's shipments).
Acceptance criteria
- AC-1 unit Given an unspent shrine and max stamina 100, when the player chooses stamina, then max stamina is 110, current stamina rises by 10 and stat.boon is emitted once.
- AC-2 unit Given a spent shrine, when the player interacts, then no choice is offered and stats are unchanged.
- AC-3 unit Given max health at its cap of 200, when the shrine choice opens, then health is listed as unavailable and cannot be chosen (edge).
- AC-4 unit Given all three stats at cap, when the player interacts, then the shrine is not spent and reports at-cap.
- AC-5 integration Given a spent shrine, when the game is saved and loaded, then the shrine is still spent.
- AC-6 e2e Given the grey-box testbed shrine, when the player chooses mana, then the mana HUD maximum increases by 10 with no console errors.
Waits on
- mw-e19.6 Implement minimal supporting stats and rare stat growth sources open
area:simclass:allepic:e19milestone:m2mw-e19.18hasCapability must honour replacing unlocks (subsumed capabilities stay usable)
Description
Why
ADR-0004 says a replacing unlock must subsume the capability it replaces (Telekinesis still pulls levers like Mage Hand). PR #143 (mw-e19.3) revokes the replaced capability's learned source and only UnlockBook.knows follows the replacement chain; hasCapability used by puzzles, traversal and other checks does not. Authoring any replacement chain today would break checks on the replaced id.
What
Make the registry's capability query treat a capability as held when a held capability replaces it (transitively), or keep the replaced grant; one source of truth for every caller.
Acceptance criteria
- AC-1 unit Given spell.telekinesis learned as a replacement of spell.mage-hand, when a puzzle checks hasCapability(actor, spell.mage-hand), then it returns true.
- AC-2 unit Given a replacement chain A→B→C with only C held, when any caller queries A, then it is held.
area:simepic:e19milestone:m2mw-e19.11Unlock capabilities from notable deeds via data-driven rules
Description
Why
Some verbs are best earned by doing something memorable (befriending the minotaur, surviving a fall into the mine), rewarding curiosity instead of repetition.
What
As a player, I want remarkable actions to teach me something so that the world itself trains me.
Deed rules:
{when: event/fact conditions, grants: capability, once: true, message}evaluated on world-state changes. Explicitly not repetition counters (anti-grind): validator rejects deed rules whose condition is a count > 3 of the same event unless flagged.Scope
- In: Deed rule schema, evaluator on world-state/event bus, validator anti-grind rule, notification event
- Out: Specific deeds (world-content group)
Acceptance criteria
- AC-1 unit Given a deed rule on fact
minotaur.befriended, when the fact becomes true, then the capability is learned with channeldeedexactly once. - AC-2 unit Given the fact toggles false then true again, when evaluated, then the deed does not grant twice.
- AC-3 unit Given a deed rule requiring 20 kills of the same creature type without
allowRepetition, when validated, then validation fails citing the anti-grind rule.
Waits on
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simepic:e19milestone:m2mw-e19.14Check interaction affordance capability requirements against the capability registry
Description
Why
Scene affordances (src/content/types/interaction.ts) declare requires: [{ capability }] as a plain kebab content id, while puzzles (mw-e15.1) name capabilities by registry id (spell.mage-hand) checked against src/content/data/capability/. Two spellings of the same idea means a gated lever can name a capability nothing grants.
What
As a designer, I want affordance capability requirements to use the capability id format and be checked against the registry at load so that a typo fails in CI rather than greying out a prompt forever.
Scope
- In: interaction requirement uses capabilityId; load check against the capability registry with file and pointer
- Out: the runtime capability registry (mw-e19.2)
Acceptance criteria
- AC-1 content Given a scene spawn whose affordance requires an undeclared capability id, when validated, then validation fails naming the id, file and pointer.
- AC-2 content Given an affordance requirement that is not in capability id format, when validated, then validation fails.
Waits on
- mw-e19.2 Create capability registry with source-tracked grants done
area:contentarea:simepic:e19milestone:m2size:smw-e19.19Let shrines and boons raise max mana (mana profile.max growth API)
Description
From PR #267 (mw-e06.3): mana profile.max is fixed at install; ADR-0004 stat growth (shrines, mw-e19.17 boons) needs an API to raise it deterministically and persist it.
Acceptance criteria
- AC-1 a shrine/boon raises max mana; current mana handling documented; survives save/load
area:simepic:e19milestone:m2
E20Economy & ShopsGold has meaningful uses and merchants have identities, inventories and specialties (Quest for Glory merchants with personality; Elder Scrolls living shops). Players can buy, sell, haggle and steal, and prices respond to disposition, specialty and stolen goods.6/12 done · 50%
M2 Class fantasies 2
mw-e20.6Support fences and merchant reactions to stolen goods
Description
Why
Stolen goods need an outlet and consequences; offering a merchant their own stolen silver should be memorable (and funny).
What
As a thief, I want to sell stolen goods to a fence at a discount, and face consequences if I try to sell them to their owner so that theft is a complete loop.
Fences buy stolen items at the economy-doc discount. Non-fences refuse stolen items; if the merchant (or their faction) is the owner, emit
crime.theft.recognized→ disposition drop and optional guard alert. Fences can "launder" at a fee (clear stolen flag) if the design doc enables it.Scope
- In: Fence rules, owner recognition, disposition/crime events, optional laundering
- Out: Guard arrest/bounty behaviour (mw-e11)
Acceptance criteria
- AC-1 unit Given a stolen item worth 100 and a fence with discount 0.4, when sold, then the player receives the price model result for a fence (breakdown includes stolen discount).
- AC-2 unit Given a merchant who owns a stolen item the player tries to sell, when attempted, then the sale is refused, disposition drops by the configured amount and
crime.theft.recognizedis emitted. - AC-3 unit Given a stolen item owned by someone else, when offered to a non-fence, then the sale is refused with no crime event.
- AC-4 unit Given laundering enabled, when paid, then the stolen flag clears and gold is deducted atomically.
Waits on
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.4 Track NPC disposition with bands, event modifiers and factions done
area:simclass:thiefepic:e20milestone:m2mw-e20.7Allow stealing shop stock with witness-based consequences
Description
Why
Shops are prime thief targets; lifting an item from a counter while the merchant is distracted is a classic thief moment.
What
As a thief, I want to steal displayed wares or from the merchant's stock when unobserved so that distractions and timing pay off.
Shop display items are world items owned by the merchant; taking them uses e18 ownership + witness checks. Witnessed shoplifting: merchant disposition drop, prices up, possible ban (
merchant.bannedfact) and guard alert. Stolen shop stock decrements merchant stock.Scope
- In: Display items bound to merchant stock, consequences ladder (warning → price hike → ban), ban lifting via fine/quest effect
- Out: Pickpocketing the merchant (mw-e10)
Acceptance criteria
- AC-1 unit Given an unwitnessed theft of a displayed item, when taken, then merchant stock decrements and the instance is stolen with owner = merchant.
- AC-2 unit Given a first witnessed theft, when detected, then the consequence ladder moves to "warning" and disposition drops per data.
- AC-3 unit Given a banned merchant, when the player opens the shop, then trade is refused with a ban dialogue line until a
fine.paideffect lifts the ban. - AC-4 integration Given the greybox shop with a merchant facing away, when the thief takes a displayed item, then no crime event fires; facing toward, then one fires.
Waits on
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
area:simclass:thiefepic:e20milestone:m2
M3 MVP content 4
mw-e20.9Add purchasable services as meaningful gold sinks
Description
Why
Gold has meaningful uses beyond items: healing, lodging, translation of books, bribes and donations create choices and stories.
What
As a player, I want to pay for services (heal, rest at the inn, translate an untranslated book, bribe a guard, donate to the shrine) so that gold opens doors, not just inventory slots.
Service schema:
{id, providerNpcId, price or priceFormula, conditions, effects}reusing the effect DSL; invoked from dialogue. Bribes/donations can change disposition or facts.Scope
- In: Service schema + validator, purchase via dialogue effect, atomic gold deduction
- Out: Trainer techniques (e19-knight-trainer-channel uses this)
Acceptance criteria
- AC-1 unit Given a translate-book service costing 40 and an untranslated book in inventory, when purchased, then 40 gold is removed and the book instance gains
translated. - AC-2 unit Given insufficient gold, when purchasing, then nothing changes and the dialogue receives
insufficient-gold. - AC-3 unit Given a service whose conditions are unmet (e.g. no untranslated book), when listed, then it is hidden or shown locked per its
showWhenLockedflag. - AC-4 content Given service data, when validated, then every provider NPC and effect reference resolves.
Waits on
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:simepic:e20milestone:m3mw-e20.8Add haggling with class options and disposition risk
Description
Why
QfG-style merchant banter and DOS2 persuasion make trading a social encounter rather than a spreadsheet.
What
As a player, I want to haggle a price using my class's strengths (knight's reputation, thief's bluff, sorcerer's impressive trick, archer's trade in pelts) so that talking well saves gold.
Haggle is a short dialogue sub-flow: choose an approach, deterministic check (disposition + class/capability modifiers + merchant personality), success grants a haggle modifier (bounded by the economy doc), failure worsens price or disposition; limited to 2 attempts per item per day.
Scope
- In: Haggle flow via dialogue runtime, deterministic check formula, attempt limits, personality modifiers
- Out: General persuasion checks (e22-persuasion-checks is reused)
Acceptance criteria
- AC-1 unit Given a greedy merchant and a thief bluff approach with modifiers summing above threshold, when haggling, then the price improves by the data-defined amount and never beyond the doc cap.
- AC-2 unit Given a failed haggle, when resolved, then disposition drops per personality data and the next haggle on that item has a penalty.
- AC-3 unit Given 2 attempts already made on an item today, when a third is requested, then the option is unavailable with the merchant's refusal line.
- AC-4 unit Given identical state, when the same haggle is attempted twice from the same save, then the outcome is identical (no hidden randomness).
Waits on
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.7 Add deterministic persuasion checks with fail branches done
area:simepic:e20milestone:m3mw-e20.11Add data-driven merchant barks for shop events
Description
Why
Merchants with personality (QfG5) react to what you do — browsing forever, haggling badly, offering them their own candlestick.
What
Bark sets keyed by shop event (
greet,browseLong,buyExpensive,sellJunk,haggleWin,haggleFail,stolenRecognized,banned,farewell) with condition filters and anti-repetition (no same line twice within 5 uses). Uses e22 bark system for display.Scope
- In: Bark set schema for shop events, selection logic, 1 grey-box bark set
- Out: Actual merchant writing (world-content group)
Acceptance criteria
- AC-1 unit Given a bark set with 3 greet lines, when greet fires 3 times, then 3 distinct lines are chosen before any repeats.
- AC-2 unit Given a
browseLongthreshold of 60 s in the shop UI, when exceeded, then exactly one browseLong bark is emitted. - AC-3 content Given a bark set missing the
greetevent, when validated, then validation fails.
Waits on
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.13 Show ambient NPC barks as accessible positional subtitles open
area:contentepic:e20milestone:m3mw-e20.12Add economy balance report and CI gate
Description
Why
Balance drifts as content grows; a CI gate keeps gold meaningful without playtesting every change.
What
CLI
pnpm content:economy-report: sums expected obtainable gold (loot report + quest rewards + sellable items at average buy rates) vs total cost of meaningful sinks (books, techniques, tools, services) for the MVP region; fails CI if the ratio leaves the band in docs/design/economy.md. Outputs markdown report artifact.Scope
- In: Report CLI, CI step, markdown output
- Out: Loot distribution details (e18-treasure-audit-tool, reused)
Acceptance criteria
- AC-1 unit Given fixture content with known totals, when the report runs, then the computed ratio matches the analytic value.
- AC-2 unit Given a ratio outside the configured band, when run in CI mode, then the process exits non-zero naming the direction (too rich/too poor).
- AC-3 unit Given a quest with a gold reward effect, when the report runs, then it is included in obtainable gold.
Waits on
- mw-e18.10 Build treasure audit and loot distribution report tool open
- mw-e20.9 Add purchasable services as meaningful gold sinks open
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
area:toolsepic:e20milestone:m3
E21BookshopsMagical bookshops are exciting progression and exploration destinations. A data-authored bookshop definition combines regional stock pools, school specialties, deterministic rotation, rare one-off books, secret back-room stock, stolen and forbidden books, book-specific pricing and a bookseller identity that hooks into dialogue and rumours. Any settlement's bookshop can be authored purely as data.0/9 done · 0%
M3 MVP content 9
mw-e21.1Generate bookshop stock from regional pools and school specialties
Description
Why
Bookshops should be genuinely exciting destinations with different inventories per settlement (constitution §4).
What
As a designer, I want to author a bookshop as data (region, specialties, pools, rules) on top of the generic shop system so that each settlement's bookshop feels distinct without code.
BookshopDefinition: shopId (E20), region, specialtySchools[] with weight multipliers, staplePool (always stocked), regionalPool, rarePool, secretPool, rotation rules, bookseller profile id. Stock generation is deterministic from world seed + shop id + rotation period.
Scope
- In: bookshop schema, stock generator for staples/regional/specialty, integration with e20 shop buy/sell
- Out: rotation (e21-rotating-stock)
- Out: rare/secret (own beads)
- Out: Briar Glen shop content (world-content group, E24)
Notes
The same book may appear in multiple regional pools; uniques are tracked globally so a unique book only ever exists once.
Acceptance criteria
- AC-1 unit Given a bookshop with 3 staple books and a regional pool of 10, when stock is generated, then all 3 staples plus N regional books (per rule) are present.
- AC-2 unit Given specialty Fire with weight 3×, when stock is generated over 1000 seeds, then Fire books are selected ≈3× as often as unweighted books (±10%).
- AC-3 unit Given the same world seed, shop id and period, when generated twice, then stock is identical.
- AC-4 integration Given the player buys a book, when the transaction completes via the e20 shop system, then the book is in inventory and removed from stock.
- AC-5 content Given a bookshop referencing a non-book item in a book pool, when validated, then validation fails.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e07.1 Define book item schema: spell, language, condition, legality done
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
area:simclass:sorcererepic:e21milestone:m3mw-e21.2Rotate bookshop stock on an in-game schedule
Description
Why
Rotating inventory gives a reason to return to the bookshop after each expedition.
What
As a sorcerer, I want the bookshop's non-staple shelves to change every few in-game days so that revisiting is worthwhile.
Rotation period in in-game days; rotating slots re-roll from pools excluding books the player already owns or knows (configurable); sold-out items restock only at rotation; state persists.
Scope
- In: rotation scheduler, exclusion rules, persistence of current stock and last rotation
- Out: rare/unique books (e21-rare-stock)
Notes
Rotation triggers on in-game time crossing the boundary, including while the player is away (lazy evaluation on shop open).
Acceptance criteria
- AC-1 unit Given a rotation period of 3 days, when in-game time advances 3 days and the shop opens, then rotating slots re-roll exactly once.
- AC-2 unit Given in-game time advances 9 days while away, when the shop opens, then stock equals what 3 sequential rotations would produce (deterministic).
- AC-3 unit Given excludeKnown=true, when rotating, then no book teaching an already-known spell appears in rotating slots.
- AC-4 unit Given a pool smaller than the slot count after exclusions, when rotating, then remaining slots are filled with lore books or left empty — never duplicates.
- AC-5 integration Given a save mid-period, when reloaded, then stock and next rotation time are unchanged.
Waits on
- mw-e20.5 Restock merchant inventories on world-time ticks deterministically done
- mw-e21.1 Generate bookshop stock from regional pools and school specialties open
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e30.1 Define versioned save format with section registry and migrations done
area:simclass:sorcererepic:e21milestone:m3mw-e21.3Stock rare and unique books gated by world state conditions
Description
Why
Rare books are the thrill of a bookshop: the one copy of a legendary grimoire appearing after you help the bookseller.
What
As a sorcerer, I want rare books that appear only under conditions (quest completed, disposition level, day of week, region event) and unique books that exist once in the world so that bookshop visits can surprise me.
Conditions evaluated against the world state store and dialogue disposition; unique books tracked globally; a rare book may be "reserved" for the player for N days.
Scope
- In: condition evaluation, unique registry, reservation window
- Out: secret back-room stock (e21-secret-stock)
Notes
Conditions reuse the shared condition language from E22/E23 if available; otherwise a minimal condition set with a migration note.
Acceptance criteria
- AC-1 unit Given a rare book conditioned on world fact "helped_bookseller", when the fact is false, then the book is not stocked; when true, it appears at next shop open.
- AC-2 unit Given a unique book already looted from a dungeon, when a shop would stock it, then it is skipped.
- AC-3 unit Given a reserved rare book with a 5-day window, when 5 days pass without purchase, then it leaves stock and returns to the rare pool.
- AC-4 content Given a rare entry with an unknown condition key, when validated, then validation fails.
Waits on
- mw-e21.1 Generate bookshop stock from regional pools and school specialties open
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simclass:sorcererepic:e21milestone:m3mw-e21.5Price books by rarity, condition, legality, region and disposition
Description
Why
Gold should have meaningful uses (E20); expensive books are aspirational goals.
What
As a sorcerer, I want book prices that reflect rarity, condition, forbidden premium, regional demand and my relationship with the bookseller so that saving up for a grimoire matters.
price = base(rarity, tier) × condition × legality premium × region demand(school) × disposition modifier; sell-back price for books the player sells (booksellers pay more for books in their specialty).
Scope
- In: pricing function (pure, data-configured), sell-back pricing, integer rounding rules
- Out: generic currency/transactions (e20-shop-system)
Notes
All arithmetic in integer copper to avoid float drift; 100% coverage as an economy rule.
Acceptance criteria
- AC-1 unit Given base 200, damaged ×0.5, forbidden ×1.8, region ×1.0 and neutral disposition, when priced, then the price is 180.
- AC-2 unit Given friendly disposition −10% modifier, when priced, then the price is reduced by 10% (rounded to nearest copper).
- AC-3 unit Given the player sells a book in the shop's specialty school, when priced, then sell-back is the specialty rate (e.g. 40% of buy) instead of the default (25%).
- AC-4 unit Given any combination of modifiers, when priced, then the result is an integer ≥ 1 (property test).
- AC-5 content Given a pricing config with a negative multiplier, when validated, then validation fails.
Waits on
- mw-e20.3 Implement pure price model with disposition and stolen modifiers done
- mw-e21.1 Generate bookshop stock from regional pools and school specialties open
area:simclass:sorcererepic:e21milestone:m3mw-e21.7Give booksellers identity profiles, wants and rumour hooks
Description
Why
Merchants have identities (E20); a bookseller who wants a specific tome or gossips about a rare book elsewhere drives exploration.
What
As a designer, I want a bookseller profile (personality tags, favoured/disliked schools, wanted books list, rumours about books elsewhere, greeting variants keyed on player class and known spells) so that the dialogue group can make each bookseller memorable.
Exposes conditions/variables to the dialogue runtime: bookseller.wants(bookId), bookseller.rumour(), player.knowsSchool(school). Wanted books pay a bonus or unlock rare stock.
Scope
- In: profile schema, dialogue variables/conditions, wanted-book bonus, rumour selection rule (points to books in other shops or dungeons)
- Out: actual dialogue writing and NPC characters (world-content/narrative groups)
Notes
Rumours reference book ids + a location hint string authored in content; they add entries to the grimoire "rumoured spells" section (e07-grimoire-ui).
Acceptance criteria
- AC-1 integration Given a profile with wantedBooks [X], when the player sells book X, then the bonus price applies and world fact "delivered_X" is set.
- AC-2 integration Given the dialogue runtime queries bookseller.rumour(), when the player already owns the rumoured book, then a different rumour is chosen or none.
- AC-3 integration Given a rumour is heard, when resolved, then the rumoured spell appears in the grimoire rumoured list with its location hint.
- AC-4 content Given a profile referencing an unknown book id in wantedBooks, when validated, then validation fails.
Waits on
- mw-e21.1 Generate bookshop stock from regional pools and school specialties open
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:simclass:sorcererepic:e21milestone:m3mw-e21.8Browse bookshop shelves with covers, previews and known markers
Description
Why
The bookshop should feel like browsing a magical shop, not a spreadsheet.
What
As a sorcerer, I want to browse books by shelf/school with cover art, price, a first-page preview, comprehension estimate and "already known"/"stolen"/"forbidden" markers so that choosing a book is exciting and informed.
Extends the generic shop UI with book-specific panels; sell tab shows sell-back prices and wanted-book highlights.
Scope
- In: bookshop UI panels, preview (reuses reader component), markers, sell tab highlights, keyboard/gamepad navigation
- Out: generic shop UI (E20)
- Out: final cover art (asset prompt beads per book)
Notes
Placeholder covers: school-coloured leather with title text until art arrives.
Acceptance criteria
- AC-1 integration Given a stocked shop, when the UI opens, then books are grouped by school shelf with price and cover (placeholder if no art).
- AC-2 integration Given a book teaching a known spell, when listed, then it shows an "Already known" marker.
- AC-3 integration Given the preview action, when used, then the first page renders via the reader component (garbled if language unknown).
- AC-4 integration Given insufficient gold, when attempting to buy, then the buy button is disabled with a reason and no transaction occurs.
- AC-5 e2e Given the greybox testbed fixture shop, when a scripted player buys a book, then it appears in inventory and gold decreases by the listed price.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e07.9 Read books in a page-turning reader with a study action open
- mw-e21.5 Price books by rarity, condition, legality, region and disposition open
area:uiclass:sorcererepic:e21milestone:m3mw-e21.9Provide a bookshop authoring template, validator and fixture shop
Description
Why
The world-content group must author the Briar Glen bookshop (and future ones) purely as data.
What
Template src/content/bookshops/_template.json with every field documented, a CLI/dev validator (pnpm validate:bookshops) reporting stock simulation over 10 rotations, and a non-Briar-Glen fixture shop ("Testbed Tomes") in the greybox testbed exercising every rule.
Scope
- In: template, validator CLI with stock simulation report, fixture shop, authoring notes in docs/design/bookshops.md
- Out: Briar Glen bookshop content (world-content group, E24)
Notes
Report flags pools that can never appear (all conditions unsatisfiable) and uniques duplicated across shops.
Acceptance criteria
- AC-1 content Given the template, when validated, then it passes the bookshop schema.
- AC-2 integration Given the fixture shop, when the validator simulates 10 rotations, then it prints stock per period and exits 0.
- AC-3 integration Given a shop whose rare book condition references a fact no content ever sets, when validated, then the validator warns "unreachable stock".
- AC-4 content Given two shops listing the same unique book, when validated, then validation fails.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e21.1 Generate bookshop stock from regional pools and school specialties open
- mw-e21.2 Rotate bookshop stock on an in-game schedule open
- mw-e21.3 Stock rare and unique books gated by world state conditions open
area:toolsclass:sorcererepic:e21milestone:m3mw-e21.4Reveal secret back-room stock through dialogue, trust or discovery
Description
Why
Secret inventory rewards curiosity and relationships — the "do you have anything… special?" moment.
What
As a sorcerer, I want some bookshops to have hidden stock unlocked by a password, high disposition, a quest, or physically finding the back room so that secrets exist even in shops.
Secret pool gated by unlock method; can be accessed by a thief breaking in (stolen goods flagged on removal).
Scope
- In: secret pool unlock via dialogue action/disposition/world fact, physical back-room container hook
- Out: dialogue content and passwords (world-content group)
Notes
Taking from the back room without buying marks books stolen (e21-stolen-forbidden-stock).
Acceptance criteria
- AC-1 integration Given a dialogue action unlockSecretStock, when executed, then the secret pool becomes purchasable for this shop.
- AC-2 unit Given disposition below the threshold and no other unlock, when the shop opens, then secret items are not listed.
- AC-3 integration Given the player takes a secret-stock book from the back-room container without buying, when removed, then the book is flagged stolen with owner = the shop.
- AC-4 unit Given secret stock unlocked, when saved and reloaded, then it remains unlocked.
Waits on
- mw-e21.3 Stock rare and unique books gated by world state conditions open
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:simclass:sorcererepic:e21milestone:m3mw-e21.6Handle stolen and forbidden books in bookshop trade
Description
Why
Some books are stolen or forbidden (constitution §4) — trading them should carry risk and create thief/sorcerer crossover.
What
As a sorcerer or thief, I want booksellers to react to stolen and forbidden books — refusing, fencing at a discount, or selling forbidden stock under the counter — so that illicit books have a market with consequences.
Bookseller profile flags: buysStolen, sellsForbidden, reportsContraband. Selling a book stolen from this very shop triggers recognition. Forbidden stock is only shown when no lawful witnesses are nearby.
Scope
- In: stolen-owner tracking in trade, bookseller legality flags, witness check for forbidden sales, recognition consequence
- Out: guard response behaviour (E11)
- Out: forbidden reading consequences (e07-forbidden-books)
Notes
Stolen flag comes from the E17/E10 ownership model — Related: thief group stealing/ownership bead.
Acceptance criteria
- AC-1 unit Given a bookseller with buysStolen=false, when offered a stolen book, then the sale is refused with reason "stolen".
- AC-2 unit Given buysStolen=true, when buying a stolen book, then price is the fence rate (default 50% of normal sell-back).
- AC-3 integration Given the player tries to sell a book stolen from this same shop, when offered, then the bookseller recognises it: disposition drops and a world fact "caught_selling_back" is recorded.
- AC-4 integration Given sellsForbidden=true and a lawful NPC within perception range, when the shop opens, then forbidden stock is hidden until the witness leaves.
- AC-5 integration Given reportsContraband=true, when the player sells a forbidden book, then a ContrabandReported event fires.
Waits on
- mw-e07.6 Give forbidden books social risk and whispering side effects open
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e21.5 Price books by rarity, condition, legality, region and disposition open
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
area:simclass:sorcererepic:e21milestone:m3
E22NPC & Dialogue SystemCharacters support conversations with conditions and effects, disposition, meaningful choices, class-specific options, persuasion (Divinity: Original Sin 2 style), rumors that lead to places, and lightweight state tracking. Text only for MVP (no voice), with subtitle/text-size accessibility hooks. Dialogue is authored as validated data, never bespoke code.13/19 done · 68%
M2 Class fantasies 4
mw-e22.1Turn witnessed crimes into settlement bounties with pay/jail/flee
Description
Why
Thief play needs stakes: getting caught should create a new situation (a fine, a night in the Watch House cell, a chase out of town), not an instant reload. Theft, fence recognition, shoplifting, disguises and failure narratives all emit or expect crime consequences, but no item owned bounty or guard outcomes.
What
As a thief, I want crimes that are actually witnessed to raise a bounty in that settlement and lead to a confrontation I can pay, serve or flee so that failure changes the story instead of ending it.
- Crime types (data): theft (from
crime.theft/crime.theft.recognized), trespass (entering arestrictedtrigger volume owned by a settlement/faction while seen, after one warning bark), assault (damage to a non-hostile NPC via damage events), disguise exposure (crime.disguise.exposedfrom e10-disguises), murder. Each has severity, report delay and consequence effects written in the condition/effect DSL. - Witnesses: a crime counts only if a witness perceives it (e11 perception). A witness must *report* — survive and stay conscious for the report delay or reach a guard — before bounty is added. Silencing a witness is a (risky) choice.
- Bounty & reputation: bounty per settlement stored as world facts (
crime.bounty.<settlement>), crime records with witness and game time; disposition/faction effects via e22-disposition. - Guard response hook: when a guard of that settlement perceives a player whose bounty ≥ threshold, emit one
crime.confront{guard, settlement, bounty}; e11 behaviour and dialogue consume it. - Outcomes that continue the story: *pay* (gold ≥ bounty → cleared; stolen items confiscated), *jail* (advanceTime by sentence, stolen items to the settlement evidence container,
player.jailed.count++, player placed at the jail spawn — world content may offer an escape), *resist/flee* (settlement guards go to Combat toward the player, bounty += resist severity, DSL-gated services refuse while bounty > 0). AclearBountyDSL effect lets quests pardon.
Scope
- In: Crime definitions schema, witness/report pipeline, bounty facts per settlement, trespass and assault detection, confront event, pay/jail/resist outcome effects, save via world facts, debug commands
- Out: Theft detection and stolen flag — see e18-ownership-stolen
- Out: Guard movement/arrest behaviour and barks — mw-e11 (consume
crime.confront) - Out: Jail geometry and escape route — world content (Related: E24/E26)
- Out: Failure-narrative text — see e39-failure-narratives
Notes
Canon (docs/narrative/story-bible.md): Briar Glen's Watch House has a one-cell jail; the failure-narrative fact is
player.jailed.count. The story's "bounty: 500 crowns, raised to 800" is the missing-persons reward on the notice board, not a crime bounty — keep the terms distinct in UI text ("fine"/"wanted" vs "reward").Integration note: Emits
crime.confront; guard arrest/confront behaviour in E11 consumes it (E11 guard behaviour items should subscribe when this lands).Acceptance criteria
- AC-1 unit Given a
crime.theftevent with one witness who reports, when processed, then the bounty for that witness's settlement increases by the theft severity scaled by item value tier and one crime record (type, witness, game time) is stored as a fact. - AC-2 unit Given a witnessed theft where the witness is knocked out before the report delay (default 20 s) expires, when the delay passes, then no bounty is added (edge: silenced witness).
- AC-3 unit Given a player with bounty ≥ the confront threshold perceived by a guard of that settlement, when perception fires repeatedly, then exactly one
crime.confrontevent is emitted per encounter and the pay option is disabled when gold < bounty. - AC-4 unit Given the jail outcome with a 2-day sentence, when applied, then the world clock advances 2 days, stolen items move to the evidence container, the bounty clears,
player.jailed.countincrements and the player is placed at the jail spawn without a reload. - AC-5 unit Given the resist outcome, when chosen, then every guard of that settlement within alert radius enters the Combat alert state toward the player and the bounty increases by the resist severity.
- AC-6 unit Given crimes in two settlements, when one bounty is paid, then the other settlement's bounty is unchanged.
- AC-7 content Given the crime definitions, when validated, then every crime type has severity, report delay and DSL-valid consequences, and an unknown settlement id fails validation naming the file.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e22.4 Track NPC disposition with bands, event modifiers and factions done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:simclass:thiefepic:e22milestone:m2size:m- Crime types (data): theft (from
mw-e22.14Add dialogue preview runner and debug console commands
Description
Why
Fast content creation (constitution §10): writers need to play a conversation in seconds with any state.
What
CLI
pnpm dialogue:play <id> --class thief --fact x=true --item yrunning the real runtime in the terminal;pnpm dialogue:graph <id>exporting Mermaid for docs/review. Debug console:dlg.start <id>,disp.set <npc> <n>,fact.set,rumor.list.Scope
- In: Terminal runner, Mermaid export, console commands
- Out: A visual dialogue editor (post-MVP)
Acceptance criteria
- AC-1 unit Given a fixture dialogue and
--class thief, when run with scripted choice input, then the transcript matches the snapshot including the thief option. - AC-2 unit Given
dialogue:graph, when run on a fixture, then the output is valid Mermaid containing every node id. - AC-3 unit Given
disp.set unknown-npc 50, when executed, then the console reports an unknown NPC and changes nothing.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e22milestone:m2mw-e22.17Dialogue validator: cross-dialogue jump loops, rumor ids, check nodes with no reachable success
Description
Follow-ups from PR #420 (mw-e22.8): analyse loops that pass through jump nodes across dialogues; check rumor ids once a rumor content type exists (mw-e22.11); flag a check node whose success score is unreachable given the persuasion table maxima.
area:simepic:e22milestone:m2mw-e22.18Conversation robustness: body-height talk focus, endConversing on NPC death/despawn
Description
From PR #421: Talk focus uses the creature's feet (use a body-height anchor); call endConversing when an NPC dies or despawns mid-talk from the owning modules, not only from the controller's per-step check.
area:simepic:e22milestone:m2
M3 MVP content 2
mw-e22.12Let players give gifts and bribes that shift disposition
Description
Why
Giving the right thing to the right person is a classic QfG-style solution and a gold/item sink.
What
As a player, I want to offer items or gold to characters, with their tastes deciding the effect, so that knowing people pays off.
NPC
likes/dislikesitem tags; a generic "Offer…" dialogue option opens an item picker; effect = disposition delta by taste × value tier; bribes can satisfy dialogue conditions (bribedfact). Offering a stolen item to its owner returns it (clears flag) and triggers the owner'sstolenReturnedline.Scope
- In: Taste data, offer flow, disposition effect, bribe facts, stolen-return path
- Out: Creature feeding/befriending (mw-e14)
Integration note: Promoted to P1 — befriending/helping creatures (Brother Horn, the goblins, Clatter) is an MVP constitution pillar and depends on this.
Acceptance criteria
- AC-1 unit Given an NPC who likes
flowerand a flower offered, when resolved, then disposition increases by the liked amount for its value tier. - AC-2 unit Given a disliked item offered, when resolved, then disposition decreases and the item is returned to the player.
- AC-3 unit Given a stolen item offered to its owner, when resolved, then the item is removed, the stolen flag cleared and the owner's
stolenReturnednode is entered. - AC-4 unit Given a quest item offered to a non-target NPC, when attempted, then the offer is refused and the item stays.
Waits on
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e22.4 Track NPC disposition with bands, event modifiers and factions done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
area:simepic:e22milestone:m3mw-e22.13Show ambient NPC barks as accessible positional subtitles
Description
Why
Overheard lines make the world feel alive and deliver rumors and stealth feedback; without voice they must be readable subtitles.
What
As a player, I want to read what nearby characters say (guards muttering, merchants calling out) with speaker names and direction so that I overhear the world.
Bark runtime: condition-filtered lines triggered by events (alert state changes, proximity, time), priority + cooldown + anti-repetition. Subtitle display: speaker name, direction indicator for off-screen speakers, max 3 concurrent, respects text size, background opacity and "show speaker names" settings; can be toggled.
Scope
- In: Bark schema + selector, subtitle HUD, settings hooks
- Out: Merchant bark content (e20-merchant-barks)
- Out: AI state bark triggers wiring (mw-e11 emits events)
Notes
UI assets assumed: subtitle background plate, off-screen direction chevron.
Acceptance criteria
- AC-1 unit Given 5 barks triggered in the same second, when displayed, then at most 3 are visible, ordered by priority.
- AC-2 unit Given a bark line used once with cooldown 30 s, when triggered again at 10 s, then a different line or no line is chosen.
- AC-3 integration Given a speaker behind the camera, when a bark shows, then a direction indicator points toward the speaker.
- AC-4 integration Given subtitles disabled for barks in settings, when a bark triggers, then nothing is shown but the bark event is still emitted for telemetry/tests.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:uiepic:e22milestone:m3
E23Quest FrameworkQuests support branching objectives and multiple resolutions (including non-combat) authored as data without bespoke scripting. Quest state lives in the world-state store and persists; failure transitions continue the story instead of dead-ending it. Players track progress in an in-character journal.6/13 done · 46%
M2 Class fantasies 6
mw-e23.4Make quest failures continue the story instead of dead-ending
Description
Why
Failure creates stories (constitution §2): if the key NPC dies or the thief is caught, the world should adapt.
What
As a player, I want the story to continue when I fail — a new lead, a harder path, a changed ending — so that I rarely feel the need to reload.
Failure continuation types: start a successor quest, move to an alternate stage, or resolve with a "bittersweet" resolution. Common auto-failure triggers: essential NPC removed, item destroyed, time window elapsed. Runtime guarantees a continuation is applied atomically with the failure. Journal records the failure narratively.
Scope
- In: Continuation application, built-in failure triggers (npc removed, item destroyed, deadline), journal hook
- Out: Timed world events beyond quest deadlines (mw-e27)
Acceptance criteria
- AC-1 unit Given a quest whose giver dies with continuation
startQuest(avenge-giver), when the giver is removed, then the original fails and the successor starts in the same tick. - AC-2 unit Given a quest item destroyed (e.g. burned via e03), when
item.destroyedfires for it, then the configured failure applies. - AC-3 unit Given a deadline of 3 in-game days, when day 4 begins without resolution, then the failure fires once.
- AC-4 unit Given a failure continuation referencing a successor that fails to start (conditions false), when applied, then the runtime falls back to the quest's
defaultContinuationand logs a content error.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e23.3 Bind quest objective kinds to sim events done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:simepic:e23milestone:m2mw-e23.5Validate quests are resolvable and failures have continuations
Description
Why
An unresolvable quest is the worst kind of bug; CI must prove every quest can be finished and every failure continues.
What
Static + symbolic validator: all references resolve (items, npcs, facts, areas, dialogues); every resolution's conditions are satisfiable given the set of known fact/item producers (placements, loot, shops, dialogue effects, other quests); every required item is obtainable somewhere; at least one non-combat resolution exists unless
combatOnly: truewith justification; every failure has a continuation that is itself valid; no quest start depends on its own resolution (cycle).Scope
- In: Producer index across content, satisfiability check, cycle detection, CLI + CI hook
- Out: Runtime simulation of full playthroughs (e23-quest-debug-tools)
Acceptance criteria
- AC-1 unit Given a resolution requiring item
xthat no placement, loot table, shop or effect produces, when validated, then validation fails naming the quest, resolution and item. - AC-2 unit Given a quest whose only resolution requires
entityState(... dead)and nocombatOnlyflag, when validated, then a non-combat warning is reported. - AC-3 unit Given quest A starting on B resolved and B starting on A resolved, when validated, then a cycle error is reported.
- AC-4 unit Given a fact required by a resolution with no producer, when validated, then validation fails naming the fact.
- AC-5 content Given all repository quests, when CI runs the validator, then it exits 0.
Waits on
- mw-e18.2 Validate loot tables: references, uniques, cycles, weights done
- mw-e22.8 Validate dialogues: reachability, exits, references, text limits done
- mw-e23.1 Define quest authoring format with objectives and resolutions done
- mw-e23.4 Make quest failures continue the story instead of dead-ending open
area:contentepic:e23milestone:m2mw-e23.7Build grey-box reference quest with three resolutions and a failure
Description
Why
An end-to-end fixture proves dialogue, quests, inventory, loot and AI integrate, and gives world-content authors a working template.
What
As a designer, I want a small grey-box quest demonstrating the full framework so that I can copy it and so CI proves the systems work together.
Testbed-only content (not Briar Glen): an NPC asks the player to recover a stolen item from a guarded room. Resolutions: combat (remove guard), stealth (take item unseen via e18 ownership), diplomacy (persuasion check with the guard), plus a failure (item destroyed → successor quest). Uses a rumor, a class-gated option and a loot container.
Scope
- In: Fixture content under content/fixtures/, Playwright e2e scripts for each path
- Out: Real game quests (world-content group)
Acceptance criteria
- AC-1 e2e Given the greybox reference quest, when the combat path is played via scripted input, then the quest resolves as
combatand the journal shows the resolution entry. - AC-2 e2e Given the reference quest, when the stealth path is played (item taken unseen), then it resolves as
stealthand no crime event was emitted. - AC-3 e2e Given the reference quest, when the diplomacy path passes the persuasion check, then it resolves as
diplomacywithout the guard entering Combat state. - AC-4 e2e Given the reference quest, when the item is destroyed by fire, then the quest fails and the successor quest becomes active.
- AC-5 content Given the fixture, when the quest and dialogue validators run, then both pass.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e22.7 Add deterministic persuasion checks with fail branches done
- mw-e22.9 Start conversations with NPCs in-world and handle interruptions done
- mw-e23.4 Make quest failures continue the story instead of dead-ending open
- mw-e23.6 Build in-character journal for quests, rumors and notes done
area:qaclass:allepic:e23milestone:m2mw-e23.10Write dialogue and quest authoring guide with templates
Description
Why
The world-content group will author Briar Glen NPCs and quests; a clear guide and templates make that fast and consistent.
What
docs/content/dialogue-and-quests.md: format reference, DSL condition/effect catalogue (generated from the registry), patterns (multiple resolutions, class-gated options, persuasion with fail branches, rumors that lead to places, failure continuations), humour/tone guidance aligned to the tone row in the contract, validator error glossary, copyable templates in content/_templates/.
Scope
- In: Guide, generated DSL catalogue section, templates for NPC dialogue, merchant, trainer, quest, rumor
- Out: Story bible (e39-story-bible)
Acceptance criteria
- AC-1 unit Given the DSL registry, when the catalogue generator runs, then every registered condition and effect kind appears in the guide section (test compares lists).
- AC-2 content Given the templates in content/_templates/, when validated, then each passes its schema.
- AC-3 manual Given the guide, when reviewed, then it includes a worked example of a quest with combat, stealth and diplomacy resolutions and a failure continuation.
Waits on
- mw-e19.8 Let trainers teach knight techniques through dialogue services open
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e22.11 Add rumor system that points players to places and secrets done
- mw-e23.7 Build grey-box reference quest with three resolutions and a failure open
area:designepic:e23milestone:m2mw-e23.8Add quest debug commands and "why is this stuck" explainer
Description
Why
Diagnosing stuck quests must be fast for designers and playtest bug reports.
What
Debug console:
quest.list,quest.start <id>,quest.objective <id> <obj> complete,quest.resolve <id> <res>,quest.why <id>(prints each resolution/objective condition with describe() and current truth values). CLIpnpm quest:sim <id> --script pathto run a quest against scripted fact/item changes headlessly.Scope
- In: Console commands, explainer, headless quest simulator
- Out: Visual quest editor (post-MVP)
Acceptance criteria
- AC-1 unit Given an active quest with an unmet resolution condition, when
quest.whyruns, then output lists that condition as false with its describe() text. - AC-2 unit Given
quest.resolve q1 bogus, when executed, then the console lists valid resolution ids and changes nothing. - AC-3 unit Given a scripted sim of the reference quest stealth path, when run headlessly, then it ends with resolution
stealth.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e23milestone:m2mw-e23.13Remember quest-relevant events from before a quest starts
Description
Follow-up to mw-e23.12 (PR #446): seen-facts are only written for active quests, so side quests that start later (e.g. kill the chief before being asked) never credit earlier deeds. Consider durable world facts (entity state, area visits) for ids referenced by any quest objective so a newly started quest catches up. Mind the e2e golden save hashes and the new-game-facts rule.
area:simepic:e23milestone:m2
M3 MVP content 1
mw-e23.9Add optional quest wayfinding hints as an accessibility setting
Description
Why
Default play favours discovery (no waypoints), but some players need help navigating; an opt-in hint respects both.
What
As a player who gets lost, I want to enable subtle hints toward my pinned objective so that I can keep playing without a guide.
Objectives may author
hintTarget(entity/area). Setting "Quest hints": Off (default) / Subtle (compass nudge when facing direction) / Clear (on-screen marker). Hints never reveal hidden/secret objectives.Scope
- In: Hint target data, three-level setting, compass nudge + marker rendering hook
- Out: Full world map (not planned for MVP)
Notes
UI assets assumed: compass nudge glyph, objective marker icon.
Acceptance criteria
- AC-1 unit Given setting Off, when a pinned objective has a hintTarget, then no hint renders.
- AC-2 integration Given setting Clear, when the target is on-screen, then a marker renders at its projected position; off-screen, an edge arrow renders.
- AC-3 unit Given a hidden objective with a hintTarget, when any setting is active, then no hint renders for it.
Waits on
- mw-e23.6 Build in-character journal for quests, rumors and notes done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:uiepic:e23milestone:m3
E24Briar GlenBriar Glen is a dense, lived-in settlement: every named building from the story bible exists and is enterable, merchants and the Lamplit Stacks have authored stock, named NPCs keep day/night schedules, rumours point at real places, rooftops and secrets reward thieves and the curious, and the first-night sleepwalk plays out through its streets.4/22 done · 18%
M3 MVP content 17
mw-e24.13Place Briar Glen's NPCs on day/night schedules
Description
Why
A lived-in town: people open shops, go to the inn, sleep — and some walk in their sleep.
What
As a player, I want townsfolk to keep daily routines so that the town feels alive and so that timing matters for thieves.
Schedule data (consumer of e27-world-clock, which leaves NPC routines to consumers) per named NPC (Mirela, Hale, Vane, Dot, Hollis, Juniper, Oswin, Duggan pre-capture) and 8–12 generic townsfolk: windows (time range → location/activity) built on e11-patrol-routines posts and the world clock. Shop hours bound to merchant hours. Hale sleepwalks toward the Bell Line stones on unlocked nights (Captain's Boots).
Scope
- In: Schedule schema (thin layer on patrol routines + clock windows), schedule data for all town NPCs, shop hours
- Out: Off-screen catch-up engine (Related: e27-offscreen-catchup, P2 — not needed: positions are resolved directly from the schedule at load)
- Out: Dialogue (E39)
Notes
The clock and scheduler are e27-world-clock; this item adds only the thin schedule-window schema and all Briar Glen routine data.
Acceptance criteria
- AC-1 unit Given Mirela's schedule and time 08:00, when resolved, then her target is the Stacks counter; at 22:00, her upstairs room.
- AC-2 integration Given the player enters town at 14:00 after being away, when loaded, then every NPC is placed at their scheduled location for 14:00.
- AC-3 integration Given an NPC is in dialogue when their window ends, when the dialogue closes, then they walk to the next location (edge: never teleport in view).
- AC-4 perf Given all scheduled NPCs present at Market Green midday, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-8551 Walk scheduled NPCs between locations instead of popping at window edges done
- mw-8stk Measure Market Green midday frame time with all scheduled townsfolk (mw-e24.13 AC-4) open
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:contentepic:e24milestone:m3mw-e24.3Place systemic objects and hiding spots throughout Briar Glen
Description
Why
The town must be a toybox too: lanterns to snuff, hay to hide in, troughs to freeze, washing lines to burn.
What
As a player, I want the town full of things that react to fire, water, light and force so that I can improvise.
Lamp posts and window lights (extinguishable, relit by the lamplighter on schedule), water troughs, hay carts and barrels (hiding places), washing lines (flammable ropes), stacked crates, market stalls (breakable), shadowed alleys, loose cobbles for throwing.
Scope
- In: Prop placement with material presets, hiding-place volumes, light sources, throwables
- Out: Interiors' props (per-building items)
Notes
Fire in town is a real risk: flammable density near the Ox and stalls must be capped so fires burn out, not destroy the town (bible tone).
Acceptance criteria
- AC-1 content Given the town scene, when validated, then every placed prop uses an e03 material preset and no ad-hoc properties.
- AC-2 integration Given a fire started at a market stall, when simulated 120 s, then it burns out without spreading to building shells flagged non-flammable (edge).
- AC-3 integration Given a hay cart hiding place at night, when a guard passes at 3 m, then the hidden player remains undetected.
Waits on
- mw-e03.13 Support hanging objects on ropes that burn, cut or snap done
- mw-e03.16 Extinguish and relight torches, candles and braziers systemically done
- mw-e03.2 Provide data-defined material presets for common world objects done
- mw-e09.8 Hiding places: wardrobes, barrels, tall grass and shadowed alcoves open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
area:contentepic:e24milestone:m3mw-e24.4Build the Sleeping Ox inn: common room, rooms, cellar and rest
Description
Why
The inn is the story's first night, the rumour hub and the Briarwood wake point.
What
As a player, I want a warm inn where I can rest, hear rumours and sell hot goods so that town has a home base.
Interior: common room with hearth (Dot's Ember request can fail and scorch), bar, lending shelf, stairs to the player's room (the first-night window), Dot's back room (thief), cellar with fencing stock and the goblin kettle for Framed in Brass. Rest via an inn-kind rest point. Respawn rule: Briarwood deaths wake here.
Scope
- In: Interior scene, inn-kind rest point (e27-rest-points) paid via e20-services, lending shelf books, cellar with fence merchant data, kettle item, respawn rule entry
- Out: Dot's dialogue (e39-dialogue-town-core)
- Out: Fence runtime (e20-fences-stolen-goods)
Notes
Lending shelf books from e39-books-lore (A Traveller's Account of the Glen, The Ox Beneath the Hill copy).
Acceptance criteria
- AC-1 integration Given the player rents the room, when resting until dawn, then rest.completed fires, the clock reads 06:00 and rest-reset facts reset.
- AC-2 integration Given the player dies in the Briarwood, when the respawn rule resolves, then the player wakes in the Ox room.
- AC-3 content Given Dot's fence merchant, when validated, then isFence is true and the goblin kettle is present in the cellar.
- AC-4 integration Given a botched Ember at the hearth, when fire spreads to the rug, then it is extinguishable and sets fact ox.scorched which Dot remarks on (edge).
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e20.6 Support fences and merchant reactions to stolen goods open
- mw-e20.9 Add purchasable services as meaningful gold sinks open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:contentepic:e24milestone:m3mw-e24.5Author the Lamplit Stacks bookshop on the e21 authoring template
Description
Why
The constitution makes bookshops "genuinely exciting destinations"; this is the MVP's one excellent bookshop.
What
As a sorcerer, I want a crooked three-storey bookshop with rotating stock, a restricted shelf and a back room so that visiting Mirela is a highlight.
src/content/bookshops/lamplit-stacks.json from the e21 template: regional pool (licensed schools), rotating shelf, rare conditional stock (e.g. restored after dungeon finds), buyback counter, The Vesperine Hours (sale or loan). Interior: three floors, red shelf, reading nook, Mortimer's rare shelf, the curtain and back-room door (content behind them is e24-stacks-back-room).
Scope
- In: Bookshop data (public stock), interior scene, shelf placements, Vesperine Hours item placement
- Out: Restricted shelf and back room (e24-stacks-back-room)
- Out: Bookshop runtime/UI (E21)
- Out: Mortimer behaviour (e14-mortimer)
- Out: Spellbook flavour text (e39-spellbook-flavor)
Notes
Follow the bible's legality: Illusion/Conjuration/Gravity/Shadow restricted; Necromancy/Time/Somnomancy forbidden.
Acceptance criteria
- AC-1 content Given lamplit-stacks.json, when pnpm validate:bookshops runs, then it passes and the 10-rotation simulation shows every pooled book appearing at least once.
- AC-2 integration Given the shop at closing hour, when the player is inside, then Mirela asks them to leave and the stock UI closes (edge).
- AC-3 integration Given a book sold to the buyback counter, when the stock rotates, then it may appear in public stock per the e21 rules.
- AC-4 content Given the shop, when validated, then The Vesperine Hours is obtainable by purchase or loan on day 1.
Waits on
- mw-e21.3 Stock rare and unique books gated by world state conditions open
- mw-e21.7 Give booksellers identity profiles, wants and rumour hooks open
- mw-e21.9 Provide a bookshop authoring template, validator and fixture shop open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
area:contentclass:sorcererepic:e24milestone:m3mw-e24.7Build the Forge, Fletchery & Simples and Bakery with merchant stock
Description
Why
Merchants have identities, inventories and specialties; each shop should serve a class fantasy.
What
As a player, I want Oswin's forge, Juniper's fletchery and Hollis's bakery to feel like places with people so that shopping is part of the world.
Interiors and merchant data: Brand's Forge (weapons, armour repair service, lockpicks "for miners", Ember-heats-forge discount, bell mould for recasting the tongue), Fenn's Fletchery & Simples (arrows, trick-arrow schematics, snares, salves, wakeful tea vs sleep-draught), Pell's Bakery (bread — also feed for the sleepers, Pell family Bible in the back room).
Scope
- In: Three interiors, three merchant files, services, key story items placement
- Out: Dialogue (e39-dialogue-town-secondary)
- Out: Economy balancing (e20-economy-balance-tool)
Notes
Juniper sells poisons to thieves only after a favour (condition on fact).
Acceptance criteria
- AC-1 content Given the three merchant files, when validated, then they pass e20 schema checks and price bands.
- AC-2 integration Given the sorcerer casts any heat stimulus on the forge hearth, when Oswin is next spoken to, then a discount effect applies once.
- AC-3 integration Given Juniper's stock, when the player has no favour fact, then poisons are hidden (edge); with it, they appear.
- AC-4 integration Given the tongue is lost or destroyed, when asked, then Oswin offers the recast service using the bell mould.
Waits on
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e20.9 Add purchasable services as meaningful gold sinks open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
area:contentepic:e24milestone:m3mw-e24.8Build the Watch House and a jail cell whose drain leads out
Description
Why
"Caught stealing in town — Hale jails you. The cell has a loose stone." Jail is a secret entrance, not a game over.
What
As a thief, I want getting caught to put me in a cell with a way out so that failure opens a new route.
Watch House interior (Hale's desk with the missing-persons list and footprint map, boots by the door), one cell. Jail outcome of e22-crime-bounty places the player at this cell's jail spawn and confiscates stolen goods into this settlement's evidence chest. Loose stone (interactable, or found by searching) → Vesperine drain → cistern node.
Scope
- In: Interior, cell with jail spawn, settlement evidence container, drain passage to cistern
- Out: Crime, bounty and jail outcome logic (e22-crime-bounty)
- Out: Guard arrest AI (E11)
- Out: Cistern (e24-glen-well-cistern)
Notes
Snig can be jailed here too; busting him out uses the same drain.
Acceptance criteria
- AC-1 integration Given the e22-crime-bounty jail outcome for Briar Glen, when applied, then the player stands in this cell and stolen items are in the Watch House evidence chest.
- AC-2 integration Given the player in the cell, when the loose stone is interacted with, then the drain opens and leads to the cistern node.
- AC-3 integration Given the player escapes, when Hale next sees them, then fact jail.escaped is set and his greeting changes (edge: no instant re-arrest loop).
Waits on
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
area:contentclass:thiefepic:e24milestone:m3mw-e24.9Build Vane House as an infiltration target with strongbox and ledger
Description
Why
The thief can have proof of Vane's lie before Act II; the house is the town's infiltration set piece.
What
As a thief, I want to break into the mine owner's house by rooftop or cellar so that I can steal the horn-coin sample and his secrets.
Two-storey house on the rise: servants and a house guard on schedules, study with the strongbox (horn-coin samples, letter from "Mr. S—"), Vane's mine key (for the mine gate), dogs-free garden with hedges, balcony window, coal-chute cellar. Day: Vane receives visitors; night: guard patrol.
Scope
- In: Interior and grounds, patrols, strongbox and contents, mine key placement, three entry points
- Out: Vane dialogue (e39-dialogue-town-secondary)
- Out: Strongbox lock mechanics (e10-lockpicking)
Notes
Entry points: balcony window (climb/levitate/rope arrow), coal chute (thief/any), front door (persuade, or bribe the servant).
Acceptance criteria
- AC-1 integration Given night patrols, when the stealth scenario replay enters via the balcony, then the strongbox can be opened with no guard reaching Alerted.
- AC-2 content Given the house, when validated, then at least three entry points are declared as obstacle routes for >= 3 classes.
- AC-3 integration Given the player is detected, when the guard raises the alarm, then a crime is reported via e22-crime-bounty and the jail outcome remains available (edge: failure lands in jail).
- AC-4 integration Given the mine key is taken, when the mine gate is approached, then the key opens the padlock via the keyring.
Waits on
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
- mw-e17.5 Auto-use keys from the keyring on matching locks done
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
area:contentclass:thiefepic:e24milestone:m3mw-e24.10Hide the cistern route under the Glen Well
Description
Why
"Wells can conceal tunnels." The rumour "drop a stone and you hear it land twice" should pay off.
What
As a curious player, I want to find a loose ladder rung in the Glen Well that leads to an old cistern so that the town connects to the priory.
Well shaft with a loose rung (climb down, or drop down and take fall damage), echo behaviour (a dropped object produces two landing sounds via a ledge), flooded cistern chambers (wade, freeze to cross, or swim), Snig's goblin tunnels branch, jail drain junction, and a gate to the priory end (E26).
Scope
- In: Well and cistern scene, echo ledge, water volumes, junctions to jail drain, goblin tunnels and priory
- Out: Priory side of the cistern (e26-priory-greybox)
Notes
Sets fact well.tunnel.found (resolves rumour 4).
Acceptance criteria
- AC-1 integration Given any object dropped into the well, when it lands, then two landing noise events fire about 0.4 s apart.
- AC-2 integration Given the player reaches the cistern, when entered, then well.tunnel.found is set and rumour 4 resolves.
- AC-3 integration Given the player drops down the shaft instead of climbing, when landing, then fall damage applies but never kills from full health (edge).
- AC-4 integration Given the flooded chamber, when frozen by any cold stimulus, then it becomes walkable ice.
Waits on
- mw-e02.14 Wade and swim in water and slide on frozen surfaces done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e04.19 Fall, crush and hazard damage through the damage model done
- mw-e22.11 Add rumor system that points players to places and secrets done
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
area:contentepic:e24milestone:m3mw-e24.12Build a rooftop network across Briar Glen for thieves and climbers
Description
Why
Town infiltration and verticality: the thief should be able to cross town without touching the street.
What
As a thief, I want climbable drainpipes, ivy and connected roofs so that I can move above the watch.
Roof paths linking the Ox, the Stacks (third-floor window), the Forge chimney, the Watch House, and the rise to Vane House. Gaps jumpable, some requiring a plank (movable) or rope arrow. Archer overlooks; sorcerer Levitate/Blink shortcuts.
Scope
- In: Climbable surfaces, roof walk surfaces with acoustic tags, window entries, gap props
- Out: Climbing mechanics (e10-climbing, e02-climb-surfaces)
Notes
Roof tiles are noisy (acoustic tag loose-tile) in places — a deliberate risk.
Acceptance criteria
- AC-1 integration Given the rooftop replay, when a thief crosses from the Ox to Vane House, then they never touch street navmesh.
- AC-2 integration Given a loose-tile roof section, when walked at run speed, then footstep noise loudness exceeds that of slates by >= 50%.
- AC-3 integration Given a player falls from a roof, when landing on the street, then fall damage is applied and the watch reacts only if in sight (edge).
Waits on
- mw-e02.12 Mantle onto and grab ledges detected from world geometry done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
area:contentclass:thiefepic:e24milestone:m3mw-e24.14Stage the first-night sleepwalk from the Ox to the mine gate
Description
Why
The first night is the tutorial and the hook: follow Tansy, fail gracefully, see the horned shadow.
What
As a player, I want to follow a sleepwalking child through the silent town so that I learn to move, sneak and interact while the mystery lands.
Night 1 is held: a rest veto (e27-rest-points veto registry) blocks resting until the beat resolves. Tansy's path: bakery → Market Green → mine road → Briarwood edge → mine gate. She cannot be woken (shaking raises her hum). Wolves let her pass. Tutorial prompts along the path. The beat always ends with the horned shape lifting her at the gate — even if the player falls behind (fallback trigger), gets lost, or is blocked.
Scope
- In: Tansy sleepwalk path, rest veto while night 1 is active, tutorial prompt markers, gate cinematic trigger, fallback triggers
- Out: Text (e39-intro-endings-text)
- Out: Quest graph (e39-main-quest-data)
Notes
Spans town and Briarwood edge; the gate exterior itself is e25-mine-gate-approach.
Acceptance criteria
- AC-1 integration Given the player follows Tansy, when she reaches the gate, then the horned-shape beat plays and fact night1.shadowSeen is set.
- AC-2 integration Given the player loses Tansy for 60 s, when the fallback fires, then the beat still resolves with the shadow seen from the nearest vantage (edge).
- AC-3 integration Given the player shakes Tansy, when interacted, then her hum loudness rises and she does not wake.
- AC-4 e2e Given a new game, when the first-night replay runs, then morning begins at the Ox with night1.shadowSeen set.
Waits on
- mw-e13.8 Add silent Briar Wolf packs that can be fed, scared or remembered open
- mw-e24.4 Build the Sleeping Ox inn: common room, rooms, cellar and rest open
- mw-e25.10 Build the boarded mine gate with every class's way in open
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:contentepic:e24milestone:m3mw-e24.17Author class routes for Briar Glen's obstacles
Description
Why
Town obstacles (Vane House, restricted shelf, jail, Watch House records) should open differently per class.
What
As a player of any class, I want my class to open town obstacles its own way so that town reflects my fantasy.
First-pass routes from the route matrix: knight (persuade via rank, force the coal-chute grate), archer (rope arrow to balcony, shoot the gate latch), sorcerer (Mage Hand the study key, Levitate to window, trade favours with Mirela), thief (lockpick, rooftops, pickpocket keys). Declared in the obstacle registry.
Scope
- In: Route affordances in town scenes, registry entries, replays for each route
- Out: Gap closing after audit (e16-gap-pass-surface)
Notes
Uses the route matrix ids verbatim.
Acceptance criteria
- AC-1 content Given the registry, when validated, then every E24 obstacle has >= 3 class routes declared.
- AC-2 integration Given each declared route, when replayed, then the obstacle exit fact is set.
- AC-3 integration Given a route that relies on an NPC schedule window, when attempted outside the window, then an alternative route for that class still exists (edge).
Waits on
- mw-e16.2 Declare obstacles and class routes as validated content data open
- mw-e24.12 Build a rooftop network across Briar Glen for thieves and climbers open
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
- mw-e24.8 Build the Watch House and a jail cell whose drain leads out open
- mw-e24.9 Build Vane House as an infiltration target with strongbox and ledger open
area:contentclass:allepic:e24milestone:m3mw-e24.18Art-dress Briar Glen with final environment assets
Description
Why
The lights are on in town: warmth and hedgerow charm must read at a glance.
What
As a player, I want Briar Glen to look like a crooked, bookish mining town so that it feels like a place worth saving.
Replace grey-box with approved modular town kit, props and textures; keep collision, navmesh and gameplay entity ids unchanged.
Scope
- In: Swap grey-box to final meshes/materials, prop dressing, signage, decals
- Out: Asset creation (E37 prompt/generate/integrate beads)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item.
Acceptance criteria
- AC-1 integration Given the dressed town, when the e2e town tour and all town route replays run, then they pass unchanged.
- AC-2 perf Given the dressed town at Market Green, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) and draw calls <= 1500.
- AC-3 manual Given the art review, when compared to the style bible, then no building uses colours outside the palette (edge: consistency).
Waits on
- mw-e24.13 Place Briar Glen's NPCs on day/night schedules in progress
- mw-e24.17 Author class routes for Briar Glen's obstacles open
- mw-e24.3 Place systemic objects and hiding spots throughout Briar Glen open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.14 Legibility kit: visual cues for every exploitable world property open
- mw-e37.15 Build the style-target diorama as the visual and performance benchmark open
- mw-e37.246 Integrate approved Briar Glen environment concepts open
- mw-e37.266 Integrate approved Briar Glen tileables and trims open
- mw-e37.298 Integrate approved Briar Glen exterior props open
- mw-e37.302 Integrate approved Briar Glen interior props open
- mw-e37.328 Integrate approved Briar Glen story props open
- mw-e37.352 Integrate approved world prop gap-fill open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artepic:e24milestone:m3mw-e24.19Light and score Briar Glen for day, dusk and night
Description
Why
Every warm room has one thing that makes you uneasy; lighting and ambience carry that.
What
As a player, I want town days to sound busy and nights to go quiet so that the sleepwalking dread lands.
Lighting presets per phase, lit windows by schedule, lamplighter relighting; ambience beds (market day, night crickets, forge hammering near the forge, inn murmur); music cues for town day/night/first night; reverb zones for interiors.
Scope
- In: Lighting setup, ambience zones, music cue bindings, reverb zones
- Out: Audio assets (E38)
- Out: Adaptive music engine (e28-adaptive-music)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item. Related: e28-ambience, e28-reverb-zones and e29-environment-fx are P2 in the combat plan; bind ambience beds, reverb zones and fog/firefly FX here when they land, otherwise ship lighting + music only.
Acceptance criteria
- AC-1 integration Given the clock crosses dusk, when the phase event fires, then the lighting preset blends to night within 5 s and music switches from the day to the night cue.
- AC-2 integration Given night, when sampling light at Market Green away from lamps, then the level is dark enough for thief visibility < 0.3 while crouched (edge: stealth must remain viable).
- AC-3 perf Given night with all lamps lit, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e24.18 Art-dress Briar Glen with final environment assets open
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e37.282 Integrate approved location skies and backdrops open
- mw-e38.108 Integrate approved Briar Glen music family into adaptive music open
- mw-e38.136 Integrate approved Briar Glen and wilderness ambience open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:audioepic:e24milestone:m3mw-e24.6Author the Stacks' restricted shelf and forbidden back room
Description
Why
Some books are expensive, forbidden, hidden or stolen; Mirela's curtain and back room are where the sorcerer's best finds live.
What
As a sorcerer, I want a restricted shelf that opens when Mirela trusts me and a back room holding a book she will never sell so that trust and theft both matter.
Restricted shelf (Mirror Self, Gravity Well and other restricted schools — writ or Trusted disposition), back room with Nine Sleeps on a locked lectern (never for sale; readable; stealable, teaching Lull), Court-seal markings, and Mirela's reaction facts.
Scope
- In: Secret/restricted stock data, back-room scene dressing, Nine Sleeps placement, reaction facts
- Out: Public stock (e24-lamplit-stacks)
- Out: Forbidden-book side effects (e07-forbidden-books)
Notes
Legality per story bible §3.3. Rumour 7 resolves on first restricted-shelf reveal.
Acceptance criteria
- AC-1 integration Given disposition below Trusted and no writ, when viewing the restricted shelf, then its stock is hidden; at Trusted it appears and rumour 7 resolves.
- AC-2 integration Given Nine Sleeps, when the player attempts to buy it, then it is not for sale; when stolen, then theft is recorded and Mirela reacts on the next visit (edge).
- AC-3 content Given the secret stock data, when pnpm validate:bookshops runs, then every restricted book carries the restricted legality tag and no forbidden book is purchasable.
Waits on
- mw-e21.4 Reveal secret back-room stock through dialogue, trust or discovery open
- mw-e21.6 Handle stolen and forbidden books in bookshop trade open
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
area:contentclass:sorcererepic:e24milestone:m3mw-e24.11Build the lychgate cemetery and the empty schoolhouse
Description
Why
Quiet places carry the story: Vesperine graves and a teacher's empty desk.
What
As a player, I want to find lore in the cemetery and schoolhouse so that the town's history and its missing people feel real.
Cemetery: lychgate, Vesperine graves with epitaphs (Hornfolk ear-covering motif on the oldest), Mooring family plot, a fresh unmarked grave (false alarm, humour beat). Schoolhouse: Mina Coles' desk, the skipping rhyme on the slate, Tansy's reading corner, a torn page from The Ox Beneath the Hill.
Scope
- In: Two small scenes/areas, readable epitaphs and slate, lore item placements
- Out: Text content (e39-books-lore)
Notes
The torn last page links to Tansy's copy of the book (lore payoff).
Acceptance criteria
- AC-1 content Given both areas, when validated, then every readable references an existing text id.
- AC-2 integration Given the player reads the slate rhyme, when complete, then fact lore.rhyme.read is set (used by Mirela dialogue).
- AC-3 integration Given night, when the player enters the cemetery, then no hostile creature spawns there (tone rule: the lights are on in town, edge).
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e39.13 Write in-game books, notes and inscriptions done
area:contentepic:e24milestone:m3mw-e24.15Place the rumour set on sources and wire targets to real facts
Description
Why
"NPC rumors can lead somewhere." Each of the twelve rumours must point at something placed.
What
As a player, I want rumours from Dot, townsfolk and goblins to lead to real places so that talking becomes exploring.
Bind e39 rumour data to sources (Dot, townsfolk tags, goblins) and ensure each rumour's resolvesWhen fact is produced by placed content (well echo, Mooring armour, stamped trinkets, cracked bell, ledger, restricted shelf, Hale's boots, millpond lantern, the Fold, Clatter, the Standing Horn).
Scope
- In: Source bindings, fact producers placed where missing, validation run
- Out: Rumour text (e39-rumor-set)
- Out: Rumour runtime (e22-rumors)
Notes
Rumours 10–11 come from goblins (E26 camp).
Acceptance criteria
- AC-1 content Given the rumour set, when validated, then all 12 rumours have >= 1 source and a produced resolvesWhen fact (no warnings).
- AC-2 integration Given rumour 4 heard and the cistern entered, when evaluated, then it resolves as Investigated.
- AC-3 integration Given the false rumour 3 (goblins took them), when the Framed in Brass proof is found, then it resolves with a "false" journal note (edge).
Waits on
- mw-e22.11 Add rumor system that points players to places and secrets done
- mw-e24.10 Hide the cistern route under the Glen Well open
- mw-e39.12 Write the rumour set with sources, truth values and targets done
area:contentepic:e24milestone:m3mw-e24.16Place Briar Glen's secrets, lore books and hidden treasure
Description
Why
Interesting-looking places should usually contain something interesting; every bookshelf has a book.
What
As a curious player, I want secrets in town — stashes, notes, books — so that exploring town pays off.
Placement: every bookshelf gets a book, Dot's stamped trinket in the cellar, Hale's dusty boots (Captain's Boots clue), the Pell family Bible, the "friend below" note under the player's door, a thief stash on the Forge roof, a chimney cache, and 6–10 small treasures per the e18 treasure guide.
Scope
- In: Lore/treasure placements, discovery rules, fact ids
- Out: Text (e39-books-lore)
- Out: Loot tables design (e18-treasure-design-guide)
Notes
Density target from e18-treasure-design-guide.
Acceptance criteria
- AC-1 content Given the town scenes, when audited, then every bookshelf entity has >= 1 book placed.
- AC-2 integration Given the player morning 1, when they leave the room, then the "friend below" note is at the door and readable.
- AC-3 integration Given a hidden stash taken, when saved and reloaded, then it stays empty (edge).
Waits on
- mw-e18.5 Support designer-placed and hidden treasure with discovery rules open
- mw-e18.9 Write treasure design guide for handcrafted loot open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e39.13 Write in-game books, notes and inscriptions done
area:contentepic:e24milestone:m3
M4 Fun validation 1
mw-e24.20Perf-pass and playtest Briar Glen on both presets
Description
Why
The town is the densest scene (NPCs, lights, props); it is the likeliest perf failure.
What
Run the perf suite on the town (day/night, full schedules, a street fire) on reference and low-tier machines; run a 5-tester town playtest (can they find the Stacks, the well, Vane House entries unprompted?). Record in docs/playtests/briar-glen.md; file beads.
Scope
- In: Perf captures, fixes within budget, playtest session and findings
- Out: Global perf work (E32)
Notes
Related: e32-reference-runbook.
Acceptance criteria
- AC-1 perf Given Market Green at midday with full schedules, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-2 perf Given the same scene on the Low preset, when measured, then p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat.
- AC-3 perf Given a warm cache, when entering town from the Briarwood, then the transition completes in <= 3 s and heap stays <= 1.5 GB.
- AC-4 manual Given 5 testers, when asked to find the bookshop, the well and a way into Vane House, then >= 4 find all three without help within 20 minutes.
Waits on
- mw-e24.19 Light and score Briar Glen for day, dusk and night open
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
- mw-e32.6 Implement Low/High graphics presets and dynamic resolution open
area:perfepic:e24milestone:m4
E25Wilderness RegionThe Briarwood, Mooring's Watch, the River Wend and the Deepworks' exterior approach form an interconnected wilderness around Briar Glen: the Sleepers' Path, the Standing Horn, the wolf den, the woodcutter's hut, the millpond and drowned lane, the watchtower with its signal mirror and supply tunnel, and the boarded mine gate, plus the river (Briar Glen Quay and the Stone Stair, the Millweir sluice walk, the Weeping Adit into the lower mine, the towpath and Kestrel Lock, and barge travel to Wendmouth) — with loops and shortcuts unlocked by abilities.4/26 done · 15%
M3 MVP content 21
mw-e25.2Reveal the Sleepers' Path at night or to a tracker's eye
Description
Why
The trail of trampled ferns is the forest's mystery and the archer's signature clue.
What
As an archer, I want to see the Sleepers' Path when others cannot so that my tracking fantasy pays off.
A fern-trail decal set visible when time.phase is night (any class, low contrast) or always to players with sense.tracking (archer), or with Arcane Sight / a lantern held low. Leads from town to the mine gate with a branch to the priory (sets path.found; unlocks Captain's Boots shortcut knowledge).
Scope
- In: Trail decal entities with visibility conditions, branch to priory, fact wiring
- Out: Tracking capability definition (e19-archer-arrow-channel / class data)
Notes
If sense.tracking does not exist in class data, raise a bead with systems; fallback is night-only visibility.
Acceptance criteria
- AC-1 integration Given day and a non-archer, when looking at the trail, then it is not rendered; at night it is rendered at low contrast.
- AC-2 integration Given an archer with sense.tracking at noon, when looking at the trail, then it is rendered at full contrast.
- AC-3 integration Given the player follows the branch to the priory, when reached, then path.found is set and the priory shortcut is known on the map (edge: sets once).
Waits on
- mw-e19.2 Create capability registry with source-tracked grants done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:contentclass:archerepic:e25milestone:m3mw-e25.4Build the wolf den encounter with feed, scare, sneak and fight routes
Description
Why
The silent pack is the first creature encounter with real options.
What
As a player, I want to deal with the Briar Wolf pack my own way so that my first wilderness threat has choices.
Den hollow with 4–5 wolves, carcass bait spot, rocky overlooks (archer), bramble cover (thief), a fallen log bridge (knight shoves to block a flank), fire pit (scare). Juniper's hunting snare cache nearby. Pack-memory facts drive later reactions.
Scope
- In: Encounter placement, overlooks, cover, bait spot, snare cache
- Out: Wolf behaviour (e13-briar-wolves)
Notes
Resolves rumour 1 when observing wolves ignoring a sleepwalker (first night or later).
Acceptance criteria
- AC-1 integration Given the player drops meat at the bait spot, when the pack eats, then the den path can be crossed without combat for 120 s.
- AC-2 integration Given a thief crouched in bramble cover at night, when crossing, then no wolf exceeds Suspicious in the scenario replay.
- AC-3 integration Given the pack is killed, when the player returns in the "Vespers Rung" ending, then the den is empty rather than respawned (edge: pack memory).
Waits on
- mw-e13.8 Add silent Briar Wolf packs that can be fed, scared or remembered open
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
area:contentepic:e25milestone:m3mw-e25.5Build the millpond and drowned lane with a Hushling and a lost lantern
Description
Why
"Something at the millpond sings on still nights. Not a bird." True, and dangerous.
What
As a player, I want a flooded lane haunted at night by a voice-mimicking Hushling so that the wilderness foreshadows the Knot.
Millpond with a sluice (open/close changes the lane's water level), the drowned lane (wade, freeze, or drain it), a sleepwalker's lost lantern (item and clue), a single night-only Hushling that mimics one of the missing, and the woodcutter's empty hut nearby (safe rest spot, axe, lore).
Scope
- In: Pond and lane scene, sluice mechanism, water level states, night-only Hushling spawn, lantern item, woodcutter's hut
- Out: Hushling behaviour (e13-hushlings)
Notes
Resolves rumour 9. The woodcutter's hut is folded in here as the nearby safe spot.
Acceptance criteria
- AC-1 integration Given the sluice is opened, when simulated, then the lane water level drops to wading depth within 20 s.
- AC-2 integration Given day, when the lane is entered, then no Hushling is present; at night it spawns and mimics one of the missing by name.
- AC-3 integration Given the player rings any loud noise source >= 0.85 near it, when propagated, then the Hushling dissipates and does not respawn until the next night (edge).
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:contentepic:e25milestone:m3mw-e25.7Place the Hollow Sentinels and goblin lookout with class resolutions
Description
Why
The Watch Still Stands: watchword, formal relief, bell cord, dispel, stealth or smash.
What
As a player of any class, I want to end the watch my class's way so that the tower becomes my safe base.
Sentinels on armour stands named in the keeper's log; goblin lookout (2–3 goblins with an alarm pot) below. Resolutions wired as facts: watchword from the keeper's log (any), knight formal relief, archer shoots the bell cord (watch.ended), sorcerer dispel/overload binding, thief sneaks to the roof, smash. Reward: tower as safe base (rest point), Sergeant Mooring's watch-key, closing line in the log.
Scope
- In: Creature placement, bell cord shootable, log item, safe-base conversion, rewards
- Out: Quest graph (e39-side-quests-wilds)
- Out: Sentinel behaviour (e13-hollow-sentinels)
Notes
Resolves rumour 2.
Acceptance criteria
- AC-1 integration Given the bell cord hit by any projectile or cut, when it rings, then watch.ended is set and all sentinels stand down.
- AC-2 integration Given the watchword read in the log, when spoken to a sentinel, then that sentinel and its peers stand down.
- AC-3 integration Given the goblin lookout raises the alarm, when the sentinels hear it, then they do not leave the tower (edge: leash holds).
- AC-4 integration Given watch.ended, when the player rests in the keeper's quarters, then it acts as a rest point and respawn location for the region.
Waits on
- mw-e05.13 Shootable triggers and cuttable ropes activate mechanisms done
- mw-e13.12 Add Hollow Sentinels bound to hold the tower until relieved open
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:contentclass:allepic:e25milestone:m3mw-e25.8Turn the signal mirror to light the priory's hidden choir door
Description
Why
Redirecting light across a valley is the constitution's puzzle promise at landscape scale.
What
As a player, I want to rotate the tower's signal mirror at the right time of day so that a beam reveals a hidden door at the priory.
Rotatable mirror prefab on the roof; at day or with a light source/spell at night, the beam aligned to the priory marker sets priory.choirDoor.revealed. Alternatives: Prism Ray from the priory side, the Arcane Sight reveal, or finding the choir door lever from inside.
Scope
- In: Mirror prefab placement, long-range beam target, cross-scene fact, alternatives declared
- Out: Choir door itself (e26-priory-choir)
Notes
Beam is simulated in the tower scene and resolves to a fact; no cross-scene physics.
Acceptance criteria
- AC-1 integration Given sun phase day and the mirror rotated to the priory marker, when evaluated, then priory.choirDoor.revealed is set.
- AC-2 integration Given night and no light source on the mirror, when rotated to the marker, then nothing happens (edge).
- AC-3 content Given the puzzle file, when the solvability validator runs, then >= 2 distinct solutions are found.
Waits on
- mw-e03.17 Redirect light beams with mirrors and lenses onto receivers done
- mw-e15.4 Ship reusable puzzle prefabs built only from world components done
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:contentepic:e25milestone:m3mw-e25.9Make the collapsed supply tunnel a class-gated shortcut to the mine
Description
Why
The watchtower-to-mine loop is the "long way in" for any class and a shortcut once cleared.
What
As a player, I want to clear the collapsed tunnel under the tower so that I reach the upper Deepworks without Vane's gate.
Collapse with routes: knight clears rubble by hand (heavy push/lift), sorcerer Gravity Well or Telekinetic Throw removes key stones, thief squeezes through a gap (crawl), archer shoots a rotten prop to drop a section (then climb), any class with a pick item digs slowly. Clearing sets shortcut.supplyTunnel.
Scope
- In: Collapse entities, routes, fact wiring to E26 upper mine node
- Out: Mine interior beyond the collapse (e26-deepworks-greybox)
Notes
Boundary between E25 and E26 is the collapse itself.
Acceptance criteria
- AC-1 content Given the obstacle registry, when validated, then the supply tunnel declares >= 4 class routes.
- AC-2 integration Given the collapse cleared by any route, when evaluated, then shortcut.supplyTunnel is set and persists after reload.
- AC-3 integration Given the archer drops the rotten prop while standing under it, when it falls, then crush damage applies via environmental damage (edge).
Waits on
- mw-e02.18 Push and pull heavy objects such as crates and statues done
- mw-e03.11 Break fragile objects and smash weakened walls into debris done
- mw-e04.19 Fall, crush and hazard damage through the damage model done
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
area:contentepic:e25milestone:m3mw-e25.10Build the boarded mine gate with every class's way in
Description
Why
Getting in is the first big class-choice moment (bible §4.1 beat 5).
What
As a player of any class, I want several ways past Vane's sealed gate so that my class decides how I enter the Deepworks.
Mine gate exterior: Bell Line rule carved above ("No shaft below the Bell Line. The hill is kept."), padlocked gate (Vane's key or lockpick), rotted side-boarding (knight smash), winch pulley (archer shot drops the cage), inner bar (Mage Hand) and rope latch (Ember/any heat), ventilation chimney up the hillside (thief climb). Bell Line boundary stones where Hale's sleepwalk stops.
Scope
- In: Gate exterior scene, routes, readable carving, Bell Line stones, transition into E26
- Out: Winch house interior (e26-deepworks-greybox)
Notes
Target 4 classes + the long way (supply tunnel) + the river way (e25-weeping-adit, the sixth entry route, registered as its own obstacle).
Acceptance criteria
- AC-1 content Given the obstacle registry, when validated, then the mine gate declares routes for all four classes plus the key route.
- AC-2 integration Given the rope latch exposed to any heat stimulus, when it burns, then the gate unbars.
- AC-3 integration Given the knight smashes the side-boarding, when broken, then the gap persists after reload and fact mine.boardsBroken is set (Vane remarks).
- AC-4 integration Given the player opens the gate at night while a sleepwalker event is active, when entered, then the sleepwalker path is not blocked (edge).
Waits on
- mw-e04.17 Smash breakable walls and objects by impact force open
- mw-e05.13 Shootable triggers and cuttable ropes activate mechanisms done
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e17.5 Auto-use keys from the keyring on matching locks done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
area:contentclass:allepic:e25milestone:m3mw-e25.11Build Briar Glen Quay and the Stone Stair slipway down from the mine
Description
Why
The river is the town's second heart; the Stone Stair turns the mine's cutting hall and the quay into a loop.
What
As a player, I want a working quay below town and a stepped stone slipway from the Deepworks so that the river connects to the mine.
Briar Glen Quay: moorings for Mags Oakum's stone barge Patient Ann, dressed glenstone blocks stacked on timber cradles, bollards, a crane, a toll shed and the barge departure point. The Stone Stair: a stepped slipway for blocks from the Deepworks' old cutting hall (goblin camp) down to the quay — slide down one-way from the top (shortcut.stoneStair), climbable upward only by climbers or with a rope arrow anchor.
Scope
- In: Quay scene area, Stone Stair slipway with slide and climb routes, connectivity edges to town and the E26 cutting hall
- Out: Barge travel (e25-barge-travel)
- Out: Cutting hall interior (e26-deepworks-greybox)
Notes
Standing water only: the quay basin is an e03 water volume (wading, swimming, freezing); the river does not flow in the sim.
Acceptance criteria
- AC-1 integration Given the player at the top of the Stone Stair in the cutting hall, when they slide down, then they arrive at the quay within 10 s and shortcut.stoneStair is set.
- AC-2 integration Given a non-climbing player at the quay, when trying to go up the slipway, then no route exists until a climb or rope-arrow capability is used (edge).
- AC-3 integration Given the quay basin, when any cold stimulus freezes it, then it becomes walkable ice via e03 rules.
- AC-4 content Given connectivity.json, when validated, then quay ↔ town and stone-stair (one-way) edges exist with their unlock conditions.
Waits on
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
area:contentepic:e25milestone:m3mw-e25.12Build the Millweir and its sluice walk to the Briarwood far bank
Description
Why
The Vesperine-built weir is a river loop: crossing it opens the forest's far bank.
What
As a player, I want to cross the Millweir on its sluice walk so that I reach the Briarwood's far bank without the long road.
Weir below town with a narrow sluice walk; sluice gates set the standing-water level above and below the weir (two states); the walk is slippery when wet and gains grip when dry; a gap mid-walk needs a plank, a jump, Blink or a rope anchor. Crossing sets shortcut.millweir.
Scope
- In: Weir scene area, sluice gate mechanism with two water-level states, walk traversal with gap, connectivity edge
- Out: Flowing-water simulation (not planned — water is standing volumes only)
Notes
Falling off the walk drops the player into the pool below, never kills from full health.
Acceptance criteria
- AC-1 integration Given the sluice gate closed, when toggled open, then the upper pool level drops to its low state within 20 s and a submerged stepping stone path is exposed.
- AC-2 integration Given the player crosses the walk by any route, when they reach the far bank, then shortcut.millweir is set and persists after reload.
- AC-3 integration Given the player falls from the walk, when landing in the pool, then they take no fall damage and can swim to the bank (edge).
Waits on
- mw-e02.14 Wade and swim in water and slide on frozen surfaces done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
area:contentepic:e25milestone:m3mw-e25.13Build the Weeping Adit: the flooded sixth way into the lower Deepworks
Description
Why
The river's source is also the mine's back door; going up the Adit is the sixth entry route (bible §4.1 beat 5).
What
As a player of any class, I want to go up the flooded drainage tunnel where the Wend springs so that I reach the lower Deepworks without Vane's gate.
Adit mouth on the hillside (humming audible at night — rumour 16), a flooded tunnel of standing-water chambers with a push-current volume (external impulse, not a flow sim) at the narrows. Routes: swim with air pockets; freeze the current's edge into an ice ledge (any cold stimulus); ride in on an empty stone barge being winched back up (winch mechanism, timed by Mags' schedule); climb the dry drainage ledge (climbers). Emerges in the Deepworks' flooded lower gallery (E26).
Scope
- In: Adit mouth and tunnel scene, push-current volume, air pockets, winched-barge ride, ice-ledge route, transition to the E26 flooded gallery
- Out: Lower Deepworks interior (e26-deepworks-greybox)
- Out: Mags' dialogue about the Adit (e39-dialogue-harbor-folk)
Notes
Registered as its own mine-entry obstacle so the E16 audit counts the Adit separately from the gate. Resolves rumour 16. Buoyancy (e03-buoyancy, P2) is not required: the barge is a mechanism on a winch path.
Acceptance criteria
- AC-1 integration Given the push-current volume at the narrows, when a swimming player enters it, then they are pushed back toward the mouth unless they hold to a ledge or the edge is frozen.
- AC-2 integration Given any cold stimulus on the current's edge, when frozen, then an ice ledge forms and the narrows can be walked for as long as it stays frozen.
- AC-3 integration Given the winch is running and the player rides the empty barge, when it reaches the top, then the player arrives in the flooded lower gallery and mine.entry.adit is set.
- AC-4 content Given the obstacle registry, when validated, then the Adit declares >= 3 class routes plus an any-class swim route.
- AC-5 integration Given the player runs out of air in the tunnel, when drowning damage would kill them, then they are washed out to the Adit mouth instead (edge: failure moves them, not a reload).
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e02.14 Wade and swim in water and slide on frozen surfaces done
- mw-e02.15 Apply knockback, launches and fall damage to the player controller done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
area:contentclass:allepic:e25milestone:m3mw-e25.14Build the towpath and Kestrel Lock with the keeper's cottage
Description
Why
The towpath is the always-open road to Wendmouth; Kestrel Lock is its halfway landmark and gossip post.
What
As a player, I want to walk the towpath down the valley past Kestrel Lock so that I can reach Wendmouth on foot and meet Old Reed.
A compressed towpath route in two legs (Briar Glen Quay → Kestrel Lock → Wendmouth outskirts); each leg transition advances the world clock (6 game hours per leg). Kestrel Lock: two gates and a lock chamber whose standing-water level is raised or lowered by paddles (two states), a lock beam walk, the toll board, and Old Reed's cottage (toll-in-gossip rumour source, rest point).
Scope
- In: Two towpath legs with transitions and time advance, lock chamber with two water states, paddles, cottage interior, rest point
- Out: Barge travel (e25-barge-travel)
- Out: Old Reed dialogue (e39-dialogue-harbor-folk)
- Out: Toll board text (e39-harbor-rumors-texts)
Notes
Distances are compressed; the fiction is "a day downriver". Time advance keeps the Tern's Act III departure unaffected (it is quest-gated, not clock-gated).
Acceptance criteria
- AC-1 integration Given the player completes a towpath leg transition, when they arrive, then game time has advanced by exactly 6 hours via advanceTime.
- AC-2 integration Given the lock paddles are opened, when simulated, then the chamber moves to the other water-level state within 30 s and the lower gate can be opened only when levels match.
- AC-3 integration Given the player tries to open a lock gate against unequal levels, when interacted, then it refuses with a visible strain cue and no state change (edge).
- AC-4 perf Given the lock with the cottage and towpath foliage in view, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
area:contentepic:e25milestone:m3mw-e25.15Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth
Description
Why
Wendmouth must be quick to reach from the morning after the first night; a narrated barge ride is the fast way.
What
As a player, I want to pay Mags for passage on the Patient Ann so that I travel downriver in one narrated ride instead of walking.
Travel service at Briar Glen Quay, Kestrel Lock and the Wendmouth Stone Wharf: departs mornings only (refuses at night: "not with whatever's singing up there"); downriver advances 1 game day, upriver 2 (towed); a short narrated ride card with class-flavoured Mags lines; optional stowaway under the tarpaulin (thief, for a fee) that skips the customs inspection on arrival. Available from the morning after night 1.
Scope
- In: Travel service data and runtime (fare, schedule window, direction durations), ride card UI hook, stowaway flag, arrival spawn points
- Out: Flowing-water or boat-handling simulation (not planned)
- Out: Ride text (e39-dialogue-harbor-folk)
- Out: Customs inspection (e40-customs-house)
Notes
No other plan owns fast travel; this is deliberately a narrow travel-service item, reusable later for other routes.
Acceptance criteria
- AC-1 unit Given the player at the quay at 08:00 on day 2 with the fare, when they board downriver, then they arrive at the Wendmouth Stone Wharf at 08:00 on day 3 and the fare is deducted.
- AC-2 unit Given an upriver trip, when taken, then exactly 2 game days elapse and time events fire in order via advanceTime.
- AC-3 integration Given night or night 1 not yet resolved, when the player asks for passage, then Mags refuses and no time passes (edge).
- AC-4 integration Given the stowaway option taken, when arriving at Wendmouth, then the customs inspection trigger is skipped for that arrival only.
- AC-5 e2e Given a save at the quay, when the player rides down and back, then both arrivals load in <= 3 s warm with zero console errors.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e20.9 Add purchasable services as meaningful gold sinks open
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
area:gameepic:e25milestone:m3mw-e25.16Art-dress, light and score the River Wend locations
Description
Why
The river should read as the valley's working artery: tarred wood, brown water, herons and stone barges loaded with pale glenstone.
What
As a player, I want the quay, Millweir, Adit, towpath and Kestrel Lock to look and sound like the story bible so that the river has identity.
Final environment kit and props for the river locations, water surface materials for standing-water volumes, lighting per time-of-day, the river music cue and river ambience bindings; gameplay ids and collision unchanged.
Scope
- In: Art swap for five river locations, water material presets, lighting, music/ambience bindings
- Out: Asset creation (E37/E38)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item. Related: e28-ambience, e28-reverb-zones and e29-environment-fx are P2 in the combat plan; bind ambience beds, reverb zones and fog/firefly FX here when they land, otherwise ship lighting + music only.
Acceptance criteria
- AC-1 integration Given the dressed river locations, when all E25 river replays and route replays run, then they pass unchanged.
- AC-2 integration Given the player enters any river location, when crossing its trigger, then the river music cue plays within 3 s.
- AC-3 perf Given the quay with the barge, crane and water surface in view, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) and p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat (edge: water shader on Low).
Waits on
- mw-e25.12 Build the Millweir and its sluice walk to the Briarwood far bank open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.146 Integrate approved River Wend environment concepts open
- mw-e37.154 Integrate approved River Wend tileables open
- mw-e37.196 Integrate approved river quay, weir and lock props open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e38.68 Integrate approved River Wend and barge music into adaptive music open
- mw-e38.80 Integrate approved River Wend ambience open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:artepic:e25milestone:m3mw-e25.17Author class routes and ability-gated shortcuts across the wilderness
Description
Why
Paths loop together and reveal shortcuts as players acquire abilities.
What
As a player of any class, I want new abilities to open new wilderness shortcuts so that returning to the forest feels different.
Shortcuts: rope-arrow anchor up the priory crag, the Millweir gap (plank/jump/Blink/rope), climbing the Stone Stair upward, frozen millpond crossing, bramble burn-through to the tower spur, a climbable ravine to the mine hillside, a Blink gap over the drowned lane. Registry entries and replays.
Scope
- In: Shortcut affordances, registry entries, replays, connectivity updates
- Out: Gap closing after audit (e16-gap-pass-surface)
Notes
Each shortcut is one-way until opened from the far side where sensible.
Acceptance criteria
- AC-1 content Given the registry, when validated, then every E25 obstacle has >= 3 class routes.
- AC-2 integration Given each shortcut, when opened, then its fact persists and connectivity reachability updates.
- AC-3 integration Given a frozen-pond crossing, when the ice melts while the player is on it, then the player drops into water and can swim out (edge).
Waits on
- mw-e05.10 Rope arrows anchor into wood and create climbable ropes open
- mw-e16.2 Declare obstacles and class routes as validated content data open
- mw-e25.10 Build the boarded mine gate with every class's way in open
- mw-e25.12 Build the Millweir and its sluice walk to the Briarwood far bank open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.5 Build the millpond and drowned lane with a Hushling and a lost lantern open
- mw-e25.9 Make the collapsed supply tunnel a class-gated shortcut to the mine open
area:contentclass:allepic:e25milestone:m3mw-e25.19Art-dress the Briarwood and Mooring's Watch with final assets
Description
Why
The forest must feel old and thick — oak, holly, bramble — without hurting perf.
What
As a player, I want the Briarwood and the watchtower to look like the story bible describes so that the region has identity.
Foliage (instanced), terrain textures, ruined tower kit, props; keep collision and gameplay ids intact.
Scope
- In: Foliage/terrain/tower art swap, LODs, instancing setup
- Out: Asset creation (E37)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item.
Acceptance criteria
- AC-1 integration Given the dressed region, when all E25 route replays run, then they pass unchanged.
- AC-2 perf Given the densest forest view, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-3 perf Given the same view on Low, when measured, then p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat (edge: foliage density scales by preset).
Waits on
- mw-e25.17 Author class routes and ability-gated shortcuts across the wilderness open
- mw-e25.7 Place the Hollow Sentinels and goblin lookout with class resolutions open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.14 Legibility kit: visual cues for every exploitable world property open
- mw-e37.250 Integrate approved Briarwood and Mooring's Watch concepts open
- mw-e37.270 Integrate approved Briarwood and Mooring's Watch tileables open
- mw-e37.306 Integrate approved wilderness props open
- mw-e37.352 Integrate approved world prop gap-fill open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artepic:e25milestone:m3mw-e25.20Light and score the wilderness for day, night and still nights
Description
Why
The wolves' silence and the song on still nights are sound-design beats.
What
As a player, I want the forest to sound alive by day and wrong by night so that dread builds before the dungeon.
Lighting per phase with moonlight and fog; ambience beds (forest day, forest night without wolf howls, millpond water, tower wind); music cues for Briarwood explore and Mooring's Watch; fireflies and fog volumes.
Scope
- In: Lighting, ambience zones, music bindings, environment FX placement
- Out: Audio assets (E38)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item. Related: e28-ambience, e28-reverb-zones and e29-environment-fx are P2 in the combat plan; bind ambience beds, reverb zones and fog/firefly FX here when they land, otherwise ship lighting + music only.
Acceptance criteria
- AC-1 integration Given night in the Briarwood, when wolf vocal cues are queried, then no howl cue is playable while fact breach.sealed is false.
- AC-2 integration Given the player enters Mooring's Watch, when crossing the trigger, then the music crossfades to the tower cue within 3 s.
- AC-3 perf Given night with moonlight, lanterns and any bound environment FX, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) (edge: FX within budget).
Waits on
- mw-e25.19 Art-dress the Briarwood and Mooring's Watch with final assets open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e37.282 Integrate approved location skies and backdrops open
- mw-e38.112 Integrate approved wilderness music families into adaptive music open
- mw-e38.136 Integrate approved Briar Glen and wilderness ambience open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:audioepic:e25milestone:m3mw-e25.23Link the Miners' Bridge to the boarded mine gate
Description
Planned world-map edge miners-bridge (mw-e24.1): the bridge past the Bridge Watch post leads across the Wend to the Deepworks gate exterior (E25 owns the exterior, mw-e25.10 builds the gate).
Acceptance criteria
AC-1 integration Given the player at the town end of the bridge, when they cross it, then the mine gate exterior scene loads. AC-2 content edge miners-bridge lists its transitions in world:connectivity.
area:contentepic:e25milestone:m3mw-e25.24Link the riverbank under the bridge to Briar Glen Quay and the towpath
Description
Planned world-map edges under-the-bridge-quay, quay-millweir, millweir-kestrel-lock (mw-e24.1): the riverbank path from under-the-bridge continues downriver to the quay, the Millweir and the towpath to Kestrel Lock.
Acceptance criteria
AC-1 integration Given the player under the bridge, when they follow the riverbank, then the quay loads, and the towpath leads on to the Millweir and Kestrel Lock. AC-2 content the edges list their transitions.
area:contentepic:e25milestone:m3mw-e25.3Place the Standing Horn menhir and its readable inscription
Description
Why
Statues speak through posture; the menhir teaches the Hornfolk visual language early.
What
As a player, I want a Hornfolk stone with hands over its ears so that I start to understand the people who built the Knot.
Clearing with the menhir (hands over ears), an inscription band readable in part by any class and fully by a sorcerer holding Hornfolk fragments (reveals the first rule of the Knot), a hidden horn-coin under a root, and a quiet spot where the song is audible at night (hint of what is below).
Scope
- In: Menhir placement, inscription readable with conditions, hidden coin, night audio emitter
- Out: Inscription text (e39-books-lore)
Notes
Resolves rumour 12.
Acceptance criteria
- AC-1 integration Given a sorcerer with the Hornfolk fragment item, when reading, then the full inscription shows and lore.knot.rule1 is set.
- AC-2 integration Given a non-sorcerer, when reading, then a partial translation shows and no fact is set (edge).
- AC-3 integration Given the player reaches the clearing, when entered, then rumour 12 resolves.
Waits on
- mw-e07.5 Translate books written in unknown languages open
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
area:contentepic:e25milestone:m3mw-e25.18Place wilderness and river secrets, lore caches and treasure
Description
Why
The world rewards curiosity: a hollow tree, a drowned cart, a goblin stash.
What
As a curious player, I want hidden things in the Briarwood and the tower so that detours pay.
Placement: Tansy's hair ribbon on the mine rail, the keeper's log pages, a goblin stash in a hollow oak, a drowned cart in the lane, a Juniper snare cache, a spellbook page in the woodcutter's hut, Mags' split-plank horn-coin at the quay, the Kestrel Lock toll board, a lost cargo crate in the lock chamber, and 8–12 small treasures.
Scope
- In: Placements, discovery rules, facts
- Out: Text (e39-books-lore, e39-item-creature-text)
Notes
Use e18 treasure density guidance.
Acceptance criteria
- AC-1 content Given the wilderness scenes, when audited, then every "interesting-looking" landmark tag has >= 1 placed discovery.
- AC-2 integration Given Tansy's ribbon picked up, when inspected, then its item text matches e39 text ids.
- AC-3 integration Given a cache looted, when saved and reloaded, then it remains empty (edge).
Waits on
- mw-e18.5 Support designer-placed and hidden treasure with discovery rules open
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
area:contentepic:e25milestone:m3mw-e25.26Enforce a data-level fire-spread cap for bramble walls (both leaves burning, 12 m)
Description
mw-e25.1 AC-2 (PR #413) caps Briarwood fire spread only through firebreak layout, tested with one torched gate leaf. Add a data-level spread cap (e.g. per-scene or per-material max spread radius from ignition origin) and an integration test with both leaves of a bramble gate burning and wind/adjacent flammables, proving nothing ignites beyond 12 m.
area:contentepic:e25milestone:m3
M4 Fun validation 1
mw-e25.21Perf-pass and playtest the wilderness on both presets
Description
Why
Foliage-heavy scenes are the classic browser perf trap.
What
Perf suite runs over Briarwood, Mooring's Watch, the quay, the Adit and Kestrel Lock on reference and low-tier machines; a 5-tester navigation playtest (can they find the tower, the den and the mine gate without a map?). Findings and beads in docs/playtests/wilderness.md.
Scope
- In: Perf captures and fixes, playtest, findings
- Out: Global perf work (E32)
Notes
Related: e32-reference-runbook.
Acceptance criteria
- AC-1 perf Given the densest Briarwood view, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-2 perf Given a wolf fight near fire on Low, when measured, then p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat.
- AC-3 manual Given 5 testers, when asked to reach Mooring's Watch and the mine gate, then >= 4 do so within 15 minutes without help.
Waits on
- mw-e25.16 Art-dress, light and score the River Wend locations open
- mw-e25.20 Light and score the wilderness for day, night and still nights open
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
- mw-e32.6 Implement Low/High graphics presets and dynamic resolution open
area:perfepic:e25milestone:m4
E26First DungeonOne exceptional multi-route dungeon network — the Deepworks mine, Bellwater Priory and the Knot labyrinth (Outer Ring, Ossuary Galleries, Chapel of Echoes, the Fold, Horn's Hall, Sounding Shaft, the Cradle) — combining combat, stealth, the six MVP puzzles, creatures and secrets, ending in the Sounding Shaft climb and the ringing of the Vesper Bell, with level states for all four endings.5/28 done · 18%
M3 MVP content 21
mw-e26.3Populate the Deepworks: goblin camp, miner Forgotten, mine hazards
Description
Why
The goblin camp can be fought, sneaked through or talked to — the space must support all three.
What
As a player, I want a goblin camp with lookouts, alarms and side passages so that I choose between talking, sneaking and fighting.
Camp layout (Kettleback's kettle throne, Duggan's crate, hammocks incl. Snig's with the primer, alarm pots), patrols, lookouts; miner Forgotten hauling stone below; systemic objects: oil lamps, mine carts (pushable, ride-able downhill), rotten supports (breakable collapse), spilled lamp-oil channels (flammable liquid), the sleepwalker scrawl "it's so pretty down here".
Scope
- In: Creature placement and patrols, props with material presets, alarm devices, side passages
- Out: Social behaviour (E14)
- Out: Goblin combat (e13-rootcellar-goblins)
Notes
Goblin rumours 10–11 are sourced here. Related: e03-gas-clouds (P2) — if it lands, add a firedamp pocket that explodes on ignition.
Acceptance criteria
- AC-1 integration Given a thief entering via the side vent at night, when the stealth replay runs, then Duggan's crate is reachable without any goblin reaching Alerted.
- AC-2 integration Given a spilled lamp-oil channel under a rotten support, when any heat source ignites the oil, then the fire spreads along the channel and the burnt support collapses.
- AC-3 integration Given a mine cart pushed downhill into goblins, when it hits, then knockback applies via physics impulse (not scripted).
- AC-4 integration Given every alarm pot is destroyed beforehand, when the camp is alerted, then goblins fall back to shouting (edge: alarm degraded, not broken).
Waits on
- mw-e03.13 Support hanging objects on ropes that burn, cut or snap done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e14.2 Make the Rootcellar clan talkable, bribable and forgiving of capture open
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
area:contentepic:e26milestone:m3mw-e26.4Build Vane's sealed gallery, the ledger and the sealable breach
Description
Why
The gallery holds the proof of Vane's lie; sealing the breach is the best outcome's key act.
What
As a player, I want to find Vane's ledger and the horn-ring and seal the breach so that I can expose him and restore the Knot's sentence.
Sealed gallery door (Vane key, pick, smash), gallery 9 carvings with pry marks, the Deepworks Ledger, the horn-ring pried from a carving, the breach. Sealing options (fact breach.sealed): collapse Vane's tunnel (break supports / Thunderclap), freeze and shore (frozen + timber), or goblins wall it (goblins.hired.breach).
Scope
- In: Gallery scene content, ledger and horn-ring items, three sealing routes
- Out: Framed in Brass quest (e39-side-quests-wilds)
Notes
Sealing should be possible only after horn.befriended (Help path) or explicitly allowed earlier with a warning — decide in e26-dungeon-layout.
Acceptance criteria
- AC-1 integration Given the supports broken by any force above threshold, when collapsed, then breach.sealed is set and the breach navmesh link is removed.
- AC-2 integration Given goblins.hired.breach, when the player returns, then the wall is built and breach.sealed is set.
- AC-3 integration Given the player is inside the breach when it collapses, when resolved, then they are pushed to the gallery side, never trapped (edge).
Waits on
- mw-e03.11 Break fragile objects and smash weakened walls into debris done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
area:contentepic:e26milestone:m3mw-e26.6Place the Watchers statue puzzle in the priory choir
Description
Why
Statues that do something; the choir puzzle reveals the pact and the Sounding Shaft.
What
As a player, I want to turn the singing-monk statues to face the altar so that the choir reveals its secrets.
Watchers puzzle from e15-reference-puzzles-b placed in the choir (rotate, levitate-rotate, or topple one — the destructive solution), rewarding the pact scroll and the abbot's letters cache. The hidden choir door (revealed by the signal mirror, or lever from inside) is a second entry.
Scope
- In: Puzzle instance, rewards, hidden door states
- Out: Puzzle mechanics (e15-reference-puzzles-b)
- Out: Mirror (e25-signal-mirror)
Notes
One of the six MVP puzzles.
Acceptance criteria
- AC-1 content Given the placed puzzle, when the solvability validator runs, then >= 2 distinct solutions pass and >= 3 classes can solve it.
- AC-2 integration Given a statue is toppled, when evaluated, then the puzzle remains solvable and no softlock is reported (edge).
- AC-3 integration Given priory.choirDoor.revealed, when the choir is approached from the garden, then the hidden door is open.
Waits on
- mw-e15.10 Build greybox reference puzzles for statues, darkness and sound done
- mw-e25.8 Turn the signal mirror to light the priory's hidden choir door open
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
area:contentepic:e26milestone:m3mw-e26.7Build the scriptorium with book mimics, tallow ooze and abbot letters
Description
Why
A library that bites: surviving pages, mimics and wax — and fire is both weapon and risk.
What
As a sorcerer, I want a ruined scriptorium full of pages, candles and dangers so that curiosity is rewarded and punished.
Book mimics among real shelves; Tallow Ooze in the candle store; the abbot's letters fragments; loose lore and spellbook pages; flammable paper everywhere (fire spreads fast here — burning the room destroys loot).
Scope
- In: Creature and item placement, flammable density design, page placements
- Out: Mimic and ooze behaviour (E13)
- Out: Letter text (e39-books-lore)
Notes
Fire destroying lore is a valid, story-creating failure; key lore has a second copy elsewhere. Related: e07-damaged-books (P2) — if it lands, some pages become missing pages of damaged books.
Acceptance criteria
- AC-1 integration Given a fire started in the scriptorium, when it burns out, then destroyed pages are recorded as facts and each key lore item has a surviving alternate source.
- AC-2 integration Given a book mimic, when the player feeds it a paper item, then it pacifies and yields a page.
- AC-3 integration Given the ooze melted by fire, when its puddle ignites, then fire spreads to adjacent flammable shelves (edge: systemic chain).
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e13.10 Add Tallow Ooze that melts into fire puddles and shatters when frozen open
- mw-e13.11 Add book, lectern and chest mimics with tells and feeding open
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
area:contentepic:e26milestone:m3mw-e26.8Build the belfry and the sour-bell consequence of early ringing
Description
Why
Ringing the bell without its tongue should create a story, not a no-op.
What
As a player, I want to be able to strike the cracked bell early so that I face the consequence and meet Horn.
Belfry climb, bell frame, bell ropes. Striking the tongueless bell with any heavy impact or loud force (hammer, mace, arrow, Thunderclap) plays a sour note: wakes every Forgotten in the priory, +1 song intensity for 5 minutes, Hushlings rise up the shaft, and Horn comes toward the sound (horn.heardBell).
Scope
- In: Belfry content, bell impact detection by force/noise, consequence wiring
- Out: The finale ring (e26-vesper-bell-finale)
Notes
This is a "failure creates stories" beat and a Horn-meeting route.
Acceptance criteria
- AC-1 integration Given the tongueless bell struck by any impact >= threshold, when resolved, then a sour-bell event fires and every priory Forgotten enters Searching.
- AC-2 integration Given the sour bell, when evaluated, then song.intensity rises by 1 for 300 s and horn.heardBell is set.
- AC-3 integration Given the bell struck repeatedly, when evaluated, then consequences apply once per 300 s window (edge: no stacking exploit).
Waits on
- mw-e03.25 Emit noise events from world interactions with material loudness done
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
area:contentepic:e26milestone:m3mw-e26.11Make the Knot loop unless you follow its runes
Description
Why
The first rule of the Knot — followed runes lead somewhere; ignored runes lead in circles — is the labyrinth's identity.
What
As a player, I want the labyrinth to quietly loop me back when I ignore the runes so that reading the walls becomes the way through.
Loop junctions: data-driven portal-free loops (corridor segments that reconnect) plus conditional junction doors that route by whether the player followed the rune sequence (tracked per junction). Loom-spider webs copying runes act as clues. Goblins pass freely (cannot read, never caught). Chalk marks from the "friend below" help early.
Scope
- In: Junction state logic, rune sequence data, web clue placements, chalk mark placements
- Out: Rune text/art (E37/E39)
Notes
Must never feel like a bug: loops show a recurring landmark (a specific carved face) so players notice.
Acceptance criteria
- AC-1 unit Given a junction expecting rune "horn-left" and the player takes the unmarked path, when crossing, then they are routed back to the loop start landmark.
- AC-2 integration Given the player follows the rune sequence through 3 junctions, when completed, then they reach the Ossuary entrance.
- AC-3 integration Given the player loops 3 times at one junction, when evaluated, then a diegetic hint (a web clue lit, a chalk mark) appears (edge: anti-frustration).
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
area:contentepic:e26milestone:m3mw-e26.12Populate the Outer Ring: spiders, first Forgotten, Horn sighting
Description
Why
Act II's first steps into the Knot must introduce its creatures and the warden at a distance.
What
As a player, I want my first minutes in the Knot to introduce spiders, the Forgotten and a glimpse of the minotaur so that I feel watched and curious.
Loom spider nests with web routes, the first sweeping Forgotten, a scripted distant sighting of Horn dragging a wrapped figure across a gallery, "friend below" notes and chalk arrows (genuinely helpful), systemic lights (braziers for scaring spiders).
Scope
- In: Creature placement, sighting trigger, notes and chalk placement, braziers
- Out: Note text (e39-dialogue-abbot)
Notes
The sighting must not be interactable at range (Horn exits before the player can reach him).
Acceptance criteria
- AC-1 integration Given the player crosses the sighting trigger, when fired, then Horn crosses the far gallery and exits within 8 s and fact horn.sighted is set.
- AC-2 integration Given a brazier lit near a spider nest, when light exceeds 0.7, then the nest's spiders flee and the web route can be burned.
- AC-3 integration Given the player shoots at Horn during the sighting, when hit, then he does not engage and still exits (edge).
Waits on
- mw-e13.13 Add Brother Horn's creature data: charges, grabs and throws to stop open
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e26.11 Make the Knot loop unless you follow its runes open
area:contentepic:e26milestone:m3mw-e26.13Populate the Ossuary: Forgotten habits, Clatter's garden, the psalter
Description
Why
Among the skeletons, one keeps apologising — the Ossuary is where the game's warmest quest begins.
What
As a player, I want galleries of praying and sweeping dead and one who remembers so that I learn the dead were people.
Monk and miner Forgotten on habit routines, bone piles for scatter/burn tactics, Clatter tending pale mushrooms in the "flower room", his psalter hidden among the niches, the Dark Crossing pit (placed in e26-dungeon-puzzles), holy-water font (consecrated).
Scope
- In: Creature/habit placement, Clatter spawn, psalter item, font, bone piles
- Out: Clatter logic (e14-clatter)
Notes
Resolves rumour 11.
Acceptance criteria
- AC-1 integration Given a crouched player humming the hymn, when crossing the Ossuary, then no Forgotten enters Combat at song intensity <= 2.
- AC-2 integration Given the psalter picked up, when inspected, then clatter.piece.psalter is set.
- AC-3 integration Given Clatter killed accidentally while hostile, when evaluated, then he reassembles unless bones burned, and the quest records clatter.harmed (edge).
Waits on
- mw-e13.3 Forgotten crumble and reassemble unless bones are scattered or burned open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
area:contentepic:e26milestone:m3mw-e26.14Build the Chapel of Echoes with the Chimes puzzle and portcullis trap
Description
Why
Sound misbehaves here: the chapel is the sound puzzle, a Hushling hunting ground and a Horn trap.
What
As a player, I want a chapel where echoes carry and misdirect so that sound becomes both clue and danger.
Chimes puzzle (e15-reference-puzzles-b) placed: reproduce a chime order heard behind a wall, shoot bells from the balcony, or read the order in a book. Acoustic trick: noise from certain points is re-emitted at mirror points (misdirects hunters). Hushlings patrol. Portcullis trap: lure Horn under it and drop it (horn.trapped) — the archer's single-shot resolution.
Scope
- In: Puzzle instance, echo emitters, Hushling placement, portcullis trap state
- Out: Echo propagation engine changes (E09) — use noise re-emitters only
Notes
One of the six MVP puzzles.
Acceptance criteria
- AC-1 content Given the Chimes instance, when validated, then >= 2 solutions pass and >= 3 classes can solve it.
- AC-2 integration Given a noise at an echo point, when propagated, then a copy is emitted at the mirror point and Hushlings investigate the mirror point.
- AC-3 integration Given Horn pursuing the player under the portcullis, when it drops, then horn.trapped is set and Horn cannot reach the Fold side.
- AC-4 integration Given the portcullis dropped with no one under it, when evaluated, then a winch on the far side can raise it (edge: no softlock).
Waits on
- mw-e13.14 Build the Warden boss encounter with yield, spare and Trial of Horns open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e15.10 Build greybox reference puzzles for statues, darkness and sound done
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
area:contentepic:e26milestone:m3mw-e26.15Place the Frozen Channel, Sunbeam, Scales and Dark Crossing puzzles
Description
Why
The MVP needs ~6 meaningful puzzles built from world systems; four are placed here (Watchers and Chimes in their rooms).
What
As a player, I want dungeon puzzles that use light, ice, weight and darkness so that the systems I have learned open the way.
Place from e15 references: Frozen Channel (Deepworks flooded gallery), Sunbeam (priory crypt stair, light down to a sensor), Scales (Outer Ring Hornfolk gate — crates, bodies, ice blocks), Dark Crossing (Ossuary pit — torches, light spell, braziers or smoke drafts). Each rethemed to its area with area props.
Scope
- In: 4 puzzle instances, retheming, solution replays, softlock policies
- Out: Puzzle kit (E15)
Notes
Zero new TypeScript: engine gaps become E03/E15 beads.
Acceptance criteria
- AC-1 content Given the 4 instances, when the solvability validator runs, then each has >= 2 distinct solutions.
- AC-2 integration Given e15-solution-audit, when run, then each is solvable by >= 3 classes at MVP capability levels.
- AC-3 integration Given each declared solution, when its replay runs, then the puzzle reaches solved.
- AC-4 integration Given the Scales gate solved with an ice block that later melts, when evaluated, then the gate stays open (edge: one-shot).
Waits on
- mw-e15.10 Build greybox reference puzzles for statues, darkness and sound done
- mw-e15.8 Report and enforce per-class solution coverage for puzzles in CI done
- mw-e15.9 Build greybox reference puzzles for light, freezing and weight done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
area:contentepic:e26milestone:m3mw-e26.16Stage Horn's Hall and the Fold, the reveal and the wake-up point
Description
Why
The reveal lands visually before anyone explains it: the missing asleep on moss beds, bread at their sides.
What
As a player, I want to enter the Fold and see the sleepers safe so that I understand Horn before he says a word.
Horn's Hall arena (pillars, bronze-wired horn racks, Horn's resting spot for the "rests one eye" trick), the Fold door (horn-key on Horn's belt; pickpocketable), the Fold: seven moss beds (six missing + Tansy after night 1), goblin bread, chalk tallies (sleepers and the player's rescues). Respawn rules: death in the Knot wakes here (horn alive); variants: goblin camp (Horn dead, goblins friendly), Forgotten laying-out (hostile goblins).
Scope
- In: Hall arena, Fold scene, sleeper placements, tallies updating from facts, respawn rules content
- Out: Horn encounter (e13-horn-encounter)
- Out: Sleeper wake-up (e26-vesper-bell-finale)
Notes
Chalk tallies read rescues.byHorn.
Acceptance criteria
- AC-1 integration Given the player enters the Fold, when inside, then all seven sleepers are present on beds and fact fold.seen is set.
- AC-2 integration Given the player dies in the Knot with horn alive, when resolved, then they wake on a Fold moss bed and the tally shows one more mark.
- AC-3 integration Given horn.killed and goblins Hostile, when the player dies in the Knot, then they wake among the Forgotten in the Ossuary laying-out niche (edge).
- AC-4 integration Given the horn-key is pickpocketed while Horn rests, when used, then the Fold opens without combat.
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e13.14 Build the Warden boss encounter with yield, spare and Trial of Horns open
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
area:contentepic:e26milestone:m3mw-e26.17Stage the Cradle arena for the abbot confrontation
Description
Why
The heart of the Knot, where the dream leaks into the physical, is the final stage.
What
As a player, I want a round chamber that bends toward its centre so that the finale feels dreamlike.
Place the four anchor lanterns (shootable, extinguishable), Hushling emergence points, Forgotten stirring positions, Tansy hostage position, Clatter's entrance, cover pillars, a balcony ring (archer), shadowed alcoves (thief), and the sorcerer's secret route arrival. Dream-leak zones (low gravity puddles / drifting objects) as world-property volumes.
Scope
- In: Arena placement, anchor lanterns, spawn points, dream-leak volumes
- Out: Abbot encounter logic (e13-abbot-encounter)
Notes
Dream-leak volumes use existing gravity/force properties only.
Acceptance criteria
- AC-1 integration Given the four anchor lanterns, when each is extinguished by any means, then the abbot's max health drops by 25%.
- AC-2 integration Given each class's Cradle replay, when run, then the encounter can be resolved by at least one path per class.
- AC-3 integration Given all lanterns destroyed before the abbot appears, when the encounter begins, then he relights one lantern (edge: encounter cannot be trivially skipped).
- AC-4 perf Given the Cradle fight with 4 Hushlings, 4 Forgotten and dream VFX, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) and p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat.
Waits on
- mw-e13.16 Build the Cradle encounter with fight, trick, persuade and join paths open
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
area:contentepic:e26milestone:m3mw-e26.18Build the Sounding Shaft climb with defenders and chase variants
Description
Why
With the tongue recovered, the bell must ring: a vertical ascent with the song rising behind.
What
As a player, I want to climb from the Cradle to the belfry on ropes, counterweights and old bell-ropes so that the finale is a set piece.
Shaft interior: ledges, bell-rope climbs, counterweight lifts, a collapsing stair section. Variants by facts: Horn holds the foot (horn alive and healed/befriended); goblins or Clatter hold it; knight holds while restored Clatter climbs (sacrifice beat); nobody → chase with Hushlings climbing. Archer shortcut: rope line skips half the climb.
Scope
- In: Shaft scene, climb routes, defender variants, chase spawner, archer shortcut
- Out: Ally behaviours (e14-horn-befriend-help)
Notes
Falling during the chase drops the player to the last ledge checkpoint, not the bottom (anti-frustration).
Acceptance criteria
- AC-1 integration Given no defender facts, when the climb begins, then Hushlings spawn below and climb at 80% of player climb speed.
- AC-2 integration Given an archer fires a rope arrow into the marked beam, when climbed, then half the shaft is skipped.
- AC-3 integration Given the player falls mid-climb, when they land, then they respawn on the last ledge checkpoint with the chase paused 3 s (edge).
- AC-4 integration Given the knight-hold variant, when chosen, then the player fights at the foot until the bell rings and Clatter completes the climb.
Waits on
- mw-e05.10 Rope arrows anchor into wood and create climbable ropes open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e14.6 Befriend and help Brother Horn without fighting open
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
- mw-e26.8 Build the belfry and the sour-bell consequence of early ringing open
area:contentepic:e26milestone:m3mw-e26.19Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers
Description
Why
The solution is a lullaby. Ringing the cracked bell — wrong and beautiful — is the climax.
What
As a player, I want to hang the bronze tongue and ring the bell so that the Nightjar sleeps and the missing wake.
Belfry finale: hang tongue (or Oswin's recast), optional mend first (Frostglass/Alteration-class property effect sets bell.mended for a truer note), ring. Song falters (song.intensity → 0), Hushlings dissolve, the Fold sleepers wake (sleepers.woken), Tansy asks who you are. Sets vespers.rung.
Scope
- In: Tongue hang interaction, mend option, ring sequence, facts, dissolve/wake hooks
- Out: Endings text (e39-intro-endings-text)
- Out: Ending level states (e26-ending-states)
Notes
Mend is systemic: any freeze or alteration effect applied to the crack entity counts.
Acceptance criteria
- AC-1 integration Given the tongue hung, when the bell is rung, then vespers.rung is set, song.intensity becomes 0 and all Hushlings dissolve.
- AC-2 integration Given bell.mended before ringing, when rung, then the truer-note variant plays and fact bell.trueNote is set.
- AC-3 integration Given the tongue was destroyed and no recast exists, when the belfry is reached, then Oswin's recast path is signposted (edge: never unwinnable).
Waits on
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e26.18 Build the Sounding Shaft climb with defenders and chase variants open
- mw-e26.8 Build the belfry and the sour-bell consequence of early ringing open
area:contentepic:e26milestone:m3mw-e26.20Wire dungeon shortcuts: crypt, cistern, cage lift and one-way doors
Description
Why
Every loop is a shortcut the player can open; the dungeon should fold back on itself.
What
As a player, I want to open doors from the far side and ride lifts back up so that returning to earlier areas is fast.
Shortcuts: crypt passage Knot ↔ priory (Horn opens, or pry from below), cistern ↔ priory (gate from the priory side), winch cage mine top ↔ bottom, supply-tunnel arrival ↔ goblin hall, Outer Ring ↔ Deepworks breach bypass. One-way doors with a visible "barred on this side" state.
Scope
- In: Shortcut doors/lifts, facts, connectivity updates
- Out: Transition engine (e01-area-transitions)
Notes
Each shortcut fact persists and shows on the world map notes.
Acceptance criteria
- AC-1 integration Given a one-way door approached from the locked side, when interacted, then it shows the barred state and does not open.
- AC-2 integration Given each shortcut opened, when saved and reloaded, then it remains open.
- AC-3 integration Given the cage lift at the bottom and the player at the top, when the call lever is used, then the cage returns (edge: never stranded).
Waits on
- mw-e14.6 Befriend and help Brother Horn without fighting open
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
area:contentepic:e26milestone:m3mw-e26.21Author first-pass class routes through every dungeon area
Description
Why
The knight forces, the thief infiltrates, the archer manipulates distant mechanisms, the sorcerer alters the dungeon.
What
As a player of any class, I want each dungeon area to offer my class a route so that the Labyrinth plays differently for me.
Per area: knight (weak walls, heavy doors, holding chokepoints), archer (distant mechanisms, open-chamber dominance, rope anchors), sorcerer (freeze/levitate/blink/illusion affordances), thief (vents, crawlspaces, hidden passages, dark routes). Registry entries and replays.
Scope
- In: Route affordances across mine/priory/Knot, registry entries, replays
- Out: Gap closing after audit (e16-gap-pass-dungeon)
Notes
Every thief hidden passage should be discoverable by others too (with effort) — secrets are for everyone.
Acceptance criteria
- AC-1 content Given the registry, when validated, then every E26 mandatory obstacle has >= 3 class routes declared.
- AC-2 integration Given each declared route, when replayed, then the obstacle exit fact is set.
- AC-3 integration Given a thief hidden passage, when a non-thief searches the wall, then it can be found with a light or search action (edge).
Waits on
- mw-e16.2 Declare obstacles and class routes as validated content data open
- mw-e26.13 Populate the Ossuary: Forgotten habits, Clatter's garden, the psalter open
- mw-e26.14 Build the Chapel of Echoes with the Chimes puzzle and portcullis trap open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e26.3 Populate the Deepworks: goblin camp, miner Forgotten, mine hazards open
area:contentclass:allepic:e26milestone:m3mw-e26.23Build the four endings' level states across town and dungeon
Description
Why
Endings are assembled from world facts; the world itself should show the ending.
What
As a player, I want the world to visibly change after my ending so that my choices feel real.
Fact-gated variants (e27-world-variants): Vespers Rung (bell rung each dusk by goblins/Hale/Tansy, bread at the mine gate, wolves howl again if breach sealed); The Last Warden (Horn's absence, goblin watch or Clatter humming, lantern and footprints at the gate); The Hireling's Silence (mine reopened, Vane prosperous, sleepwalker sitting up); The Long Vespers (sleeping valley, goblin with a stick in the Cradle).
Scope
- In: Variant entity sets per ending in town, gate, Fold, Cradle; epilogue camera anchors
- Out: Ending logic and text (e39-main-quest-data, e39-intro-endings-text)
Notes
Epilogue slides are text over these level states.
Acceptance criteria
- AC-1 integration Given the facts for each ending, when the town and gate load, then the matching variant set is active and no other ending's variants are.
- AC-2 integration Given breach.sealed with Vespers Rung, when night falls in the Briarwood, then wolf howls resume.
- AC-3 integration Given contradictory facts (horn.killed and horn.befriended), when evaluated, then killed takes precedence and a content warning is logged (edge).
Waits on
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e27.8 Spawn fact-gated entity variants so the world reacts to choices done
- mw-e39.4 Script the main quest graph with four endings as quest data done
area:contentepic:e26milestone:m3mw-e26.24Art-dress the Deepworks and Bellwater Priory with final assets
Description
Why
Timbered galleries and a roofless nave must read instantly as different places.
What
As a player, I want the mine and the priory to look like the story bible so that each layer of the dungeon has its own identity.
Swap grey-box for mine kit (pale cut-glenstone galleries, timber supports, rails, block carts, frost-bright silver seams, goblin junk) and priory kit (Vesperine stone, singing statues, bell); keep gameplay ids and collision.
Scope
- In: Art swap for Deepworks and priory scenes, LODs, decals
- Out: Asset creation (E37)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item.
Acceptance criteria
- AC-1 integration Given the dressed scenes, when all E26 route and puzzle replays in these areas run, then they pass unchanged.
- AC-2 perf Given the goblin hall and the nave, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-3 manual Given the art review, when compared to the style bible, then silhouettes of interactables remain readable against dressing (edge).
Waits on
- mw-e26.21 Author first-pass class routes through every dungeon area open
- mw-e26.6 Place the Watchers statue puzzle in the priory choir open
- mw-e26.7 Build the scriptorium with book mimics, tallow ooze and abbot letters open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.14 Legibility kit: visual cues for every exploitable world property open
- mw-e37.254 Integrate approved Deepworks and Bellwater Priory concepts open
- mw-e37.274 Integrate approved Deepworks and priory tileables open
- mw-e37.310 Integrate approved dungeon props open
- mw-e37.314 Integrate approved puzzle reskin and ending props open
- mw-e37.334 Integrate approved Rootcellar goblin camp props open
- mw-e37.346 Integrate approved set-piece hero props open
- mw-e37.352 Integrate approved world prop gap-fill open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artepic:e26milestone:m3mw-e26.25Art-dress the Knot with Hornfolk stone, runes and dream visuals
Description
Why
The Knot is a sentence carved as corridors; its art must make runes legible and the Cradle dreamlike.
What
As a player, I want the labyrinth's walls to be written in Hornfolk runes and the Cradle to shimmer so that the place feels ancient and alive.
Hornfolk stone kit, rune band decals (readable, with landmark faces), bone niches, chapel acoustics visuals, Fold moss, Cradle dream-leak shaders.
Scope
- In: Art swap for Knot scenes, rune decals, Cradle shaders
- Out: Asset creation (E37)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item.
Acceptance criteria
- AC-1 integration Given the dressed Knot, when rune-navigation tests and all Knot replays run, then they pass unchanged.
- AC-2 perf Given the Cradle fight with dream shaders, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-3 manual Given 3 testers, when asked to follow runes, then >= 2 identify the rune to follow from art alone (edge: legibility).
Waits on
- mw-e26.11 Make the Knot loop unless you follow its runes open
- mw-e26.17 Stage the Cradle arena for the abbot confrontation open
- mw-e26.21 Author first-pass class routes through every dungeon area open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.14 Legibility kit: visual cues for every exploitable world property open
- mw-e37.258 Integrate approved Knot environment concepts open
- mw-e37.278 Integrate approved Knot tileables and rune bands open
- mw-e37.310 Integrate approved dungeon props open
- mw-e37.314 Integrate approved puzzle reskin and ending props open
- mw-e37.340 Integrate approved Hornfolk and Knot props open
- mw-e37.430 Integrate approved world and dream VFX open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artepic:e26milestone:m3mw-e26.26Light and score the dungeon: mine, priory, Knot and the finale
Description
Why
Darkness is a stealth system and a mood; the song must be heard growing.
What
As a player, I want each dungeon layer to have its own light and sound so that descending feels like going deeper into a dream.
Lighting per area (lamps in the mine, moonlight in the priory, rune glow in the Knot), ambience beds (mine drips and creaks, priory wind, Knot hum, Chapel echoes), music cues per area plus the song layer driven by song.intensity, set-piece audio (sour bell, Horn's Hall, Cradle, climb, Vespers ring).
Scope
- In: Lighting, ambience zones, reverb zones, music cue bindings, set-piece cue wiring
- Out: Audio assets (E38)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item. Related: e28-ambience, e28-reverb-zones and e29-environment-fx are P2 in the combat plan; bind ambience beds, reverb zones and fog/firefly FX here when they land, otherwise ship lighting + music only.
Acceptance criteria
- AC-1 integration Given song.intensity changes, when evaluated, then the song music layer gain follows within 2 s.
- AC-2 integration Given the Vespers ring, when triggered, then all other music ducks and the bell cue plays uninterrupted.
- AC-3 integration Given dungeon lighting, when sampled along each thief route, then at least 40% of each route has light <= 0.3 (edge: stealth remains viable).
- AC-4 perf Given the Chapel with 3 Hushlings and all area lights, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e26.24 Art-dress the Deepworks and Bellwater Priory with final assets open
- mw-e26.25 Art-dress the Knot with Hornfolk stone, runes and dream visuals open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.116 Integrate approved mine and priory music families into adaptive music open
- mw-e38.120 Integrate approved Knot, Fold and Cradle music into adaptive music open
- mw-e38.124 Integrate approved boss, climb and finale music into adaptive music open
- mw-e38.140 Integrate approved Deepworks, priory and Knot ambience open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:audioepic:e26milestone:m3mw-e26.22Place dungeon secrets, notes, horn-coins and lore books
Description
Why
Lore is offered, not lectured; the dungeon should reward every detour.
What
As a curious player, I want hidden rooms, notes and relics so that I piece together the history myself.
Place 6–8 "friend below" notes, horn-coins (tiny ox asleep on the back), Hornfolk inscription fragments (sorcerer route), Mooring-era items, damaged spellbook pages, the Ox Beneath the Hill last page, 12–20 treasures per the e18 guide, and one book mimic trap per area.
Scope
- In: Placements, discovery rules, facts
- Out: Text (e39-books-lore, e39-dialogue-abbot)
Notes
Three sources that disagree for each big fact (bible §7.1).
Acceptance criteria
- AC-1 content Given the dungeon scenes, when audited, then every "friend below" note id from e39 is placed exactly once.
- AC-2 content Given the big facts (Long Vespers, the minotaur), when audited, then each has >= 3 placed sources (book, object, person).
- AC-3 integration Given a secret room opened, when saved and reloaded, then it stays open (edge).
Waits on
- mw-e18.5 Support designer-placed and hidden treasure with discovery rules open
- mw-e26.21 Author first-pass class routes through every dungeon area open
- mw-e39.13 Write in-game books, notes and inscriptions done
area:contentepic:e26milestone:m3
M4 Fun validation 2
mw-e26.27Perf-pass every dungeon area on both presets
Description
Why
The dungeon hosts the heaviest fights and effects; it must hold budgets everywhere.
What
Perf suite scenes for the goblin hall, scriptorium fire, Ossuary fight, Chapel, Horn's Hall, Cradle and the climb; captures on reference and low-tier machines; fixes within budget; area load times.
Scope
- In: Perf scenes, captures, fixes, report
- Out: Engine-level perf (E32)
Notes
Related: e32-reference-runbook, e32-memory-leak-test.
Acceptance criteria
- AC-1 perf Given each listed dungeon perf scene, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-2 perf Given the Cradle fight on Low, when measured, then p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat.
- AC-3 perf Given warm caches, when transitioning between any two adjacent dungeon areas, then load <= 3 s and heap <= 1.5 GB after 30 transitions (edge).
Waits on
- mw-e26.26 Light and score the dungeon: mine, priory, Knot and the finale open
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
- mw-e32.12 Detect memory leaks across repeated scene loads open
- mw-e32.6 Implement Low/High graphics presets and dynamic resolution open
area:perfepic:e26milestone:m4mw-e26.28Playtest the full dungeon per class and replay each critical path
Description
Why
The MVP's defining scenario must be completable and memorable for every class.
What
Automated critical-path replays per class from the mine gate to "Vespers rung"; one internal playtest per class through the whole dungeon; findings to docs/playtests/dungeon.md with beads.
Scope
- In: 4 critical-path replays, 4 playtests, findings
- Out: External playtests (e35-external-round-1)
Notes
Replays must cover two different macro-routes per class across the four runs.
Acceptance criteria
- AC-1 e2e Given a save at the mine gate, when each class's critical-path replay runs, then vespers.rung is set with zero console errors.
- AC-2 manual Given one tester per class, when finished, then each rates the dungeon >= 4/5 for "my class mattered".
- AC-3 manual Given testers who got stuck > 5 minutes, when observed, then each stuck point has a bead or accepted note (edge).
Waits on
- mw-e16.6 Close class-route gaps in the Deepworks, priory and the Knot open
- mw-e26.23 Build the four endings' level states across town and dungeon open
- mw-e26.27 Perf-pass every dungeon area on both presets open
area:qaepic:e26milestone:m4
E27Dynamic World StateMeaningful actions persist: doors opened, walls smashed, ropes burned, chests looted, creatures befriended, treasures stolen, characters helped. A typed, deterministic world-fact store plus per-entity deltas against authored baselines captures the world as the player left it and flows through e30 save serialization. Quests, dialogue, puzzles and level variants all read the same facts.10/16 done · 63%
M1 Grey-box slice 3
mw-e27.14Assign stable GUIDs to scene placements and lint orphaned persistent ids
Description
Why
mw-e27.3 (PR #158) keys authored-entity deltas by placement index (piece:<n>), so reordering placements in a scene orphans their deltas. The bead's notes require GUIDs assigned by the level pipeline and a lint that warns when a persistent entity id disappears.
What
Give every scene placement an authored, stable id (GUID) assigned by tooling; switch sceneAuthoredEntities to use it; add a content lint that diffs ids against the previous build and warns on removed persistent ids.
Acceptance criteria
AC-1 unit Reordering placements in a scene leaves every authored entity id unchanged.
AC-2 content Removing a placement that had an id in the committed lock produces a lint warning naming it.
AC-3 unit Existing piece:<n> deltas migrate to GUID ids via the save migration runner.
area:simepic:e27milestone:m1mw-e27.15Integration test: burnt rope bridge stays gone across a level round-trip
Description
Why
mw-e27.3 AC-5 was covered only partially in PR #158 (weak-wall room, scene reload) because the slice has no rope bridge or area transitions yet.
What
Once area transitions (mw-e01.11) exist and the greybox slice has a burnable rope bridge, add the integration test: burn the bridge, leave the level, return; bridge still gone and replay hashes match.
Acceptance criteria
AC-1 integration Given the greybox slice, when the player burns a rope bridge, leaves the level and returns, then the bridge is still gone and replay hashes match.
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
area:simepic:e27milestone:m1mw-e27.16Prove world saves on the greybox slice: opened door and looted chest survive a tab close
Description
Why
mw-e27.4 AC-4 asks for the greybox slice, which did not exist when it shipped. Its e2e (e2e/world-saves.spec.ts) plays the testbed instead: the closet door is the door and the floor items (healing draught, closet key) stand in for a chest (owner decision recorded in PR for mw-e27.4).
What
As a player, I want a door I opened and a chest I emptied in the slice to stay that way after I save, close the tab and load, so that I trust the game with my progress.
Port the AC-4 e2e to the slice level once its alcove chest exists (mw-e01.6): open the slice door, loot the chest's container pack, save, close the tab, load. Assert the container delta (inventory.pack emptied) as well as the door.
Scope
- In: e2e on the slice with a real container entity; container aspect asserted
- Out: Save format and sections (done in mw-e27.4); slice save triggers (mw-e01.7)
Notes
The loaded level is restored from the world section and its deltas are measured against the fresh baseline; deltas are applied only when a level is entered fresh (no area transitions yet, mw-e01.11).
Acceptance criteria
- AC-1 e2e Given the greybox slice, when the player opens the door, loots the alcove chest, saves, closes the browser tab and loads the save, then the door is open and the chest's pack is empty.
- AC-2 e2e Given the same save, when it is loaded, then #app[data-level-deltas] lists the door and container aspects for the slice level.
Waits on
- mw-e01.4 Build the slice grey-box level: room, corridor, arena, loot, exit done
- mw-e01.6 Add slice loot: skeleton key drop and an alcove chest done
area:simclass:allepic:e27milestone:m1size:s
M2 Class fantasies 1
mw-e27.13Rest at inns, campfires and shrines to reset facts and pass time
Description
Why
Persistence policies reset "on rest", archers assemble arrows and everyone swaps loadouts "at rest points", artifacts recharge on rest — but no item defined what resting is. A shared rest verb gives the world a rhythm and gives failure a place to happen (a campfire in the wild can be interrupted).
What
As a player, I want to rest at an inn bed, a campfire or a shrine so that I recover, the world moves on and I can reorganise my kit.
- Rest point entity: kind
inn(paid through e20-services later; free in grey-box),campfire(must be burning — any heat stimulus lights it; wet/extinguished fires refuse),shrine. Data per kind: restoration table, allowed durations, interrupt table. - Request: interact → choose duration (until dawn / until dusk / N hours). Refused when unsafe: reuses the autosave "unsafe" veto registry (combat/alert ≥ Searching/hostile within radius) with reasons.
- Rest:
advanceTime, restore health/stamina/mana per kind, emitrest.completed{kind, hours, point}which triggers rest-reset persistence policies. A scheduled or data-rolled (seeded RNG) interrupt stops rest early with prorated restoration and fires its event — a new situation, not a reload. - Rest actions hook: a registry lets other systems add actions to the rest menu while at a rest point (arrow assembly, loadout swap, artifact recharge). This item owns the hook only.
- Autosave once after a completed or interrupted rest.
Scope
- In: Rest point component/schema, safety check, duration selection, time advance, restoration, rest.completed event, interrupts, rest-action registry, autosave trigger, minimal rest menu on e00-ui-framework
- Out: Inn payment — see e20-services
- Out: Arrow assembly — e19-archer-arrow-channel; loadout swap — e19-respec-policy; artifact recharge — e18-unique-artifacts (register rest actions)
- Out: Which facts reset — see e27-persistence-policies
Notes
Canon: the Sleeping Ox is Briar Glen's inn (story bible §3.5). Resting is not a save-scum tool: autosave after rest uses the normal ring.
Integration note: Quest-registered vetoes (e.g. the E24 first-night beat) use the same veto registry as AC-2.
Acceptance criteria
- AC-1 unit Given a campfire rest point that is not burning, when rest is requested, then it fails with reason
no-fire; after a heat stimulus ignites it, the same request succeeds. - AC-2 unit Given a registered unsafe veto (hostile within 20 m or any alert state ≥ Searching), when rest is requested, then it fails with reason
unsafeand game time does not advance. - AC-3 unit Given rest until dawn requested at 22:00, when completed, then the clock reads 06:00 next day,
rest.completedfires once, restoration follows the kind's table, rest-reset facts reset and permanent facts are unchanged. - AC-4 unit Given two registered rest actions, when the rest menu opens at a rest point, then both are listed in priority order; when invoked away from a rest point, then each fails with reason
not-at-rest. - AC-5 unit Given an interrupt scheduled at 02:00 during an 8 h rest starting 22:00, when resting, then rest stops at 02:00, restoration is prorated to 4/8 and the interrupt event fires once (edge).
- AC-6 integration Given a completed rest, when it ends, then exactly one autosave is written after the
rest.completedevent. - AC-7 e2e Given the greybox testbed with an unlit campfire, when the player lights it and rests 8 h, then the HUD clock shows +8 h, health is full and no console errors occur.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e27.7 Apply permanent, rest-reset and transient persistence policies done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
area:simclass:allepic:e27milestone:m2size:m- Rest point entity: kind
M3 MVP content 2
mw-e27.10Resolve elapsed time for unloaded levels when the player returns
Description
Why
Leaving a freezing spell on a river and returning a day later should find it melted; a fire should have burned out. Cheap catch-up rules preserve consistency without simulating unloaded levels.
What
As a player, I want time to have passed in places I left so that the world feels continuous.
On level load, compute elapsed sim time since last visit and apply analytic catch-up rules per component (ice melts after X, fires resolve to burnt, doors on timers close, puzzle respawners complete, reset policies apply) instead of simulating.
Scope
- In: Last-visited tick per level, catch-up rule registry per component
- In: Deterministic ordered application
- Out: NPC off-screen schedules — see mw-e22
Notes
Catch-up rules are pure functions (state, elapsed) → state, 100% unit-tested.
Acceptance criteria
- AC-1 unit Given ice created with melt time 10 min and a return after 30 min, when the level loads, then the ice is gone and water volume restored.
- AC-2 unit Given a return after 1 min, when loaded, then the ice remains with proportionally reduced thickness.
- AC-3 unit Given a timed door open when left, when returning after its timer, then it is closed.
- AC-4 unit Given elapsed time is negative or zero (clock anomaly / immediate re-entry), when loaded, then no catch-up applies and no error occurs (edge).
Waits on
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e27.7 Apply permanent, rest-reset and transient persistence policies done
area:simclass:allepic:e27milestone:m3size:smw-e27.11Budget world-state save size and serialization time
Description
Why
Browser saves live in IndexedDB and autosave during play; large or slow saves cause hitches and quota errors.
What
Stress fixture with 10,000 facts + 5,000 deltas across 6 levels; perf tests for serialize/deserialize time and compressed size; autosave must not hitch the main thread (serialize off the sim tick via snapshot + worker compression).
Scope
- In: Stress fixture, perf tests, snapshot-then-compress in a worker
- Out: IndexedDB quota handling — see e30-save-serialization
Notes
Budgets from epic exit criteria.
Acceptance criteria
- AC-1 perf Given the stress fixture, when serialized on the reference machine, then snapshot time on the main thread ≤ 50 ms and compressed size ≤ 1 MB.
- AC-2 perf Given autosave during 60 s of play, when measured, then no frame exceeds 33 ms due to save work.
- AC-3 unit Given a snapshot taken while the sim continues ticking, when compressed in the worker, then the saved state equals the state at snapshot tick (edge: no tearing).
Waits on
- mw-e27.4 Serialize world facts and entity deltas through the save system done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfepic:e27milestone:m3size:s
E28Audio & Combat FeedbackWeapons, spells, footsteps, creatures and environments have satisfying positional audio and feedback, driven by data from sim events, within the browser and performance baseline.12/20 done · 60%
M1 Grey-box slice 1
mw-e28.17Playtest: hear block, parry and clean hit apart with eyes closed
Description
Why
mw-e28.4 AC-5 is a manual listening test and cannot be claimed by automated tests.
What
In the combat sandbox, three playtesters close their eyes during a dummy fight and name block, parry and clean hit by sound alone. Record the result in the bead.
Scope
- In: the listening session and its notes
- Out: sound changes (file beads for any)
Acceptance criteria
- AC-1 manual Given the sandbox, when three playtesters close their eyes during a dummy fight, then each can distinguish block, parry and clean hit by sound alone (mw-e28.4 AC-5).
Waits on
- mw-e28.16 Parry and riposte cues that are never stolen done
area:audioclass:allepic:e28milestone:m1milestone:m3
M2 Class fantasies 4
mw-e28.7Bow audio: draw creak, release, flyby and payload cues
Description
Why
A creaking draw and a sharp release make the bow feel tense and powerful (Archery epic outcome: bows feel excellent).
What
As an archer, I want the bow to creak as I draw and arrows to whistle past so that shots feel tense and physical.
Draw creak loop whose playbackRate (0.9→1.1) and gain follow draw fraction; focused-release chime; release twang by draw strength; arrow flyby when passing within 3 m of the listener (pitch bend simulating doppler); impacts reuse the combat material matrix; ricochet; trick payload cues (ignite, splash, rope unfurl, rattle); pickup cue on retrieval.
Scope
- In: Continuous draw loop control, flyby detection, cue rules for bow events
- Out: Final bow SFX (asset group)
Notes
Acceptance criteria
- AC-1 unit Given draw fraction 0.5, when the creak loop updates, then playbackRate is 1.0 and gain is the 50% curve value.
- AC-2 unit Given an arrow passing 2 m from the listener, when its closest approach occurs, then one flyby cue plays; at 4 m, then none.
- AC-3 integration Given a focused release event, when processed, then the chime plays in the same frame as the release twang.
- AC-4 unit Given the draw is cancelled, when processed, then the creak loop fades out within 100 ms and no release twang plays.
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
area:audioclass:archerepic:e28milestone:m2mw-e28.10Audio occlusion through walls and doors
Description
Why
Hearing a muffled fight behind a door is atmosphere and information; it should match how creatures hear (the stealth model) conceptually.
What
As a player, I want sounds behind walls and doors to be muffled so that I can judge where things are and whether a door is open.
For the top 16 priority sources within 30 m, raycast listener↔source at 10 Hz (staggered). Occluded: low-pass 22 kHz→900 Hz and −8 dB; closed door on the path: −12 dB and 600 Hz; open door: unoccluded path. 150 ms smoothing. Door states from e03-doors-locks-switches; acoustic values shared with the stealth group's door constants.
Scope
- In: Ray scheduling, filter/gain mapping, door state lookup, smoothing
- Out: Sim noise occlusion for creatures (e09-sound-propagation)
- Out: Full path-finding propagation (post-MVP)
Notes
Budget: ≤ 16 rays per 100 ms.
Acceptance criteria
- AC-1 integration Given a source behind a solid wall, when occlusion updates, then its low-pass cutoff settles at 900 Hz and gain at −8 dB within 150 ms.
- AC-2 integration Given a source behind a closed door that then opens, when occlusion updates, then filtering clears within 150 ms.
- AC-3 unit Given 40 eligible sources, when scheduled, then only the 16 highest-priority are raycast and no more than 16 rays run per 100 ms.
- AC-4 unit Given the listener inside a collider (camera clipping), when raycasting, then the result is treated as unoccluded (no false muffling).
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
area:audioclass:allepic:e28milestone:m2mw-e28.18Feed armour weight class into the footstep armour layer
Description
Why
The footsteps cue sheet (mw-e28.6) plays sfx-armor-plate-layer / sfx-armor-chain-layer on each step when the cue lookups know a character's armour class, but nothing supplies it until armour weight exists, so the layer is silent in the testbed.
What
Pass armorOf (attachGameAudio option) from the e04.16 armour weight class so the knight in plate clanks as it walks; add the leather layer placeholder.
Scope
- In: armorOf wiring in main.ts, leather placeholder
- Out: final armour SFX (mw-e38.24 to mw-e38.27)
Acceptance criteria
- AC-1 integration Given the player in plate armour in the testbed, when walking, then each footstep also plays sfx-armor-plate-layer.
Waits on
- mw-e04.16 Armor weight sets roll class, stamina regen, poise and noise done
area:audioclass:allepic:e28milestone:m2milestone:m3mw-e28.19Wall recoil and charge-ready combat cues
Description
Why
mw-e28.4 lists distinct cues for a swing recoiling off a wall and a charged attack reaching full charge; the sim emits neither event yet, so the placeholder ids sfx-knight-wall-recoil-clang and sfx-knight-charge-ready are unused.
What
Once the sim emits a wall-recoil event (mw-e04.15) and a charge-full event (mw-e04.13), add them to the cue-event table and bind combat cue rules.
Scope
- In: cue events and rules
- Out: the sim events themselves
Acceptance criteria
- AC-1 integration Given a charged heavy held to full charge, when it becomes ready, then sfx-knight-charge-ready plays at the knight.
- AC-2 integration Given a sword swing that hits a wall, when it recoils, then sfx-knight-wall-recoil-clang plays at the contact.
Waits on
- mw-e04.13 Heavy and charged attacks with hyperarmor done
- mw-e04.15 Weapon reach classes and swing-type wall recoil done
area:audioclass:allepic:e28milestone:m2milestone:m3
M3 MVP content 2
mw-e28.12Reverb zones per space: caves, halls, interiors, forest
Description
Why
Acoustic space sells the dungeon: a sword clang should echo in the labyrinth and die in the forest.
What
As a player, I want each space to sound like its size and material so that caves, halls and forests feel different.
Convolution reverb sends per zone type (cave, stone hall, wooden interior, forest, open field) using impulse-response assets; zone chosen via e03-triggers volumes; 1 s crossfade across boundaries; Low preset uses a single shared convolver with fewer IRs.
Scope
- In: Reverb send buses, zone volumes, crossfade, Low-preset fallback, placeholder IRs
- Out: Recorded/generated IR assets (asset group)
Notes
Acceptance criteria
- AC-1 integration Given the listener entering a cave trigger volume, when processed, then the cave send ramps to its target over 1 s while the previous zone fades.
- AC-2 unit Given overlapping zones, when resolved, then the zone with the highest declared priority wins.
- AC-3 unit Given the Low preset, when zones switch, then at most one ConvolverNode exists.
- AC-4 unit Given a missing IR asset, when the zone activates, then the dry signal plays unaffected and a warning is logged.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
area:audioclass:allepic:e28milestone:m3mw-e28.20Bind menu snapshot, critical-hit duck and UI stinger duck to game events
Description
Follow-up from mw-e28.9 (PR #312). src/audio/mix-snapshots.ts defines the menu snapshot and the critical and ui-stinger ducks, but nothing triggers them: no critical-hit event exists in src/sim yet, and title/full-screen menus don't set the menu snapshot. Also tune snapshot dB values by ear.
AC-1 unit Title and full-screen menus apply the menu snapshot and restore on close.
AC-2 unit A critical-hit event (once defined) triggers the critical duck.
AC-3 unit UI stingers trigger the ui-stinger duck.
area:audioclass:allepic:e28milestone:m3
M4 Fun validation 1
mw-e28.15Audio performance and download budgets
Description
Why
Audio must fit the hardware baseline: ≤ 50 MB initial download and steady 60 fps.
What
Budgets added to the e32 perf suite: decoded audio memory ≤ 96 MB; main-thread audio work ≤ 0.5 ms/frame p95; voices ≤ 48 High / 24 Low; audio share of initial download ≤ 12 MB (music streamed); format strategy (Opus in WebM/Ogg with AAC fallback for Safari).
Scope
- In: Budget assertions, format pipeline, lazy-load per area
- Out: Global perf suite (e32-perf-budgets)
Notes
Cites the contract hardware baseline; no new targets invented beyond audio shares.
Acceptance criteria
- AC-1 perf Given the combat stress scene, when profiled on the reference machine, then main-thread audio work ≤ 0.5 ms per frame p95.
- AC-2 perf Given a cold load of the vertical slice, when measured, then audio bytes before playable ≤ 12 MB.
- AC-3 perf Given a 20-minute scripted session, when measured, then decoded audio memory never exceeds 96 MB.
- AC-4 e2e Given Safari (WebKit), when sounds load, then the AAC fallback is selected and plays without errors.
Waits on
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfclass:allepic:e28milestone:m4
E29VFX & Magical SpectacleMagic, impacts, destruction, stealth feedback and environmental effects establish the game's visual identity, derived from sim events and world properties, within the particle and frame budgets.12/19 done · 63%
M2 Class fantasies 5
mw-e29.9Destruction VFX: fracture chunks, dust and crack stages
Description
Why
Smashing a wall has to look as good as it feels; crack stages make breakable things readable (Zelda-like readability).
What
As a knight, I want smashed walls and crates to burst convincingly and cracked walls to look weak so that I know what I can break.
On
ObjectBroken: pre-fractured chunk meshes when available, otherwise procedural box chunks for greybox; dust cloud; cosmetic debris render-only (cap 200 pieces, fade after 8 s) — gameplay rubble remains sim physics from e03. Crack stage visuals (0/1/2) forbreakableobjects so weak walls read as weak before any hit.Scope
- In: Chunk spawner, dust, cosmetic debris cap/fade, crack stage materials
- Out: Break rules (e04-smash-breakables)
- Out: Fractured art assets (asset group)
Notes
Acceptance criteria
- AC-1 integration Given a weak wall broken, when processed, then chunks and a dust cloud spawn and cosmetic debris count stays ≤ 200.
- AC-2 integration Given crack stage 1 in sim, when rendered, then the stage-1 crack material is applied.
- AC-3 unit Given 250 cosmetic debris requests, when processed, then the oldest 50 are removed.
- AC-4 perf Given 3 simultaneous wall breaks, when measured, then frame time p95 ≤ 16.7 ms on the reference machine (High).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e04.17 Smash breakable walls and objects by impact force open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
area:renderclass:knightepic:e29milestone:m2mw-e29.13Projected decals: scorch, frost, wet, arrow holes, cracks
Description
Why
Decals leave evidence of what happened — scorch after fire, wet stone after a splash — making the world feel reactive.
What
As a player, I want surfaces to show scorch, frost, wetness and arrow holes so that the world visibly remembers what happened.
Projected decals with a cap of 128 (High) / 48 (Low), FIFO fade-out. Wet decals match the sim
wetduration exactly so visuals never lie about state; scorch remains afterburningends; frost whilefrozen; arrow holes when arrows are pulled; crack decals for crack stages.Scope
- In: Decal projector, cap/fade, state-synced lifetimes
- Out: Decal textures (asset group)
Notes
Rule: a decal never conveys a gameplay state unless that state exists in the sim.
Acceptance criteria
- AC-1 unit Given 129 decals on High, when the 129th spawns, then the oldest fades out over 1 s.
- AC-2 integration Given a surface becoming
wetin sim for 20 s, when rendered, then the wet decal appears and is gone within one frame of the sim state clearing. - AC-3 integration Given a burning object that is extinguished, when processed, then a scorch decal remains.
- AC-4 unit Given the Low preset, when decals spawn, then the cap is 48.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
area:renderclass:allepic:e29milestone:m2mw-e29.17Soft depth fade for the main VFX particle renderer
Description
mw-e29.7 ships soft particles (src/render/magic-materials/soft-particle.ts) and the depth pass behind them (SoftDepthPass: half-resolution depth of the opaque scene, drawn only while a soft particle exists), but the batched particle renderer (src/render/vfx/index.ts) does not use it, so smoke, glow and sparks still cut a hard line through floors and walls. Wire a depth fade into the batch shader on High, decide the pass cost against the frame budget (a second scene draw; consider drawing depth only when particles are near geometry, or moving the main render to a target with a depth texture), keep Low on the plain fade, and measure with ?vfx=stress.
area:renderclass:allepic:e29milestone:m2milestone:m3mw-e29.18Rune decals projected onto uneven surfaces
Description
mw-e29.7's rune decal (src/render/magic-materials/rune.ts) is a flat disc on the ground plane with a polygon offset, so it floats over stairs and cuts into slopes. Project it as a box decal that reads the scene depth (SoftDepthPass already renders it while a soft particle exists) so it wraps floors, walls and steps; keep the flat disc as the Low fallback.
area:renderclass:allepic:e29milestone:m2milestone:m3mw-e29.19Creature corpses: dissolve ones loaded already dead, and keep the burning look while dissolving
Description
mw-e29.7 dissolves a creature that dies in play and removes its render entity. Creatures drawn already dead (a loaded save, a level re-entry) still lie on the ground for ever (src/main.ts: deaths.start(..., lying=true)), so a corpse seen dissolving in one session is back, lying, after a reload. Decide what a persistent corpse should be (dissolve on load, or keep until loot is taken, see src/sim/loot) and make it consistent. Also: a creature that dies while burning, wet, frozen or charged loses its element look at death (the element look is withheld from dying bodies so it cannot put the original materials back over the dissolve's copies); let the two coexist so a burning skeleton burns while it dissolves.
area:renderclass:allepic:e29milestone:m2milestone:m3
M3 MVP content 1
mw-e29.14Ambient environment VFX: dust motes, embers, fireflies, fog
Description
Why
Light shafts full of dust and torches spitting embers give Briar Glen and the Labyrinth their mood (visual identity).
What
As a player, I want places to feel alive with dust, embers, fireflies and fog so that exploring is atmospheric.
Ambient effects placed via level data or derived from properties: dust motes in light shafts, torch embers and flicker (auto on
burninglight sources), fireflies (forest night), falling leaves, height fog / fog volumes, water drips and ripples. Environment effects are limited to ≤ 25% of the particle budget.Scope
- In: Ambient effect set, placement data, budget share
- Out: Level dressing (location epics)
Notes
Acceptance criteria
- AC-1 unit Given ambient effects requesting 40% of the particle cap, when evaluated, then they are throttled to 25%.
- AC-2 integration Given a torch with
burning, when loaded, then the ember effect attaches without per-torch authoring. - AC-3 e2e Given an environment test scene in Chromium, when loaded, then all ambient effects render with no console errors.
- AC-4 perf Given the forest test scene at night, when measured, then frame time p95 ≤ 16.7 ms on the reference machine (High).
Waits on
- mw-e29.8 Visuals derived from element states: burning, wet, frozen, shock done
area:renderclass:allepic:e29milestone:m3
M4 Fun validation 1
mw-e29.15VFX stress scene, quality tiers and auto-degrade
Description
Why
Spectacle must never break frame pacing on the target hardware (hardware baseline).
What
A combat stress scene: 12 creatures, 4 simultaneous spells, fire spreading over 10 objects, 2 destructions. Budgets added to the e32 suite. Quality tiers per preset, plus auto-degrade: when frame time exceeds budget for 2 s, halve emission rates, then disable distortion, then reduce decal cap; recover after 10 s under budget.
Scope
- In: Stress scene, budget assertions, auto-degrade controller
- Out: Global perf suite (e32-perf-budgets)
Notes
Cites contract baseline: High 60 fps reference machine at 1440p-equivalent; Low 60 fps at 1080p, 30 fps floor in worst-case combat on Iris Xe / base M1 class.
Acceptance criteria
- AC-1 perf Given the stress scene on the reference machine (High), when measured for 60 s, then frame time p95 ≤ 16.7 ms.
- AC-2 perf Given the stress scene on the Low preset on a base-M1-class device, when measured, then frame rate never drops below 30 fps.
- AC-3 unit Given frame time over budget for 2 s, when the controller evaluates, then emission rates halve; after 10 s under budget, they restore.
- AC-4 unit Given frame time spikes shorter than 2 s, when evaluated, then no degradation occurs.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
- mw-e29.8 Visuals derived from element states: burning, wet, frozen, shock done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfclass:allepic:e29milestone:m4
E30Save/LoadBrowser-safe saves reliably preserve character and world state across sessions, browser restarts and game updates; a corrupted or interrupted save never costs the player all their progress.9/15 done · 60%
M1 Grey-box slice 4
mw-e30.13Autosave at a testbed checkpoint volume shows in the slot list (e2e) and HUD indicator
Description
Why
mw-e30.5 shipped the autosave scheduler as API only: its AC-5 e2e (checkpoint volume in the testbed -> autosave in the slot list with the area name) is proven at the sim level in src/game/save/autosave/autosave.integration.test.ts because the greybox testbed (mw-e00.21) and HUD layer (mw-e00.23) do not exist yet.
What
Wire AutosaveScheduler into the game loop (update between sim steps, reset after load), mark a testbed checkpoint volume via autosaveAtCheckpoints, render the HUD saving indicator and the non-blocking failure warning from scheduler.subscribe, and add the Playwright test.
Acceptance criteria
- AC-1 e2e Given the testbed with a checkpoint volume, when the player enters it, then an autosave appears in the slot list with the area name.
- AC-2 e2e Given an autosave write fails twice, then a non-blocking HUD warning is shown and play continues.
Acceptance criteria
- AC-1 e2e Given the testbed with a checkpoint volume, when the player enters it, then an autosave appears in the slot list with the area name.
- AC-2 e2e Given an autosave write fails twice, then a non-blocking HUD warning is shown and play continues.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
area:gamearea:simepic:e30milestone:m1mw-e30.6Add quicksave and quickload actions
Description
Why
Experimentation-heavy play (constitution: "too useful" spells) benefits from instant save-scumming for players who want it.
What
As a player, I want quicksave/quickload keys so that I can try risky ideas instantly.
Quicksave writes the single quicksave slot (subject to the same safety vetoes as autosave, except combat is allowed); quickload asks for confirmation if unsaved progress > 60 s.
Scope
- In: Quicksave/quickload actions registered in the action map
- In: Confirmation prompt
- Out: Default key bindings list (e02-input-actions)
Notes
- None.
Acceptance criteria
- AC-1 unit Given quicksave during combat, when triggered, then the save succeeds (combat not vetoed for quicksave) but mid-air/dialogue still vetoes with a HUD message.
- AC-2 integration Given no quicksave exists, when quickload is pressed, then a "no quicksave" toast appears and nothing else changes.
- AC-3 e2e Given progress of 90 s since the last save, when quickload is pressed, then a confirmation dialog appears before loading.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e30.4 Support manual save slots with metadata and thumbnails done
area:gameepic:e30milestone:m1mw-e30.12Wire canvas thumbnail capture into saves and verify it in a browser
Description
Why
mw-e30.4 added captureCanvasThumbnail (src/render/thumbnail.ts, coverage-excluded) and the SaveSlots captureThumbnail seam, but no renderer exists yet (mw-e00.13), so the capture is never exercised in a real browser.
What
Once the renderer exists, pass () => captureCanvasThumbnail(canvas) to SaveSlots saves (pause-menu save, autosave, quicksave), make sure the WebGL canvas is captured right after a render (or preserveDrawingBuffer), and add a Playwright check that a saved slot shows a non-placeholder WebP/PNG thumbnail within 256x144 and 64 KiB in Chromium, Firefox and WebKit.
Acceptance criteria
AC-1 e2e Given a manual save from the running game, when the slot list is read, then the slot has a captured thumbnail (not the placeholder) in each of Chromium, Firefox and WebKit.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e30.4 Support manual save slots with metadata and thumbnails done
area:renderarea:simepic:e30milestone:m1mw-e30.15Veto saving while the player is mid-air
Description
Why
The autosave contract (mw-e30.5) and the pause menu (mw-e01.3) say saving waits while the player is mid-air, so a save never captures a fall in progress. Only the combat veto exists today (src/game/creatures/save-veto.ts).
What
Register a
mid-airsafety veto alongside the combat veto in src/main.ts'ssaveVetoes: it objects with "Can't save while falling" while the player is not grounded (jumping, falling, hanging from a ledge or climbing count as mid-air; standing on a ladder rung is the owner's call). The pause menu's Save shows the reason and autosaves wait for landing, through the same SafetyVetoes.Scope
- In: the veto, its reason text, wiring into the shared vetoes
- Out: dialogue veto (lands with the dialogue runtime)
Notes
Discovered while building the pause menu (mw-e01.3).
Acceptance criteria
- AC-1 unit Given the player airborne after a jump, when the veto is asked, then it returns "Can't save while falling"; grounded, it returns null.
- AC-2 integration Given a checkpoint crossed mid-jump, when the player lands, then the autosave is written after landing, never before.
- AC-3 integration Given the pause menu opened mid-air, then Save is disabled with the reason.
area:gamearea:simepic:e30milestone:m1
M3 MVP content 2
mw-e30.9Prevent two open tabs from clobbering each other's saves
Description
Why
Browser games are easily opened twice; concurrent writers silently overwrite progress.
What
Use the Web Locks API (fallback: BroadcastChannel heartbeat) to elect a single "active" tab. A second tab shows "Game already open in another tab" with a "Play here instead" option that asks the first tab to release (it autosaves and pauses).
Scope
- In: Lock acquisition, takeover protocol, fallback
- Out: Cross-device sync (not planned)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the game open in tab A, when opened in tab B, then B shows the already-open message and makes no writes.
- AC-2 e2e Given B chooses "Play here instead", when A receives the request, then A autosaves, pauses with a notice, and B loads that autosave.
- AC-3 e2e Given tab A crashes (closed abruptly), when tab B retries, then it acquires the lock within 5 s.
Waits on
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
area:gameepic:e30milestone:m3mw-e30.10Export and import save files
Description
Why
Browser storage can be cleared; players and playtesters need a way to back up and share saves.
What
As a player, I want to export a save to a file and import it later so that clearing browser data does not erase my adventure.
Export produces
the-vesper-bell-<slot>-<date>.vbsave(JSON envelope, gzip). Import validates envelope, checksum and version, then runs migrations and places it into a chosen slot.Scope
- In: Export/import in settings/load menu
- In: Validation errors shown in plain language
- Out: Cloud sync
Notes
- None.
Acceptance criteria
- AC-1 integration Given an exported save, when imported into a fresh profile, then loading it yields the same sim state hash.
- AC-2 integration Given a file that is not a save (random JSON or PNG), when imported, then a clear "not a save from The Vesper Bell" error is shown and no slot changes.
- AC-3 integration Given a save from an older schema version, when imported, then migrations run and it loads.
- AC-4 unit Given a save file larger than 20 MB, when imported, then it is rejected before parsing.
Waits on
- mw-e30.4 Support manual save slots with metadata and thumbnails done
area:gameepic:e30milestone:m3
E31Accessibility & SettingsPlayers can tailor controls, text, camera, audio, visuals and difficulty so the game is comfortable and playable for a broad audience, meeting the basic tier of the Game Accessibility Guidelines.13/17 done · 76%
M3 MVP content 3
mw-e31.12Add gamepad deadzone, sensitivity curve and vibration settings
Description
Why
Gamepad is second-class but supported; stick drift and vibration sensitivity vary by player and hardware.
What
As a player, I want to tune stick deadzones, response curves and vibration so that my gamepad feels accurate.
Inner/outer deadzone per stick, response curve (linear/exponential), vibration intensity 0–100% (where Gamepad haptics are supported).
Scope
- In: Settings + input-layer processing
- Out: Gamepad action mapping (e02-input-actions)
Notes
- None.
Acceptance criteria
- AC-1 unit Given inner deadzone 0.15, when stick magnitude is 0.1, then output is 0; at 1.0 output is 1.0 (rescaled).
- AC-2 unit Given vibration 0%, when a haptic request is made, then no vibration API call occurs.
- AC-3 integration Given a browser without
vibrationActuator, when haptics are requested, then no error is thrown.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:gameepic:e31milestone:m3mw-e31.16Gamepad rebinding in the Key bindings panel
Description
Follow-up from mw-e31.2 (PR #317). The binding model and storage already carry pad bindings, but the Controls > Key bindings panel only captures keyboard/mouse. Capturing a pad button needs the UI pad adapter suspended during capture (A/B otherwise act as confirm/back). Also count pad bindings toward the Pause required-action rule.
AC-1 unit In capture mode, a pad button press binds that button and does not trigger UI confirm/back.
AC-2 unit Removing the last keyboard binding for Pause is allowed when a pad binding remains, and refused when none does.
AC-3 e2e A rebound pad button persists across reload.
area:uiepic:e31milestone:m3mw-e31.17Camera options follow-up: camera distance, separate X/Y and stick sensitivity, lock-on switch sensitivity
Description
mw-e31.6 shipped look sensitivity (mouse + stick, one slider), invert X/Y, FOV 60-100 and auto-centre. Still open from its description: camera distance default, separate X and Y sensitivity, separate stick sensitivity, lock-on switch sensitivity.
area:gamearea:uiepic:e31milestone:m3
M4 Fun validation 1
mw-e31.13Audit MVP against Game Accessibility Guidelines basic tier
Description
Why
Individual features do not guarantee an accessible whole; an audit before external playtests catches gaps.
What
Walk through every GAG "basic" guideline (motor, cognitive, vision, hearing, speech, general) against the MVP build; record pass/fail/N-A with evidence in
docs/accessibility/audit-<date>.md; file beads for each failure.Scope
- In: Audit doc, beads for findings
- Out: Fixing findings (separate beads)
Notes
- None.
Acceptance criteria
- AC-1 manual Given the MVP build, when every GAG basic guideline is assessed, then the audit doc records a status and evidence for each.
- AC-2 manual Given any failing guideline, when the audit completes, then a bead exists for it with priority and a link from the audit doc.
- AC-3 manual Given the audit, when complete, then zero failures are left without a decision (fix, defer with reason, or N/A).
Waits on
- mw-e31.11 Make every menu keyboard- and gamepad-operable with visible focus done
- mw-e31.2 Let players remap keyboard, mouse and gamepad bindings done
- mw-e31.4 Scale UI text 80–200% with readable defaults done
- mw-e31.5 Caption gameplay-critical sounds with direction indicators done
area:qaepic:e31milestone:m4needs-human
E32Performance & Browser CompatibilityStable frame pacing and fast loading on the hardware baseline across current desktop Chrome, Edge, Firefox and Safari, protected by automated budgets so regressions are caught in PRs rather than playtests.4/19 done · 21%
M1 Grey-box slice 6
mw-e32.2Benchmark sim tick cost in CI with regression alerts
Description
Why
The sim runs every tick regardless of GPU; its cost is measurable headlessly and must stay well inside the frame budget.
What
vitest benchsuites for representative sim loads (500 entities with propagation, 30 AI agents perceiving, 200 physics props) with a budget: mean tick ≤ 4 ms in Node on CI hardware. CI compares against main baseline artifact and fails on > 25% regression.Scope
- In: Bench suites, baseline artifact, comparison script
- Out: Frame-time budgets (e32-perf-budgets)
Notes
- None.
Acceptance criteria
- AC-1 perf Given the "full-load" bench, when run in CI, then mean tick ≤ 4 ms and p99 ≤ 8 ms.
- AC-2 unit Given baseline 2.0 ms and PR 2.6 ms, when compared, then the check fails (30% > 25%); at 2.4 ms it passes.
- AC-3 unit Given no baseline available, when compared, then it records a new baseline and passes with a warning.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfepic:e32milestone:m1mw-e32.4Run smoke suite on Chromium, Firefox and WebKit in CI
Description
Why
Target browsers are current Chrome, Edge, Firefox and Safari; engine-specific breakage must be caught before players see it.
What
Playwright projects for chromium, firefox, webkit running the boot + testbed smoke specs in CI (chromium required per-PR; firefox/webkit required on main and nightly, optional per-PR label
full-matrix). Known-difference allowlist per browser with bead references.Scope
- In: Playwright project matrix, CI job(s), docs of manual Edge/Safari checks
- Out: Mobile browsers (mw-e36)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the smoke suite, when run on chromium, firefox and webkit, then the testbed boots with zero console errors on all three.
- AC-2 integration Given a nightly run failing on webkit only, when complete, then a GitHub issue is opened/updated with the failing spec and trace artifact.
- AC-3 integration Given a known-difference allowlist entry without a bead id, when validated, then CI fails.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
area:qaepic:e32milestone:m1mw-e32.5Detect GPU/browser capabilities and pick a safe default preset
Description
Why
First impressions matter: a low-end laptop should start on Low, and unsupported browsers need a clear message.
What
On boot detect WebGL2 (and WebGPU if the ADR uses it), WASM + SIMD, max texture size,
deviceMemory,hardwareConcurrency, renderer string (where exposed); choose default preset (High/Low) on first launch; show an unsupported screen listing what is missing; log capabilities to local telemetry.Scope
- In: Capability probe, preset heuristic table, unsupported screen
- Out: Dynamic resolution at runtime (e32-quality-presets)
Notes
- None.
Acceptance criteria
- AC-1 unit Given capabilities {deviceMemory: 4, renderer contains "Intel"}, when the heuristic runs, then Low is chosen; given an Apple M1 Pro renderer string, then High.
- AC-2 e2e Given WebGL2 unavailable (init-script override), when loaded, then the unsupported screen names WebGL2 and links to browser help.
- AC-3 unit Given the renderer string is masked/unavailable, when the heuristic runs, then it falls back to deviceMemory/cores and never throws.
- AC-4 unit Given the player has already chosen a preset, when the game boots again, then detection does not override it.
Waits on
- mw-e00.19 Bootstrap the chosen renderer and physics with a boot smoke test done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:gameepic:e32milestone:m1mw-e32.10Show a dev perf HUD with frame graph, draw calls and heap
Description
Why
Developers need to see performance cost while building content, not after CI complains.
What
As a developer, I want an overlay showing fps, frame-time graph (last 240 frames), sim tick ms, draw calls, triangles, textures/geometries count and JS heap so that I can spot regressions immediately.
Toggle via debug console or F3; dev builds only.
Scope
- In: Overlay, renderer stats adapter
- Out: Automated budgets (e32-perf-budgets)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given a dev build, when F3 is pressed, then the HUD shows fps, frame-time graph, sim ms, draw calls and heap values that update each frame.
- AC-2 integration Given a production build, when F3 is pressed, then nothing happens and HUD code is absent from the bundle.
- AC-3 unit Given
performance.memoryis unavailable (Firefox/Safari), when the HUD renders, then heap shows "n/a" without errors.
Waits on
- mw-e00.19 Bootstrap the chosen renderer and physics with a boot smoke test done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e32milestone:m1mw-e32.16Chart reference perf results from perf/results as a trend
Description
Why
mw-e32.1 commits each reference run as perf/results/<date>-<sha>.json so performance can be followed over time, but nothing draws those results yet; a trend nobody can see does not catch slow drift (constitution §10, good browser performance).
What
As a developer, I want a trend chart of reference-mode frame time built from perf/results so that I can see whether the perf-baseline scene is getting slower across commits.
A page published with the site on every merge to main plots p50/p95 frame time per committed reference result (x: date/commit, y: ms) with the 16.7 ms budget line from perf/perf-budgets.json.
Scope
- In: reading perf/results/*.json (the PerfRunReport format in scripts/perf/report.ts), a static chart page in the site build, a unit-tested data loader.
- Out: running the reference suite (mw-e32.15), CI-mode software frame time (compared against main per PR in mw-e32.1).
Notes
Results files are written by e2e/perf/reporter.ts; frames[].frame holds the percentiles.
Acceptance criteria
- AC-1 unit Given perf/results files from two or more reference runs, when the trend data is built, then it lists p50 and p95 per run in date order with the commit SHA.
- AC-2 unit Given a malformed or non-reference results file, when the trend data is built, then it is skipped with a warning naming the file rather than breaking the build.
- AC-3 e2e Given the built site, when the trend page loads, then it shows one point per committed result and the 16.7 ms budget line, with no console errors.
Waits on
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfepic:e32milestone:m1milestone:m4mw-e32.19Re-encode music-open-world.m4a under the 3 MB audio loop cap
Description
Why
music-open-world.m4a is 3.65 MB, over the 3 MB audio loop cap; it is waived in perf/size-budgets.json (mw-e32.3).
What
Re-encode the approved take at a lower AAC bitrate (or trim the loop) so it fits, then delete the waiver.
Scope
- In: the m4a encode; waiver removal
- Out: the general compression pipeline (mw-e32.7)
Acceptance criteria
AC-1 integration Given the production build, when pnpm size:check runs without the music-open-world waiver, then every audio loop is at most 3 MB.
area:perfepic:e32milestone:m1milestone:m4
M3 MVP content 7
mw-e32.6Implement Low/High graphics presets and dynamic resolution
Description
Why
The baseline defines two presets; they must actually hit 60 fps on their target hardware.
What
As a player, I want Low/High presets plus individual toggles and optional dynamic resolution so that the game runs smoothly on my machine.
Preset controls render scale, shadow resolution/cascades, max dynamic lights, post-processing, particle density, LOD bias, draw distance. Dynamic resolution adjusts render scale between 50–100% to hold a frame-time target, with hysteresis.
Scope
- In: Preset table, settings UI section, dynamic resolution controller (pure logic unit-tested)
- Out: Asset LODs creation (art epics)
Notes
- None.
Acceptance criteria
- AC-1 unit Given frame times consistently 20 ms with target 16.7 ms, when the controller updates, then render scale steps down by ≤ 10% per second and never below 50%.
- AC-2 unit Given frame times oscillating around target ±0.5 ms, when the controller runs, then scale changes at most once per 2 s (hysteresis).
- AC-3 perf Given the combat stress scene on Low at 1080p on an Iris Xe / base M1 class machine, when measured, then p50 ≥ 60 fps and p95 frame time ≤ 33.3 ms.
- AC-4 e2e Given a preset switch at runtime, when applied, then it takes effect within 1 s without reload or console errors.
Waits on
- mw-e00.19 Bootstrap the chosen renderer and physics with a boot smoke test done
- mw-e32.5 Detect GPU/browser capabilities and pick a safe default preset open
area:renderepic:e32milestone:m3mw-e32.7Stream and compress assets with a progress-reporting loader
Description
Why
Load to playable ≤ 10 s requires compressed assets and loading only what the current area needs.
What
Asset manifest generated at build (id → hashed URL, size, area tags); area-based preloading with priority; texture compression (KTX2/Basis), mesh compression (meshopt or Draco per ADR), audio as Opus/AAC with fallback; loading screen showing real progress; failed-fetch retry with backoff.
Scope
- In: Manifest build step, loader with priorities/retries, compression tooling scripts
- In: Loading screen progress
- Out: Service-worker/HTTP caching (e32-asset-caching)
Notes
- None.
Acceptance criteria
- AC-1 perf Given a cold load at 50 Mbps throttling in CI mode, when Briar-Glen-equivalent testbed area loads, then playable ≤ 10 s.
- AC-2 unit Given a fetch failing twice then succeeding, when loading, then the loader retries with backoff and completes; after 3 failures it surfaces a retry UI.
- AC-3 unit Given the manifest, when an asset id is missing, then a typed error names the id at build time (build fails).
- AC-4 e2e Given the loading screen, when assets load, then the progress bar increases monotonically from 0 to 100%.
Waits on
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
area:perfepic:e32milestone:m3mw-e32.8Cache hashed assets for warm reload ≤ 3 s
Description
Why
Warm reload ≤ 3 s keeps the death/retry and playtest loops snappy.
What
Immutable caching for content-hashed assets (Pages headers are limited, so add a service worker with cache-first for hashed URLs and network-first for index.html); versioned cache names with cleanup of old versions on activate; opt-out via
?nosw.Scope
- In: Service worker (Workbox or hand-rolled), update flow ("new version available — reload")
- Out: Offline play guarantee (nice side effect, not a goal)
Notes
- None.
Acceptance criteria
- AC-1 perf Given a previously loaded build, when the page is reloaded, then load to playable ≤ 3 s on the reference machine.
- AC-2 e2e Given a new deployment, when the page loads, then the old cache version is deleted after activation and a "new version available" prompt appears.
- AC-3 e2e Given
?nosw, when loaded, then no service worker is registered and existing ones are unregistered.
Waits on
- mw-e32.7 Stream and compress assets with a progress-reporting loader open
area:perfepic:e32milestone:m3mw-e32.9Eliminate frame hitches from GC, shader compilation and long tasks
Description
Why
Stable frame pacing matters more than average fps for combat feel (parry timing).
What
Detect and fix hitch sources: shader/pipeline pre-warm during loading, object pooling for projectiles/particles/events on hot paths, long-task detection in dev overlay, allocation audit of the sim tick (target near-zero allocations per tick).
Scope
- In: Shader pre-warm, pools, allocation test for sim tick
- Out: Perf HUD (e32-perf-hud)
Notes
- None.
Acceptance criteria
- AC-1 perf Given the combat stress scene for 60 s on the reference machine, when measured, then frames > 33 ms number ≤ 3 and none > 50 ms after warm-up.
- AC-2 unit Given 1,000 steady-state sim ticks, when heap allocations are measured in Node (
--expose-gcheap delta), then growth ≤ 64 KB. - AC-3 e2e Given the first cast of each VFX type after loading, when measured, then no frame exceeds 33 ms (pre-warm effective).
Waits on
- mw-e00.20 Bind fixed-step sim to rAF rendering with interpolation done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfepic:e32milestone:m3mw-e32.12Detect memory leaks across repeated scene loads
Description
Why
Area transitions and reloads after death repeat hundreds of times per session; leaks compound into crashes.
What
Playwright test (Chromium, CDP heap) loads/unloads testbed scenes and save/load cycles 20 times, forcing GC, and asserts heap growth and GPU resource counts stay bounded.
Scope
- In: Leak test spec, renderer resource counters
- Out: Fixing specific leaks (bug beads)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given 20 scene load/unload cycles, when heap is sampled after forced GC, then growth from cycle 2 to cycle 20 ≤ 5%.
- AC-2 e2e Given 20 save/load cycles, when renderer geometry/texture counts are read, then they return to the cycle-2 value (±2).
- AC-3 integration Given a deliberately leaking fixture, when the test runs, then it fails (test proves it can detect leaks).
Waits on
- mw-e30.1 Define versioned save format with section registry and migrations done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:qaepic:e32milestone:m3mw-e32.13Add combat stress scene with 12 creatures and heavy VFX
Description
Why
The worst case — a chaotic fight with spells flying — is what players remember; budgets must be measured there.
What
Scene
perf-combatin the greybox kit: 12 active creatures in combat AI, 4 simultaneous spell effects, 30 physics props, fire propagation active, 8 dynamic lights. Scripted deterministic camera path and input replay so measurements are repeatable. Added to both perf modes.Scope
- In: Scene data, scripted replay, perf-budgets.json entries
- Out: Creature content (mw-e13)
Notes
- None.
Acceptance criteria
- AC-1 perf Given
perf-combaton the reference machine (High), when measured over 1,800 frames, then p95 frame time ≤ 16.7 ms. - AC-2 perf Given
perf-combaton Low at 1080p on the low-tier class machine, when measured, then worst-case frames stay ≥ 30 fps (p99 ≤ 33.3 ms). - AC-3 unit Given the scripted replay, when run twice, then sim state hashes match (repeatable measurement).
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
area:perfepic:e32milestone:m3mw-e32.11Recover gracefully from WebGL/GPU context loss
Description
Why
GPU resets happen (driver updates, sleep/wake); losing a session to a black screen is avoidable.
What
Handle
webglcontextlost/restored(or WebGPU device lost): pause sim, show overlay, rebuild GPU resources from the asset cache on restore, resume; if restore fails within 10 s, autosave (if safe) and offer reload.Scope
- In: Context-loss handler, resource rebuild path
- Out: Save logic (e30-autosave)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given
WEBGL_lose_context.loseContext()thenrestoreContext(), when triggered in the testbed, then rendering resumes within 5 s with the same sim state hash as before loss. - AC-2 e2e Given the context is never restored, when 10 s pass, then an autosave is attempted and a reload prompt appears.
- AC-3 e2e Given loss during loading, when restored, then loading completes without duplicate resources or errors.
Waits on
- mw-e00.19 Bootstrap the chosen renderer and physics with a boot smoke test done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
area:renderepic:e32milestone:m3
M4 Fun validation 2
mw-e32.14Run and record a full perf pass on reference and low-tier machines
Description
Why
Absolute frame-time budgets cannot run on GPU-less CI; a documented manual pass before playtests closes the loop.
What
Write
docs/perf/runbook.md(machine prep, browser versions,pnpm perf:refsteps) and execute it on the reference machine and a low-tier machine across Chrome, Edge, Firefox, Safari; commit results toperf/results/; file beads for any budget misses.Scope
- In: Runbook doc, committed results, beads for misses
- Out: Fixes (separate beads)
Notes
- None.
Acceptance criteria
- AC-1 manual Given the runbook, when executed on the reference machine in all 4 browsers, then results files are committed with browser/OS versions.
- AC-2 perf Given the results, when compared to the baseline, then every budget passes or has a filed bead with owner and priority.
- AC-3 manual Given the low-tier machine run, when complete, then Low preset results are recorded for
perf-baselineandperf-combat.
Waits on
- mw-e32.13 Add combat stress scene with 12 creatures and heavy VFX open
- mw-e32.4 Run smoke suite on Chromium, Firefox and WebKit in CI open
- mw-e32.6 Implement Low/High graphics presets and dynamic resolution open
area:perfepic:e32milestone:m4needs-humanmw-e32.17Firefox warns about the shadow map's depth texture filtering on every scene
Description
Firefox 159 logs 'WebGL warning: drawElementsInstanced: Depth texture comparison requests (e.g. LINEAR) Filtering, but behavior is implementation-defined...' once on every scene, the plain testbed (?scene=testbed) included, so any Firefox smoke that fails on console warnings (mw-e32.4) will fail on it. It comes from the soft shadow map (PCF sampling a depth texture with a linear filter), not from the magical materials; found while running mw-e29.7's material test scene in Firefox, where e2e/magic-materials.spec.ts lets it through. Decide whether to switch the shadow map type or accept the warning in the cross-browser smoke.
area:perfarea:renderclass:allepic:e32milestone:m4
E33Developer/Content ToolsDevelopers and designers can create, tune and debug spells, creatures, loot, encounters, dialogue and puzzles in minutes, with visual insight into the systemic simulation and one-keypress bug capture.4/17 done · 24%
M1 Grey-box slice 7
mw-e33.2Pause, slow-motion and frame-step the simulation
Description
Why
Tuning parry windows and debugging propagation needs time slowed or stepped one tick at a time.
What
As a developer, I want to pause, change timescale (0.1×–4×) and step single ticks so that I can inspect fast interactions.
Implemented in the game loop (sim steps at fixed dt; timescale changes how many steps per real second), so determinism is preserved.
Scope
- In: Loop timescale, pause, step; console commands and hotkeys (dev)
- Out: Replay scrubbing (possible follow-up)
Notes
- None.
Acceptance criteria
- AC-1 unit Given timescale 0.5, when 1 real second passes at 60 Hz, then 30 sim steps occur.
- AC-2 unit Given paused, when "step" is invoked 3 times, then exactly 3 ticks advance.
- AC-3 integration Given a session played at mixed timescales and recorded, when replayed at 1×, then the final state hash matches.
Waits on
- mw-e00.20 Bind fixed-step sim to rAF rendering with interpolation done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m1mw-e33.3Inspect and edit entity components live
Description
Why
Systemic bugs ("why didn't the rope burn?") are solved by seeing the properties involved.
What
As a developer, I want to click an entity and see/edit its components live so that I can debug systemic interactions.
Pick via raycast (or list/search), panel showing components with schema-driven editors (number, bool, enum, vector), edits applied through a
debug.setComponentsim command (recorded), highlight selected entity.Scope
- In: Picker, inspector panel, edit command
- Out: World overlays (e33-world-overlays)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the testbed, when a crate is clicked in inspect mode, then its components (e.g. Position, Physics) are listed with current values updating live.
- AC-2 unit Given an edit setting a numeric field to a string, when applied, then schema validation rejects it and the sim is unchanged.
- AC-3 integration Given an edit to
flammable=true, when recorded and replayed, then the replay reproduces the edited state. - AC-4 e2e Given the selected entity is destroyed, when the next frame renders, then the panel shows "entity destroyed" instead of throwing.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m1mw-e33.4Add `pnpm content:check` validator with cross-reference report
Description
Why
Designers need fast, readable validation of all content before committing; CI needs the same check.
What
CLI wrapping the content pipeline: validates all content types, cross-references (loot tables → items, spawns → creatures, dialogue → quests), orphan detection (defined but never referenced), string-table key existence; human-readable output with file:line,
--jsonfor CI annotations; runs in CI jobcontent.Scope
- In: CLI, CI job, GitHub annotation formatter
- Out: Schemas themselves (owned by respective epics)
Notes
- None.
Acceptance criteria
- AC-1 integration Given all committed content, when
pnpm content:checkruns, then it exits 0 in ≤ 10 s. - AC-2 unit Given a loot table referencing a missing item, when checked, then it exits non-zero printing the file, JSON path and missing id.
- AC-3 unit Given an item defined but never referenced by any loot table, shop or placement, when checked with
--orphans, then it is listed as a warning (not an error). - AC-4 integration Given
--json, when run in CI with an error, then a GitHub annotation appears on the offending file.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
area:toolsepic:e33milestone:m1mw-e33.5Scaffold new content entries with a test stub via CLI
Description
Why
Every content entry must have a test (contract §3); scaffolding makes the right thing the easy thing.
What
pnpm new <type> <id>createscontent/<type>/<id>.jsonfrom a per-type template (valid defaults) and a test stubtest/content/<type>/<id>.test.tsthat exercises the entry viadescribeContent; refuses duplicate ids; validates id format (kebab-case).Scope
- In: CLI, per-type templates registered alongside schemas
- Out: GUI editors (not planned for MVP)
Notes
- None.
Acceptance criteria
- AC-1 integration Given
pnpm new testprop lantern, when run, then the JSON and test stub are created,pnpm content:checkpasses and the new test runs green. - AC-2 unit Given an id that already exists, when run, then it exits non-zero without overwriting.
- AC-3 unit Given id
Big Lantern, when run, then it rejects with a kebab-case suggestionbig-lantern.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
area:toolsepic:e33milestone:m1mw-e33.6Hot-reload content JSON into the running game
Description
Why
Tuning spells and creatures by editing JSON and seeing the change in under a second is the core of fast content iteration.
What
As a designer, I want edits to content files to apply to the running game without restart so that tuning is instant.
Vite HMR for
content/**→ re-validate the changed file → swap catalogue entry → notify systems (content.on("changed", id)) which refresh live instances where safe (e.g. spell damage, creature speed); invalid edits keep the old version and show an overlay error.Scope
- In: HMR handler, catalogue swap, change notifications, error overlay
- Out: Production (dev only)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the dev server and testbed running, when a testprop JSON field is edited, then the change is visible in-game within 1 s without page reload.
- AC-2 e2e Given an edit introducing a schema error, when saved, then an overlay shows the error with file and path and the game keeps running on the previous valid data.
- AC-3 unit Given a hot-swapped entry, when the content hash is recomputed, then it changes and the replay recorder marks the recording as content-modified.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
area:toolsepic:e33milestone:m1mw-e33.8Capture a reproducible bug report with one keypress
Description
Why
Emergent systems create hard-to-reproduce bugs; a deterministic replay of the last minutes turns "it happened once" into a failing test.
What
As a developer or playtester, I want to press F9-dev (configurable) to save a bug bundle so that the exact situation can be replayed.
The game continuously keeps a rolling buffer: a save snapshot every 60 s plus input commands since. Capture writes
bug-<timestamp>.mwbug(snapshot + inputs + build SHA + content hash + settings + recent console/errors + screenshot + optional note).?bug=<file>or drag-drop loads and replays it;pnpm bug:test <file>converts it into a golden replay test.Scope
- In: Rolling buffer, bundle format (zod), loader, conversion to test
- Out: Telemetry export bundles (e34-playtest-export) — reuse format pieces
Notes
- None.
Acceptance criteria
- AC-1 integration Given 3 minutes of play then capture, when the bundle is replayed on a fresh load of the same build, then the final state hash equals the captured final hash.
- AC-2 unit Given a bundle from a different build SHA, when loaded, then the user is warned that replay may diverge and divergence is reported at the first checkpoint mismatch.
- AC-3 perf Given the rolling buffer active, when measured in the testbed, then added cost is ≤ 0.2 ms per frame and memory ≤ 20 MB.
- AC-4 integration Given
pnpm bug:test, when run on a bundle, then a replay test file is generated that passes.
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e30.1 Define versioned save format with section registry and migrations done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m1mw-e33.7Add named teleport bookmarks and dev state snapshots
Description
Why
Reaching the room you are working on in seconds, in the right world state, removes most iteration friction.
What
As a developer, I want named bookmarks and instant dev snapshots so that I can jump to a place and state repeatedly.
Bookmarks as content (
content/dev/bookmarks.json) + consoletp <name>;snap save <name>/snap load <name>store full save envelopes in a dev IndexedDB store; URL?snap=<name>boots into a snapshot.Scope
- In: Bookmarks, snapshot commands, URL param
- Out: Player save slots (e30-save-slots)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given bookmark
arena-center, whentp arena-centerruns, then the player is placed there next tick. - AC-2 integration Given
snap save athen changes thensnap load a, when compared, then the state hash equals the snapshot's. - AC-3 integration Given a snapshot from an older save schema, when loaded, then save migrations apply; if they fail, a clear error is shown.
Waits on
- mw-e30.1 Define versioned save format with section registry and migrations done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m1
M2 Class fantasies 4
mw-e33.9Visualize perception cones, alert states and noise events
Description
Why
Stealth that feels fair requires designers to see exactly what creatures perceive and why.
What
As a designer, I want overlays for sight cones, hearing radii, current alert state, last-known-position markers, search paths and noise events so that I can tune stealth encounters.
Overlay toggles per category; reads sim data only (no sim changes).
Scope
- In: Overlay renderer for AI/perception data, toggles via console
- Out: AI logic (mw-e11)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given a guard in the testbed with the overlay on, when the player is in the sight cone, then the cone renders and the guard's alert label changes as the state progresses.
- AC-2 e2e Given a noise event, when emitted, then an expanding ring shows its propagated radius for 1 s.
- AC-3 integration Given overlays toggled on vs off, when the same replay runs, then sim state hashes are identical (overlay is read-only).
Waits on
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e11.17 AI debug overlay: sight cones, hearing, states, LKP and paths done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m2mw-e33.10Visualize light levels, element propagation and trigger volumes
Description
Why
The systemic world (fire, water, light, triggers) is invisible data until visualized; debugging emergent interactions depends on it.
What
As a designer, I want heatmap overlays for light level, temperature/fire, wetness/electricity and trigger volume outlines so that I can see why systemic interactions do or do not happen.
Grid-sampled overlays from sim queries; property-tag filter ("show all flammable").
Scope
- In: Light field heatmap, element overlays, trigger volume wireframes, property filter
- Out: The systems themselves (mw-e03)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the light overlay in a room with one torch, when shown, then sampled values decrease with distance from the torch.
- AC-2 e2e Given the property filter
flammable, when enabled, then exactly the entities with that tag are highlighted. - AC-3 integration Given overlays on vs off, when the same replay runs, then state hashes are identical.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e03.29 Show properties, element fields and light levels in a debug overlay done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m2mw-e33.11Create a spell sandbox scene with live parameter tuning
Description
Why
Magic must feel magical; designers need a playground to cast every spell against every material and creature.
What
As a designer, I want a sandbox with material stations (water, oil, wood, rope, metal) and dummies where I can cast any spell and tweak its parameters live so that I can find surprising combinations.
Scene
sandbox-spells, consolelearnall,cast <spellId>, parameter panel bound to hot-reloadable spell data.Scope
- In: Sandbox scene, commands, parameter panel
- Out: Spell content (mw-e08)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given
?scene=sandbox-spellsandlearnall, when opened, then every spell in content is castable from a list. - AC-2 e2e Given a spell's radius changed in the panel, when cast again, then the new radius applies without reload.
- AC-3 content Given the sandbox scene, when validated, then it contains at least one entity for every property tag defined in e03-world-properties.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e33.6 Hot-reload content JSON into the running game open
area:toolsepic:e33milestone:m2mw-e33.12Create a creature arena to spawn and pit creatures
Description
Why
Creatures are creatures, not target dummies; their behaviours and relationships need a place to be observed.
What
As a designer, I want an arena where I can spawn any creature with chosen disposition/faction and watch interactions so that I can tune behaviours.
Scene
sandbox-creatures, commandsspawn <creature> --faction --disposition,ai freeze/unfreeze,faction set.Scope
- In: Arena scene, commands
- Out: AI implementation (mw-e11/mw-e12)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the arena, when
spawn goblin-grunt --disposition hostileandspawn wolf --disposition neutralrun, then both spawn with those dispositions visible in the inspector. - AC-2 unit Given
ai freeze, when ticks advance, then frozen creatures' AI systems do not update while physics still does. - AC-3 unit Given a creature id with invalid disposition, when spawned, then validation fails with allowed values listed.
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m2
M3 MVP content 2
mw-e33.13Preview dialogue graphs and edit quest/world state in-game
Description
Why
Branching quests with multiple resolutions are untestable by playing through every path manually.
What
As a narrative designer, I want a dialogue previewer and a quest/world-fact editor so that I can test any branch instantly.
Dialogue previewer runs a dialogue graph standalone with editable condition inputs (disposition, facts, inventory). Quest panel lists quests, states, objectives; set state/fact via recorded debug commands. Graph lint: unreachable nodes, dead ends without exit.
Scope
- In: Previewer panel, quest/fact panel, graph lint in content:check
- Out: Runtimes (e22-dialogue-runtime, e23-quest-runtime, e27-world-state-store)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given a dialogue id, when opened in the previewer with disposition=friendly, then only nodes whose conditions pass are selectable.
- AC-2 unit Given a dialogue graph with an unreachable node, when linted, then content:check reports the node id.
- AC-3 integration Given
quest set <id> <state>, when executed, then dependent world facts update and the change is recorded in the replay.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
area:toolsepic:e33milestone:m3mw-e33.14Simulate loot tables and report drop distributions
Description
Why
Exploration should produce interesting discoveries; designers need to see actual distributions, not guess from weights.
What
CLI
pnpm loot:sim <tableId> --rolls 10000 --seed 1prints item frequencies, expected gold, and flags items with 0 drops or duplicates of "unique" items; also available as console command.Scope
- In: CLI + console command using sim loot roller with seeded RNG
- Out: Loot runtime (e18-loot-tables)
Notes
- None.
Acceptance criteria
- AC-1 unit Given a table with weights 1:3, when 10,000 rolls simulate with seed 1, then frequencies are 25% ± 1.5% and 75% ± 1.5% and the output is identical across runs.
- AC-2 unit Given a unique item that can drop twice in one roll, when simulated, then it is flagged.
- AC-3 unit Given an unknown table id, when run, then it exits non-zero listing close matches.
Waits on
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e33.4 Add `pnpm content:check` validator with cross-reference report open
area:toolsepic:e33milestone:m3
E34Telemetry & PlaytestingPrivacy-first, local-first, opt-in measurement of deaths, abandoned encounters, spell usage, puzzle completion and other signals that complements — never replaces — direct observation of players.0/8 done · 0%
M2 Class fantasies 1
mw-e34.1Define local-first telemetry event schema, store and export
Description
Why
Measuring deaths, abandonments and spell usage guides design — but this is a free game and players' privacy comes first.
What
Add
src/game/telemetry: a zod-typed event catalogue ({type, v, sessionId (random per session), tick, simTimeMs, area, payload}), a typedemit()API, a bounded local ring buffer in IndexedDB (max 50k events / 20 MB, oldest dropped), and export to NDJSON (gzip). No network code in this item. Events are only recorded if telemetry is enabled (e34-consent) except a small in-memory buffer used for bug reports.Scope
- In: Event catalogue, emitter, IndexedDB store, export, clear-all
- In: Schema rule: no free-text string fields except enumerated ids (enforced by schema test)
- Out: Consent UI (e34-consent)
- Out: Specific gameplay events (e34-gameplay-events)
- Out: Any upload (e34-opt-in-upload)
Notes
- Session id is random, not derived from device; no user id, no IP, no fingerprinting data (UA only as coarse browser family + version).
Integration note: Moved to m2 — e04-skeleton-fun-validation (m2) consumes it.
Acceptance criteria
- AC-1 unit Given an event missing a required payload field, when emitted, then it is dropped and a dev-only warning logged (never throws into gameplay).
- AC-2 unit Given the ring buffer at capacity, when a new event is stored, then the oldest is evicted and count stays at the cap.
- AC-3 unit Given the event catalogue, when a schema-lint test inspects it, then no event has an unconstrained free-text string field.
- AC-4 integration Given 1,000 stored events, when exported, then the NDJSON file has 1,000 valid lines that re-parse against the schema.
- AC-5 e2e Given telemetry disabled (default), when playing for 2 minutes, then nothing is written to the telemetry IndexedDB store and zero network requests carry telemetry.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:gameepic:e34milestone:m2
M3 MVP content 4
mw-e34.2Add opt-in telemetry consent, privacy notice and data controls
Description
Why
Privacy-first: players must knowingly opt in, see exactly what is recorded, and delete it anytime.
What
As a player, I want to choose whether gameplay statistics are recorded, see what is recorded and delete it so that I stay in control.
Off by default. First-launch (or playtest-mode) prompt explains local-first recording; settings toggle; "View my data" (event counts by type + sample), "Export", "Delete all".
docs/privacy.mdpublished with the site.Scope
- In: Consent prompt, settings controls, privacy doc
- Out: Upload consent (separate, e34-opt-in-upload)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given a fresh profile, when the game starts, then telemetry is off until the player explicitly opts in.
- AC-2 e2e Given telemetry on with stored events, when "Delete all" is confirmed, then the store is empty and the counts show 0.
- AC-3 e2e Given the player opts out after opting in, when playing further, then no new events are stored.
- AC-4 manual Given docs/privacy.md, when reviewed, then it lists every event type and field recorded and states nothing leaves the device without a separate explicit action.
Waits on
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:uiepic:e34milestone:m3mw-e34.3Emit core gameplay events: deaths, encounters, spells, puzzles
Description
Why
E34 outcome names these signals explicitly: deaths, abandoned encounters, spell usage and puzzle completion.
What
Instrument:
session_start/end(class, build),death(cause type, killer content id, area, position bucketed to 2 m),encounter_start/complete/abandon(encounter id, duration, resolution: combat/stealth/social/avoid),spell_cast(spell id, target type, outcome tags),puzzle_start/solve/abandon(puzzle id, solution path id),ability_used(class verb id),setting_changed(difficulty/assist ids only),item_acquired(source: loot/shop/steal).Scope
- In: Event definitions + emit points in game glue listening to sim events
- In: Encounter/puzzle id conventions in content
- Out: Analysis (e34-analysis-scripts)
Notes
- Emit from game layer listening to the sim event bus — never call telemetry from src/sim.
- Abandon = left encounter/puzzle volume for > 60 s without resolution (configurable).
Acceptance criteria
- AC-1 integration Given the testbed with a scripted fight replay ending in player death, when run, then exactly one
deathevent is recorded with cause, killer id and 2 m-bucketed position. - AC-2 integration Given the player leaves an encounter volume for 61 s unresolved, when evaluated, then
encounter_abandonis emitted once; returning at 59 s emits nothing. - AC-3 integration Given a puzzle solved via an alternative solution path, when emitted, then
puzzle_solve.solutionPathidentifies that path id. - AC-4 unit Given telemetry disabled, when sim events fire, then emitters short-circuit without allocation-heavy work (≤ 0.01 ms per event measured).
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:gameepic:e34milestone:m3mw-e34.4Capture uncaught errors locally for playtest bundles
Description
Why
Playtesters rarely report console errors; capturing them locally makes crashes diagnosable without third-party services.
What
Global
error/unhandledrejectionhandlers record{message, stack (sanitized of URLs query strings), buildSha, area, tick}into a small local error log (always on, local only, max 200 entries) shown in the bug/feedback bundle and "View my data".Scope
- In: Handlers, sanitizer, local log
- Out: Remote error reporting services (not used)
Notes
- None.
Acceptance criteria
- AC-1 unit Given a thrown error with a stack containing
?token=abc, when sanitized, then query strings are stripped. - AC-2 e2e Given an unhandled promise rejection, when it occurs, then an entry is logged and the game continues running.
- AC-3 unit Given 250 errors, when logged, then only the latest 200 are kept and repeated identical errors are counted rather than duplicated.
Waits on
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:gameepic:e34milestone:m3mw-e34.5Add an in-game feedback hotkey capturing context and a note
Description
Why
Players' moments of delight or confusion are best captured in the moment, tied to where and when they happened.
What
As a playtester, I want to press a key, type a quick note and tag it (bug / confusing / fun / idea) so that my feedback carries its context.
Captures screenshot, area, position, tick, recent 60 s of events (if enabled), optional bug bundle (e33-replay-bug-capture); stored locally and included in the playtest export.
Scope
- In: Feedback overlay, local storage, inclusion in export
- Out: Uploading (e34-opt-in-upload)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the feedback key pressed, when the overlay opens, then the game pauses and resumes on close.
- AC-2 e2e Given a submitted note tagged "fun", when exported, then the bundle contains the note, tag, screenshot and area/tick.
- AC-3 unit Given a note longer than 2,000 characters, when submitted, then it is truncated with an indicator.
Waits on
- mw-e34.1 Define local-first telemetry event schema, store and export open
area:uiepic:e34milestone:m3
M4 Fun validation 2
mw-e34.6Export a single playtest bundle file for testers to send
Description
Why
External testers need a one-click way to share everything useful, without accounts or servers.
What
As a playtester, I want one "Export playtest data" button producing one file so that sending results to the team is trivial.
Bundle
.mwplay(zip): telemetry NDJSON, error log, feedback notes + screenshots, settings, build SHA, capability summary, optional latest save and bug bundles. Shows file size and a contents preview before saving.Scope
- In: Bundle writer (zip in browser), preview dialog
- Out: Analysis (e34-analysis-scripts)
Notes
- None.
Acceptance criteria
- AC-1 integration Given a session with events, feedback and errors, when exported, then the bundle contains each component and a manifest listing them with counts.
- AC-2 integration Given telemetry disabled, when exported, then the bundle omits telemetry and the manifest says so.
- AC-3 unit Given the bundle manifest schema, when an exported bundle is parsed by the analysis tooling, then it validates.
Waits on
- mw-e34.3 Emit core gameplay events: deaths, encounters, spells, puzzles open
- mw-e34.4 Capture uncaught errors locally for playtest bundles open
- mw-e34.5 Add an in-game feedback hotkey capturing context and a note open
area:gameepic:e34milestone:m4mw-e34.7Aggregate playtest bundles into a death heatmap and usage report
Description
Why
Telemetry only matters if it turns into decisions; the team needs a quick report per playtest round.
What
pnpm playtest:report <dir>reads N.mwplaybundles and writes a single-file HTML report: death heatmap over top-down area maps, encounter resolution breakdown (combat/stealth/social/avoid) and abandonment rates, spell usage frequency per class, puzzle solve rates and solution-path distribution, session length, feedback notes by tag with screenshots.Scope
- In: Node aggregation (100% covered pure functions), HTML report generator
- In: Top-down map images generated from greybox scenes
- Out: Qualitative observation notes (e35 protocol)
Notes
- None.
Acceptance criteria
- AC-1 unit Given 3 fixture bundles with known deaths, when aggregated, then per-area death counts and heatmap bins match expected values.
- AC-2 unit Given a corrupt or wrong-version bundle among valid ones, when processed, then it is skipped with a warning and the report still builds.
- AC-3 integration Given the report, when opened in Playwright offline, then it renders all sections with zero console errors.
- AC-4 unit Given encounters resolved 5 combat / 3 stealth / 2 avoided, when aggregated, then the breakdown shows 50/30/20%.
Waits on
- mw-e34.6 Export a single playtest bundle file for testers to send open
area:toolsepic:e34milestone:m4
Post-MVP 1
mw-e34.8Offer optional, explicit upload of a playtest bundle
Description
Why
Manual file sending adds friction for remote testers; an explicit upload button can help, but only with separate consent.
What
As a playtester, I want to optionally upload my bundle with one click after reviewing it so that I do not have to email files.
Separate consent per upload (preview + "Upload" button), minimal endpoint (e.g. serverless function writing to private storage) with size limit and rate limiting, no cookies, delete-by-receipt-code.
Scope
- In: Upload UI, endpoint spec, ADR on hosting choice
- Out: Automatic/background upload (never)
Notes
- Deferred post-MVP: file export covers MVP playtests. Requires secret handling for the endpoint — no secrets in the repo.
Acceptance criteria
- AC-1 e2e Given the upload dialog, when the player does not press Upload, then zero network requests leave the page.
- AC-2 integration Given a bundle > 25 MB, when upload is attempted, then it is refused client-side with a message.
- AC-3 integration Given a successful upload, when complete, then a receipt code is shown that can be used to request deletion.
Waits on
- mw-e34.6 Export a single playtest bundle file for testers to send open
area:infraepic:e34milestone:post-mvp
E35MVP Fun ValidationExternal players demonstrate that combat, magic, stealth and exploration are intrinsically enjoyable, measured against fun hypotheses written before testing.0/10 done · 0%
M1 Grey-box slice 4
mw-e35.1Pre-register measurable fun hypotheses for MVP validation
Description
Why
Fun validation is only honest if success criteria are written before playtests, not rationalized after.
What
Write
docs/playtesting/fun-hypotheses.md: per pillar (combat intrinsically fun, magic feels magical, stealth satisfying, world rewards curiosity, classes change possibilities) define hypotheses with measures and thresholds, e.g. "≥ 70% of testers rate fighting a skeleton ≥ 4/5 with loot/XP disabled", "≥ 3 self-reported 'Wait… I can do that?' moments per hour (median)", "≥ 25% of dungeon encounters resolved without combat across thief players", "≥ 60% of sorcerers try a spell on the environment unprompted within 15 min". Includes go/no-go rule.Scope
- In: Hypotheses doc with measures, thresholds, data source (survey/observation/telemetry)
- Out: Running tests (later e35 items)
Notes
- None.
Acceptance criteria
- AC-1 manual Given the doc, when reviewed, then each constitution design pillar has ≥ 1 hypothesis with a numeric threshold and named data source.
- AC-2 manual Given the doc, when merged, then it defines an explicit go/no-go rule (e.g. which hypotheses must pass).
- AC-3 manual Given a hypothesis relying on telemetry, when reviewed, then the corresponding event exists or is listed as a needed bead.
area:designepic:e35milestone:m1mw-e35.2Write playtest protocol, consent form, surveys and observation sheet
Description
Why
Telemetry must not replace direct observation; a consistent protocol makes sessions comparable.
What
docs/playtesting/protocol.md: session script (setup, minimal briefing, think-aloud prompts, no-help rule, time-boxes), observer sheet (timestamped notes, "confusion/delight/wait-I-can-do-that" tags aligned with feedback tags), post-session survey (Likert items mapped to hypotheses + open questions), consent/recording form template, remote-session variant.Scope
- In: Protocol, templates in docs/playtesting/templates/
- Out: Recruitment (e35-recruit-testers)
Notes
- None.
Acceptance criteria
- AC-1 manual Given the survey, when mapped to fun-hypotheses.md, then every survey-sourced hypothesis has a corresponding question.
- AC-2 manual Given a dry-run session with one internal tester, when completed, then the protocol fits in 60 minutes and gaps found are fixed in the doc.
- AC-3 manual Given the consent form, when reviewed, then it states what is recorded, how long it is kept, and how to withdraw.
Waits on
- mw-e35.1 Pre-register measurable fun hypotheses for MVP validation open
area:qaepic:e35milestone:m1mw-e35.3Deploy playtest builds to GitHub Pages with build IDs
Description
Why
Testers need a stable URL per round and bug reports must identify the exact build.
What
CI workflow deploys the game to Pages under
/play/(latest main) and/play/<tag>/for tagged playtest releases (playtest-rN), alongside backlog.html. Build SHA + tag shown in the title screen corner and embedded in bundles;?debug=1enables console on playtest builds.Scope
- In: Pages workflow extension (shared Pages site with backlog)
- In: Version stamping
- In: Release notes from CHANGELOG
- Out: Hosting elsewhere (itch.io mirror optional later)
Notes
- None.
Acceptance criteria
- AC-1 integration Given tag
playtest-r1pushed, when the workflow runs, then/play/playtest-r1/serves that build and/play/still serves main. - AC-2 e2e Given a deployed build, when loaded, then the title screen shows the tag/SHA matching the commit.
- AC-3 integration Given a backlog-only workflow run, when deploying, then existing
/play/builds are preserved (no Pages artifact clobbering).
Waits on
- mw-e00.11 Generate backlog.html from issues.jsonl and deploy to GitHub Pages done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
area:infraepic:e35milestone:m1mw-e35.4Internal playtest: is greybox combat fun with no rewards?
Description
Why
Constitution: fighting a skeleton should be enjoyable even without XP or loot — test this at greybox, when it is cheapest to fix.
What
Run 5 internal/friendly sessions of the m1 greybox fight with loot/XP disabled using the protocol; record ratings and observations; report in
docs/playtesting/reports/m1-combat.mdwith recommended changes filed as beads.Scope
- In: Sessions, report, beads for findings
- Out: Implementing fixes
Notes
- None.
Acceptance criteria
- AC-1 manual Given 5 sessions, when completed, then the report includes per-tester combat fun rating, median, and top 3 frictions with timestamps.
- AC-2 manual Given the median rating < 4/5, when the report is written, then it proposes specific changes as beads before m2 begins.
- AC-3 manual Given the report, when merged, then every finding links to a bead or is marked won't-fix with a reason.
Waits on
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e35.2 Write playtest protocol, consent form, surveys and observation sheet open
area:qaepic:e35milestone:m1needs-human
M2 Class fantasies 1
mw-e35.5Internal playtests proving each class fantasy in greybox
Description
Why
m2 exit: each class's core verbs must feel like its fantasy before content production scales up.
What
Per class (knight, archer, sorcerer, thief): 3 sessions on a greybox encounter with multiple routes; measure route choice, verb usage (telemetry), and fantasy rating; report
docs/playtesting/reports/m2-classes.md.Scope
- In: 12 sessions, telemetry report, written findings, beads
- Out: Content (m3)
Notes
- None.
Integration note: Dropped hard dependency on e34-analysis-scripts (m4); m2 sessions are analysed from exported logs by hand.
Acceptance criteria
- AC-1 manual Given 3 sessions per class, when completed, then each class has a fantasy rating and a list of verbs used vs available.
- AC-2 manual Given a class whose testers used < 50% of its core verbs unprompted, when reported, then discoverability beads are filed.
- AC-3 integration Given the sessions' playtest bundles, when
pnpm playtest:reportruns, then per-class ability usage charts are included in the report.
Waits on
- mw-e04 E04 — Knight Combat open
- mw-e05 E05 — Archery open
- mw-e06 E06 — Magic Framework open
- mw-e10 E10 — Thief Toolkit open
- mw-e35.4 Internal playtest: is greybox combat fun with no rewards? open
area:qaclass:allepic:e35milestone:m2needs-human
M4 Fun validation 5
mw-e35.6Recruit external playtesters and schedule sessions
Description
Why
External players, not the team, must demonstrate the game is fun.
What
Recruit ≥ 16 external testers (two rounds of ≥ 8) spanning genre veterans and casual players, with at least 2 using accessibility settings; screener form, scheduling, consent collection; tracking sheet (no personal data in the repo).
Scope
- In: Screener, outreach, scheduling
- Out: Paid recruitment platforms (only if needed)
Notes
- None.
Acceptance criteria
- AC-1 manual Given recruitment, when complete, then ≥ 16 consenting testers are scheduled across two rounds with a mix recorded as aggregate counts only.
- AC-2 manual Given the repo, when checked, then no tester personal data (names, emails) is committed.
- AC-3 manual Given no-shows exceed 25% in a round, when detected, then backup testers are scheduled to maintain ≥ 8 sessions.
Waits on
- mw-e35.2 Write playtest protocol, consent form, surveys and observation sheet open
area:qaepic:e35milestone:m4needs-humanmw-e35.7Run external playtest round 1 on the MVP build
Description
Why
The first honest measurement of the fun hypotheses with players who owe us nothing.
What
Run ≥ 8 sessions with the protocol on a tagged
playtest-r1build; collect bundles, observer notes and surveys; producedocs/playtesting/reports/r1.mdscoring each hypothesis.Scope
- In: Sessions, report with hypothesis scorecard
- Out: Triage (e35-findings-triage)
Notes
- None.
Acceptance criteria
- AC-1 manual Given ≥ 8 completed sessions, when the report is written, then each pre-registered hypothesis has a measured value and pass/fail.
- AC-2 integration Given the collected bundles, when the analysis script runs, then the report embeds its heatmap and usage sections.
- AC-3 manual Given a session with a crash, when reported, then its error log and bug bundle are attached to a bead.
Waits on
- mw-e34.7 Aggregate playtest bundles into a death heatmap and usage report open
- mw-e35.3 Deploy playtest builds to GitHub Pages with build IDs open
- mw-e35.6 Recruit external playtesters and schedule sessions open
area:qaepic:e35milestone:m4needs-humanmw-e35.8Triage round-1 findings into prioritized beads
Description
Why
Findings are worthless unless they become prioritized work before round 2.
What
Cluster observations, survey comments and telemetry anomalies into issues; file beads with evidence links; prioritize by hypothesis impact; select the set to fix before round 2.
Scope
- In: Triage doc section in r1 report, beads
- Out: Fixes themselves
Notes
- None.
Acceptance criteria
- AC-1 manual Given the r1 report, when triaged, then every failed hypothesis has at least one linked bead with a proposed change.
- AC-2 manual Given the triaged beads, when prioritized, then a "before round 2" set is labelled and each has an owner/estimate.
- AC-3 manual Given a finding judged out of scope, when recorded, then the reason is written in the report.
Waits on
- mw-e35.7 Run external playtest round 1 on the MVP build open
area:designepic:e35milestone:m4mw-e35.9Run external playtest round 2 to validate fixes
Description
Why
Confirms changes improved the experience rather than just changing it.
What
Run ≥ 8 sessions with new testers on
playtest-r2; same protocol and analysis; report r2 with deltas vs r1 per hypothesis.Scope
- In: Sessions, r2 report with r1→r2 deltas
- Out: Decision (e35-mvp-go-no-go)
Notes
- None.
Acceptance criteria
- AC-1 manual Given ≥ 8 sessions with testers not in round 1, when reported, then each hypothesis shows r1 value, r2 value and delta.
- AC-2 manual Given a hypothesis that regressed, when reported, then the likely cause is analysed and a bead filed.
- AC-3 integration Given r1 and r2 bundles, when the analysis script runs in comparison mode, then side-by-side charts are produced.
Waits on
- mw-e35.8 Triage round-1 findings into prioritized beads open
area:qaepic:e35milestone:m4needs-humanmw-e35.10Record MVP go/no-go decision against fun hypotheses
Description
Why
Constitution: mobile and expansion only after the MVP proves the game is fun; this is that gate.
What
ADR
docs/adr/NNNN-mvp-go-no-go.mdapplying the pre-registered go/no-go rule to r2 results: go (proceed to post-MVP: E36 etc.), iterate (another round with named changes), or pivot; lists evidence and next steps.Scope
- In: ADR with evidence links
- Out: Post-MVP planning itself
Notes
- None.
Acceptance criteria
- AC-1 manual Given r2 results, when the ADR is written, then it applies the pre-registered rule verbatim and states the outcome.
- AC-2 manual Given an "iterate" outcome, when recorded, then the ADR names the hypotheses to re-test and the changes planned.
- AC-3 manual Given a "go" outcome, when recorded, then mw-e36 stubs are unblocked for re-planning.
Waits on
- mw-e35.9 Run external playtest round 2 to validate fixes open
area:designepic:e35milestone:m4
E36Mobile AdaptationOnly after MVP validation: touch UX, mobile performance profiles, interface redesign and mobile browser support.0/4 done · 0%
Post-MVP 4
mw-e36.1Spike: mobile browser feasibility and target devices
Description
Why
Mobile only after MVP validation; before committing, measure what mobile browsers can actually run.
What
Time-box 2 days: run the MVP build on representative iOS Safari and Android Chrome devices, measure fps/memory/load on a mobile preset, identify blockers (memory limits, WASM, audio unlock, fullscreen/orientation); ADR recommending target devices and scope.
Scope
- In: Measurements, ADR
- Out: Implementation
Notes
- None.
Acceptance criteria
- AC-1 manual Given 2 iOS and 2 Android devices, when the MVP build runs, then fps, memory and load time are recorded in the ADR.
- AC-2 manual Given a device that crashes or fails to load, when recorded, then the cause (memory, API) is identified or marked unknown with next steps.
- AC-3 manual Given the ADR, when merged, then it states go/no-go for mobile and target device floor.
Waits on
- mw-e35.10 Record MVP go/no-go decision against fun hypotheses open
area:perfepic:e36milestone:post-mvpmw-e36.2Design touch controls for movement, camera and class verbs
Description
Why
Keyboard/mouse verbs must map to touch without losing class expressiveness.
What
As a mobile player, I want virtual stick, camera drag and contextual verb buttons so that I can play every class on touch. Coarse placeholder — split after the feasibility spike.
Scope
- In: Touch input adapter onto the action map, layout customization
- Out: Anything before e36-mobile-feasibility
Notes
- None.
Acceptance criteria
- AC-1 e2e Given touch emulation, when the virtual stick is dragged, then the player moves proportionally.
- AC-2 e2e Given simultaneous stick + camera drags (multi-touch), when performed, then both apply independently.
- AC-3 manual Given each class, when played on a phone for 10 minutes, then its core verbs are usable (rated ≥ 3/5 by testers).
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e36.1 Spike: mobile browser feasibility and target devices open
area:uiepic:e36milestone:post-mvpmw-e36.3Create a mobile performance preset and memory budget
Description
Why
Mobile GPUs and memory limits (esp. iOS Safari) need their own budgets.
What
Mobile preset (render scale, shadows off/low, reduced particles, texture downscale), memory budget per spike findings, added to perf suite. Coarse placeholder.
Scope
- In: Preset, budgets, perf suite entries
- Out: Desktop presets
Notes
- None.
Acceptance criteria
- AC-1 perf Given the target floor device, when the combat stress scene runs on the mobile preset, then p95 frame time ≤ 33.3 ms.
- AC-2 perf Given iOS Safari, when played for 20 minutes, then the tab is not killed for memory (heap within the spike-defined budget).
- AC-3 unit Given a desktop capability profile, when presets are chosen, then the mobile preset is never auto-selected.
Waits on
- mw-e32.6 Implement Low/High graphics presets and dynamic resolution open
- mw-e36.1 Spike: mobile browser feasibility and target devices open
area:perfepic:e36milestone:post-mvpmw-e36.4Redesign HUD and menus for small touch screens
Description
Why
Desktop menus and dense HUDs are unusable at phone sizes with touch targets.
What
As a mobile player, I want HUD and menus sized for touch so that I can manage inventory, books and dialogue comfortably. Coarse placeholder.
Scope
- In: Touch-target sizing (≥ 44 px), portrait/landscape handling, safe areas
- Out: Desktop UI
Notes
- None.
Acceptance criteria
- AC-1 e2e Given a 390×844 viewport, when each menu renders, then all interactive targets are ≥ 44×44 CSS px and nothing overflows.
- AC-2 e2e Given a device rotation, when it occurs, then layout adapts without losing game state.
- AC-3 e2e Given a notched device safe area, when the HUD renders, then no HUD element is under the notch.
Waits on
- mw-e31.11 Make every menu keyboard- and gamepad-operable with visible focus done
- mw-e36.1 Spike: mobile browser feasibility and target devices open
area:uiepic:e36milestone:post-mvp
E37Art Direction & Asset PipelineThe Vesper Bell has one coherent, readable, performant visual identity — a hand-painted, chunky low-poly storybook world — produced by a solo developer with GPT image generation, CC0 kits and one image-to-3D tool, and every art asset flows through a validated, licence-tracked pipeline from prompt to runtime.3/432 done · 1%
M1 Grey-box slice 8
mw-e37.2Asset validator: naming, budgets and licence entries in CI
Description
Why
A solo developer cannot manually police hundreds of assets. Budgets and licences must be enforced by machine or they will drift — and a licence mistake in a public repo is expensive to undo.
What
As a developer, I want
pnpm assets:validateto check every asset against the bibles so that CI rejects misnamed, over-budget or unlicensed assets. Checks: asset-id naming regex and category prefix; source/runtime folder mirroring;assets/CREDITS.mdrow exists with an allowed licence and required fields; glTF budgets (triangles, materials, bones, texture sizes by asset class per style bible §13.2); texture power-of-two + KTX2 for runtime; filled prompt files contain the verbatim preamble; placeholder flags reported.Scope
- In: Validator module in
src/tools/assets/with pure functions (parsing CREDITS table, naming, budget rules as data) - In: glTF inspection via a glTF parsing library (no renderer)
- In: CI job + JSON/markdown report
- In: Budget table as a data file shared with docs
- Out: Audio-specific checks (see
e38-audio-validator, which extends this) - Out: Import/compression (see
e37-model-import-pipeline)
Notes
Owner requirement: 100 % tested (lines/branches/functions/statements) even though
src/toolsnormally needs ≥ 90 % — add the path to the 100 % group in coverage config. Budget classes are inferred from the asset-id prefix (model-char-*,model-creature-*,model-prop-*) with an override in a sidecar JSON.Acceptance criteria
- AC-1 unit Given asset ids
model-creature-minotaur-01andModel_Minotaur, when validated, then the first passes and the second fails with a message naming the naming rule. - AC-2 unit Given a runtime asset with no matching
assets/CREDITS.mdrow, or a row whose licence isCC-BY-NC-4.0, when validated, then validation fails listing the asset id and reason. - AC-3 unit Given a glTF fixture for a standard creature with 7 000 triangles, 2 materials or a 2048² texture, when validated against the creature budget (≤ 6 k tris, 1 material, ≤ 512²), then each violation is reported separately with actual vs limit.
- AC-4 unit Given a filled prompt file whose preamble differs from style bible §14 by one character, when validated, then it fails and shows the first differing position.
- AC-5 integration Given the CI pipeline, when a PR adds an invalid asset, then the
assets:validatejob fails and uploads a markdown report artifact. - AC-6 unit Edge: given a malformed CREDITS table row (missing columns) or a corrupt .glb, when validated, then the validator reports a parse error for that file and continues validating the rest instead of crashing.
- AC-7 unit Given the validator source, when coverage runs, then lines, branches, functions and statements are all 100 %.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
area:artarea:toolsepic:e37milestone:m1- In: Validator module in
mw-e37.3Record owner approval gates and block unapproved assets in CI
Description
Why
Claude generates the assets; the owner's taste is exercised only at approval gates (flat image, 3D turntable, music take, SFX batch). Those decisions must be durable and machine-checked, or unapproved generations will slip into the game.
What
As the owner, I want every approval I give recorded with the exact file version so that nothing I have not approved can ship. Add
assets/approvals.json(zod schema): entries of{ assetId, gate: "image"|"turntable"|"music-take"|"sfx-batch", version, sha256, decision: "approved"|"rejected", approver, date, note }; a small CLI (pnpm assets:approve <asset-id> <gate> <version> [--reject --note]) that hashes the candidate and appends the record; and a validator rule: any non-placeholder runtime asset must haveapprovedrecords for every gate its category requires, matching the committed source file hash.Scope
- In: Schema + CLI in
src/tools/assets/approvals/ - In: Required gates per category as data (images: image; 3D: image + turntable; music: music-take; SFX: sfx-batch)
- In: Validator rule wired into
assets:validate - Out: The preview UIs themselves (turntable: see
e37-turntable-preview) - Out: Licence checks (see
e37-asset-validator)
Notes
100 % coverage (same rule as the validator). The approver field is a name/handle only — no emails or secrets.
Acceptance criteria
- AC-1 unit Given a final 3D asset with an approved
imagerecord but noturntablerecord, when validated, then validation fails naming the missing gate. - AC-2 unit Given an approval record whose sha256 does not match the committed source file (file changed after approval), when validated, then validation fails with "approval stale" and both hashes.
- AC-3 unit Given
pnpm assets:approve model-creature-minotaur-01 turntable v3, when run, then a schema-valid record with the file sha256 and ISO date is appended and the file is otherwise unchanged. - AC-4 unit Given a placeholder asset (
placeholder: yesin CREDITS), when validated, then no approval is required. - AC-5 unit Edge: given a rejected record followed by an approved record for a later version, when validated, then only the approved version's hash is accepted; a malformed approvals.json fails with a parse error location.
- AC-6 unit Given the approvals module, when coverage runs, then all four metrics are 100 %.
Waits on
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
area:artarea:toolsepic:e37milestone:m1- In: Schema + CLI in
mw-e37.4Image generation runner wrapping codex exec with Images API fallback
Description
Why
Claude generates all 2D images; the owner only approves. A reliable, logged runner turns a filled prompt file into a versioned candidate image with provenance, so image "generate" tasks are Claude-run rather than needs-human.
What
As a developer, I want
pnpm assets:gen-image <prompt-file>to run the OpenAI Codex CLI non-interactively (codex exec, signed in with the owner's ChatGPT subscription, built-in gpt-image tool), pick up the new image from~/.codex/generated_images/, and copy it toassets/_incoming/<asset-id>/vN.pngwithvN.jsonrecording prompt file path + hash, tool, model, date and attempt count. On usage-limit errors: exponential backoff with jitter and resumable batch queue. Fallback: OpenAI Images API whenOPENAI_API_KEYis set and subscription generation is unavailable/rate-limited beyond a threshold.Scope
- In: Runner in
src/tools/assets/image-gen/with the codex process and HTTP behind injectable interfaces - In: Prompt-file parsing (extract filled prompt block; verify verbatim preamble before spending a generation)
- In: Batch mode over a list of prompt files with a max-per-session cap
- In: Versioning vN, never overwriting
- Out: Approval recording (see
e37-asset-approval-log) - Out: Post-processing/compression (see
e37-model-import-pipeline)
Notes
Owner requirement: 100 % coverage with the codex process mocked (spawn stub emitting stdout/exit codes; fake generated_images dir). Risk: ChatGPT subscription image usage limits — generate in planned batches, not bursts; the runner must stop cleanly and report remaining queue.
OPENAI_API_KEYread from env only, never logged (redact in error output). Transparent backgrounds: request in prompt; validate alpha channel for categories that need it.Acceptance criteria
- AC-1 unit Given a filled prompt file for
icon-item-lockpick-iron-01and a mocked codex run that writes one PNG to the generated-images dir, when the runner executes, thenassets/_incoming/icon-item-lockpick-iron-01/v1.pngandv1.json(prompt hash, tool "codex-gpt-image", model, ISO date) exist; a second run produces v2 without touching v1. - AC-2 unit Given a prompt file whose preamble differs from the style bible, when the runner starts, then it refuses before invoking codex and reports the first differing position.
- AC-3 unit Given codex exits with a usage-limit error twice then succeeds, when run, then the runner retries with exponential backoff (injected clock) and succeeds on attempt 3, recording attempts: 3.
- AC-4 unit Given usage-limit errors persist past the threshold and
OPENAI_API_KEYis set, when run, then the runner falls back to the mocked Images API and records tool "openai-images-api"; without the key it stops and reports the remaining batch queue. - AC-5 unit Edge: given codex exits 0 but produces no new image (or a non-PNG), when run, then the attempt is marked failed with a clear message and no empty version folder is left behind.
- AC-6 unit Given any error path, when logs are captured, then no API key or auth token value appears in output (redaction test).
- AC-7 unit Given the runner module, when coverage runs, then all four metrics are 100 %.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
area:artarea:toolsasset:conceptepic:e37milestone:m1- In: Runner in
mw-e37.5Review and finalize image prompt templates
Description
Why
Every per-asset prompt bead copies a template; errors or gaps in templates multiply across the whole asset set.
What
Review the 14 image templates in
assets/prompts/_templates/(concept-art, character-reference-sheet, creature-reference-sheet, environment-concept, tileable-texture, prop-sheet, ui-element, item-icon, spell-icon, npc-portrait, book-cover-and-page, vfx-flipbook-texture, skybox-backdrop, key-art-title-screen) andassets/prompts/README.mdagainst the signed-off style bible; fill one example per template category that has a first real asset (anchor assets for series: item icon, spell frame, portrait, UI panel).Scope
- In: Template edits to match the signed-off bible
- In: One worked example each for item-icon, spell-icon, npc-portrait and ui-element (these become the series anchors)
- In: README workflow review
- Out: Music/SFX templates (see
e38-prompt-templates) - Out: Per-asset prompt beads (later wave)
Notes
Templates carry
{{GPT_STYLE_PREAMBLE}}placeholders — the validator (e37-asset-validator) can check filled prompt files contain the verbatim preamble text.Acceptance criteria
- AC-1 content Given every file in
assets/prompts/_templates/for image categories, when inspected, then each contains the contract §6 fields: asset id, consuming bead(s), filled prompt with preamble placeholder, output spec, consistency checklist, iteration log and provenance table. - AC-2 manual Given the 4 worked examples, when Claude generates them via
e37-image-gen-runner, then each passes its own consistency checklist, is approved by the owner (recorded inassets/approvals.json) and is committed as the series anchor with a CREDITS row. - AC-3 content Given a filled prompt file, when the validator checks it, then it fails if the preamble text differs from style bible §14 by any character.
- AC-4 manual Edge: given a template whose instructions produced a failed generation twice (e.g. flipbook grid misalignment), when reviewed, then the template intro documents the workaround used.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artepic:e37milestone:m1mw-e37.6Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off
Description
Why
The owner will do NO 3D work: Claude must produce every model by API from owner-approved flat concept images, within budget on Iris Xe and consistent with the style bible. Hero creatures (the misunderstood minotaur) must be memorable, so they need bespoke models without hand-modelling.
What
Time-box 2 days. Question: how does Claude, with zero owner 3D work, source (a) bulk props/environments, (b) player/NPC characters with animations, and (c) hero/unique creatures, consistent with the style bible and licensable for a free public repo? Run a head-to-head Meshy API vs Tripo API bake-off using the SAME owner-approved GPT minotaur concept, scripted end-to-end by Claude (ad-hoc scripts in
spikes/3d-pipeline/), and validate the CC0-kit path with one character + one prop set. Output an ADR indocs/adr/naming ONE image-to-3D provider and at most ONE paid month / prepaid credit purchase, with rationale.Scope
- In: CC0 kit evaluation: KayKit (characters, dungeon, skeletons), Quaternius (modular village/dungeon, monsters, universal animation library), Kenney — style fit after palette recolour
- In: Meshy API vs Tripo API bake-off on one owner-approved GPT minotaur concept (generated with the creature template), each run image-to-3D → remesh/retopo → rig → animation → glb download
- In: Scoring rubric (below) and time log
- In: Round-trip into the renderer via the draft import path
- In: Animation source evaluation (CC0 animation libraries vs Mixamo)
- Out: Production import pipeline (see
e37-model-import-pipeline) - Out: Retargeting tooling (see
e37-animation-pipeline) - Out: Generating production hero assets (later prompt/generate beads)
Notes
Recommendation going in (to confirm or overturn with evidence):
- Default for bulk props, environments, modular architecture and most characters: CC0 low-poly kits (KayKit + Quaternius as the primary families, Kenney for gaps). They are already chunky/bevelled, use gradient/palette atlases that we recolour with
tex-palette-master-01, and carry zero licence risk. Mixing kit families is the main consistency risk — mitigated by one shared stylized material and palette remap. - Animations: CC0 first (KayKit character animations, Quaternius Universal Animation Library) retargeted to one humanoid skeleton. Mixamo only as fallback: its terms allow use in games but restrict redistributing raw animation files, and our repo is public — only commit animations baked into our own .glb, never raw FBX, and record the decision in the ADR.
- Hero/unique assets (minotaur, bespoke NPCs such as the bookseller, unique props): Meshy is the named candidate (owner preference), contested by Tripo in the bake-off. Flow: approved GPT reference sheet → image-to-3D → remesh/retopo to budget (≤ 20 k tris minotaur, 4–7 k key NPC) → auto-rig → export glTF → gltf-transform (meshopt + KTX2) → renderer.
- No DCC at all: the owner never opens Blender. Everything is API + gltf-transform CLI (simplify/weld/compress/pivot/scale fixes) scripted by Claude; if a model needs manual DCC fixes to be usable, that counts against it in scoring.
- Workflow (style bible §17.2): concept → OWNER GATE (flat image) → API image-to-3D → remesh/retopo → rig/animate via API → glb → validator → turntable preview → OWNER GATE (turntable) → commit + CREDITS + approvals.
Meshy facts (owner-verified, Sept 2026): Web app — Free = 100 credits/mo, outputs CC BY 4.0 (attribution in
assets/CREDITS.md+ in-game credits — acceptable for this free game); Pro = $20/mo ($240/yr), 1,000 credits/mo, private licence; image-to-3D 25 (Meshy 7) / 20 (Meshy 6), text-to-3D 20 (Meshy 6 only), AI texturing 10, remesh/rigging/animation free in-app. API (what Claude uses): prepaid API credits — image-to-3D 30 (2K textures), remesh 5, rigging 5, animation 3 per action ⇒ ≈ 46 credits for one hero with 2 actions. Confirm output licence for API-generated assets in the ADR.Tripo facts (Aug–Sep 2026, official pricing page blocked — medium confidence, re-verify): Web — Free 200–300 credits/mo, CC BY 4.0, ~15 downloads/mo cap; Pro $19.90/mo with ~3,000 credits. API: $0.01/credit — image-to-3D 20–30, smart low-poly retopo 30, auto-rig 25, animation retarget 10 each ⇒ ≈ 95–105 credits (≈ $1) per hero with 2 actions.
Bake-off scoring rubric (each 1–5, weighted): topology & tri count after reduction to budget (×3), texture fidelity vs style bible palette/painted look (×3), rig quality — deformation at shoulders/hips/knees in walk + attack (×2), time-to-glTF in our renderer incl. automated fixes (×2), API ergonomics for an unattended client — task submit/poll/download, errors, rate limits (×2), licence clarity for a public repo (×1), cost for the MVP hero set (×1). Same input image, same target budget.
Owner prerequisites: API keys in env (
MESHY_API_KEY,TRIPO_API_KEY) and a small prepaid credit top-up for each provider; keys never logged or committed.Decision output: one image-to-3D provider, at most one paid month / one prepaid credit purchase sized for the MVP hero set, with rationale and evidence files. Feeds
e37-3d-gen-api-client.Related: e39-story-bible (minotaur design intent).
Acceptance criteria
- AC-1 integration Given the owner-approved GPT minotaur concept (approval recorded), when Claude runs it through the Meshy API image-to-3D → remesh to ≤ 20 k triangles → rigging → one animation → glb download → KTX2/meshopt compression → import into the renderer test scene, then the model renders animated (idle + one attack) with no console errors, and elapsed time and credits used are logged in the ADR.
- AC-2 manual Given the SAME concept image, when run through the equivalent Tripo API pipeline (image-to-3D → smart low-poly retopo → auto-rig → retarget → glb → import), then its results are scored with the same rubric and both scorecards appear side by side in the ADR with turntable renders under
spikes/3d-pipeline/, and the owner picks a winner at a recorded turntable gate. - AC-3 manual Given one KayKit or Quaternius character and one prop set, when recoloured with the palette texture and rendered with the draft stylized material next to the minotaur, then the ADR records whether kit and generated assets read as one style (with screenshot).
- AC-4 content Given every spike output kept in the repo, when inspected, then each has an
assets/CREDITS.mdrow (tool, model, tier, date, CC BY 4.0 attribution text where applicable) and is flaggedplaceholder: yes. - AC-5 manual Given the ADR, when read, then it names exactly one image-to-3D provider, at most one paid month / credit purchase (with timing and size), the animation source (provider API vs CC0 library vs Mixamo with redistribution caveat), and confirms no step requires owner DCC work.
- AC-6 manual Edge: given an image-to-3D result that cannot reach the tri budget without visible collapse, a rig that breaks at the shoulders, or an API task that fails/times out, when scored, then the ADR documents the fallback (retry policy, CC0 kit recolour, or simplified hero concept) rather than accepting an over-budget asset.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m1mw-e37.7Placeholder asset kit: CC0 stand-ins at correct scale and naming
Description
Why
Gameplay is never blocked by final art (contract §5). Placeholders with the final naming, scale and pivots make integration a file swap later.
What
Assemble a placeholder kit from CC0 packs: one humanoid for each class (colour-coded by class accent), skeleton, goblin, wolf, spider, minotaur stand-in (scaled humanoid/ogre), and props for every Legibility cue (door, lever, chest, climbable ledge, flammable rope/barrel, breakable crate, weak wall, pushable block, lockable door), each named with its final asset id and placed under
assets/kits/+assets/source/.Scope
- In: CC0 selection and download, unmodified kits in
assets/kits/<kit>/with licence files - In: Scale (1 unit = 1 m), pivot at base centre, forward = +Z (or engine convention)
- In: CREDITS rows flagged
placeholder: yes - In: Kit catalogue page listing asset id → placeholder file
- Out: Greybox level geometry kit (owned by
e00-greybox-testbed) - Out: Final assets (per-asset beads)
Notes
Candidate sources: KayKit Adventurers/Skeletons/Dungeon, Quaternius Ultimate Monsters/Modular Dungeon, Kenney. Keep total kit ≤ 15 MB built.
Acceptance criteria
- AC-1 content Given the kit catalogue, when validated, then every placeholder has a final-format asset id, a CREDITS row with CC0-1.0 and
placeholder: yes. - AC-2 e2e Given the greybox testbed, when the kit showcase scene loads, then all placeholders render at correct relative scale (humanoid 1.7–1.9 m, minotaur stand-in 2.6–3.2 m) and screenshot sanity passes.
- AC-3 integration Given the built kit, when measured, then total runtime size ≤ 15 MB and every asset passes the validator budgets.
- AC-4 manual Given each Legibility cue prop, when viewed at 20 m on the Low preset, then the cue (cracks, pale ledge, straw/rope, brass keyhole) is identifiable by a second person.
- AC-5 integration Edge: given a placeholder swapped for a file with a different pivot or scale, when the validator runs, then it flags the pivot/scale deviation beyond 5 %.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
area:artepic:e37milestone:m1- In: CC0 selection and download, unmodified kits in
mw-e37.8Model import pipeline: glTF optimise, meshopt and KTX2 compression
Description
Why
The 50 MB initial download and Iris Xe texture memory budgets are only reachable if every model is optimised the same way, automatically.
What
As a developer, I want
pnpm assets:buildto turnassets/source/**models and textures into optimised runtime files underpublic/assets/so that I never hand-optimise. Steps: glTF/GLB/FBX-converted input → weld/dedup/prune → optional simplify to budget → meshopt compression → textures resized to tier (High/Low variants) and encoded KTX2 (ETC1S albedo, UASTC normals/UI) → validator run → manifest JSON (asset id → URLs, sizes, tris).Scope
- In: Node build script using gltf-transform (and KTX-Software/basis encoder)
- In: High/Low texture variants
- In: Incremental builds (hash cache)
- In: Manifest for the loader
- In: Loader smoke test in the renderer
- Out: Texture atlas packing (see
e37-texture-atlas-build) - Out: Animation retargeting (see
e37-animation-pipeline) - Out: Audio (see
e38-audio-import-pipeline)
Notes
Compression choice (meshopt vs Draco) follows
e00-engine-decision; default meshopt. The encoder binary must be installable on CI (pin version). Outputs are generated — do not hand-editpublic/assets/.Acceptance criteria
- AC-1 integration Given a source .glb with 2048² PNG textures, when
pnpm assets:buildruns, then the output .glb uses meshopt compression and KTX2 textures, with High (≤ tier size) and Low (half-size) variants, and the manifest lists both with byte sizes. - AC-2 integration Given no source changes, when the build runs a second time, then no files are rewritten and the run completes in ≤ 5 s for 100 cached assets.
- AC-3 e2e Given a built model in the greybox testbed, when the scene loads in Playwright, then the model renders with no console errors and a screenshot sanity check passes.
- AC-4 integration Given a model exceeding its budget, when the build runs, then the validator step fails the build and names the asset (no silent auto-decimation beyond the configured simplify ratio).
- AC-5 integration Edge: given a source file with a missing texture reference or an unsupported format, when built, then the build fails with the asset id and path and leaves previous outputs untouched.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
area:artarea:toolsepic:e37milestone:m1mw-e37.17Audit CREDITS.md and asset licences at each milestone exit
Description
Why
The game is free and the repo public: every committed asset is redistributed. One unlicensed file is a takedown risk for the whole project.
What
At each milestone exit (m1, m2, m3), reconcile every file under
assets/andpublic/assets/withassets/CREDITS.md; re-check tool terms for any tier-dependent rights (Suno, ChatGPT/gpt-image, Meshy/Tripo, SFX tool); confirm CC BY attributions appear on the in-game credits screen; list remaining placeholders.Scope
- In: Validator run + manual terms check
- In: Cross-check
assets/approvals.json: every final asset has approval records for all its gates - In: Audit log row in CREDITS.md
- In: In-game credits screen data generated from CREDITS.md (CC BY rows)
- Out: Replacing flagged assets (filed as beads)
Notes
This bead covers the m1 audit; clone it for m2/m3 exits (or reopen). Terms pages change — record the date read. Related: in-game credits screen UI (E31/E01 owners).
Acceptance criteria
- AC-1 integration Given the repository, when the validator runs in audit mode, then it lists every asset file lacking a CREDITS row and every row lacking a file, and exits non-zero if either list is non-empty.
- AC-2 content Given CREDITS rows with licence
CC-BY-4.0, when the credits data is generated, then each appears in the in-game credits data file with its attribution text. - AC-3 manual Given tier-dependent generated assets, when audited, then each row's tier/date is checked against the tool terms as of that date and the audit log records the terms URLs consulted.
- AC-4 manual Edge: given an asset whose licence cannot be confirmed, when audited, then it is flagged
placeholder: yesand a replacement bead is filed before the milestone is closed.
Waits on
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
area:artarea:musicarea:sfxepic:e37milestone:m1
M2 Class fantasies 36
mw-e37.21Generate player class characters 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 4 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-player-classes-turntable-approve)
Notes
Budget: 8–12 k tris LOD0 + LOD1 50 %, one 1024² atlas, 1 material, ≤ 65 bones (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 4 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets 8–12 k tris LOD0 + LOD1 50 %, one 1024² atlas, 1 material, ≤ 65 bones (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.20 Owner approval: player class characters flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.9Image-to-3D API client and CLI for the chosen provider
Description
Why
The owner does no 3D work; Claude produces every bespoke model by API. A tested, idempotent client makes 3D "generate" tasks Claude-run and repeatable rather than needs-human.
What
As a developer, I want
pnpm assets:gen-3d <asset-id> --from <approved-image>to submit an image-to-3D task to the provider chosen bye37-3d-pipeline-decision, poll to completion, chain remesh/retopo to the asset class tri budget, rigging and requested animations where applicable, download the .glb toassets/_incoming/<asset-id>/vN.glbwithvN.json(task ids, provider, model, credits used, date), then invoke the validator.Scope
- In: Client in
src/tools/assets/gen3d/behind a provider interface (Meshy and Tripo adapters; only the chosen one must be complete) - In: Submit, poll with backoff, download, retries on 5xx/429, timeouts
- In: Idempotency by asset id + source image hash (re-running resumes existing task ids instead of paying again)
- In: Refuses to run unless the source image has an approved
imagegate record - Out: Turntable rendering (see
e37-turntable-preview) - Out: Compression (see
e37-model-import-pipeline) - Out: Retargeting CC0 animation libraries (see
e37-animation-pipeline)
Notes
API key only from
MESHY_API_KEY/TRIPO_API_KEYenv vars — never logged, committed or written to vN.json. 100 % coverage with HTTP mocked (recorded fixture responses, no live calls in CI). Pricing for credit estimates (print before submit): Meshy API image-to-3D 30 (2K tex), remesh 5, rigging 5, animation 3/action; Tripo API $0.01/credit — image-to-3D 20–30, smart low-poly 30, auto-rig 25, retarget 10. A--dry-runprints the credit estimate only.Acceptance criteria
- AC-1 unit Given a mocked provider that returns a task id, two
IN_PROGRESSpolls thenSUCCEEDEDwith a model URL, whengen-3druns, then the .glb is downloaded toassets/_incoming/<asset-id>/v1.glband v1.json records task ids, credits and date. - AC-2 unit Given the same asset id and source image hash re-run after a crash mid-poll, when
gen-3druns, then it resumes polling the stored task id and submits no new paid task. - AC-3 unit Given 429 and 503 responses, when calls are made, then the client retries with exponential backoff up to the configured limit and then fails with a clear error naming the stage (submit/poll/remesh/rig/download).
- AC-4 unit Given a source image with no approved
imagegate record, whengen-3druns, then it refuses before any HTTP call. - AC-5 unit Given
--dry-runfor a creature with 2 animations on Meshy, when run, then it prints an estimate of 30 + 5 + 5 + 2×3 = 46 credits and makes no HTTP calls. - AC-6 unit Edge: given a missing
MESHY_API_KEY/TRIPO_API_KEY, or any error response, when logs are captured, then the key value never appears in output and the missing-key case exits non-zero with setup instructions. - AC-7 unit Given the client module, when coverage runs, then all four metrics are 100 %.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
area:artarea:toolsasset:modelepic:e37milestone:m2- In: Client in
mw-e37.10Headless turntable preview of a glb as PNG strip and GIF
Description
Why
The owner approves 3D models without opening any 3D tool. A consistent turntable rendered with the game's own material and lighting shows exactly what will ship.
What
As the owner, I want
pnpm assets:turntable <glb>to produce an 8-angle PNG contact strip and a looping GIF (plus a silhouette frame and a stats overlay: tris, materials, bones, texture sizes, budget pass/fail) so that I can approve or reject a model from an image. Runs the real renderer headless via Playwright with the shared stylized material, golden-hour preset, a 1.8 m scale figure, and optional animation frames.Scope
- In: Playwright-driven render page in
src/tools/turntable/ - In: Outputs next to the candidate:
vN-turntable.png,vN-turntable.gif,vN-silhouette.png - In: Animation sampling (idle + one action) when clips exist
- In: CI-safe (software GL/SwiftShader acceptable)
- Out: Approval recording (see
e37-asset-approval-log) - Out: In-game model viewer (Related: e33-debug-console)
Notes
Silhouette frame = solid ink on parchment at 64 px height (style bible §5 test). Uses
e37-stylized-materialonce available; before that, a basic lit material with the same light rig.Acceptance criteria
- AC-1 e2e Given a valid .glb fixture, when
assets:turntableruns headless, then it writes an 8-frame PNG strip, a looping GIF and a silhouette PNG, with no console errors. - AC-2 e2e Given a rigged fixture with an idle clip, when rendered with
--anim idle, then the strip contains sampled animation frames (frames differ pixel-wise from the bind pose). - AC-3 integration Given a model exceeding its budget, when rendered, then the stats overlay shows the failing budget in
emberred with actual vs limit. - AC-4 e2e Given the same fixture rendered twice, when compared, then outputs are pixel-identical (deterministic camera, lighting and animation sampling).
- AC-5 e2e Edge: given a corrupt .glb or missing textures, when rendered, then the command exits non-zero with the asset id and error, and writes no partial output files.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:renderarea:toolsasset:modelepic:e37milestone:m2- In: Playwright-driven render page in
mw-e37.11Character animation pipeline with retargeting to a shared humanoid rig
Description
Why
Four classes, NPCs and humanoid creatures need locomotion, combat and interaction animations; authoring per-character is impossible solo. Retargeting one library onto one skeleton makes every humanoid animate for free.
What
As a developer, I want animations from the chosen source (per
e37-3d-pipeline-decision) retargeted onto one canonical humanoid skeleton and packed into per-character clip sets so that any humanoid model gains the full move set. Includes clip naming (anim-humanoid-<action>-<nn>), root-motion policy, clip trimming/looping, and a clip manifest consumed by the renderer's animation layer.Scope
- In: Canonical skeleton definition + bone-name mapping tables (KayKit/Quaternius/Mixamo/Meshy/Tripo rigs)
- In: Batch retarget script (headless)
- In: Root-motion extraction or in-place policy
- In: Clip manifest with loop flags and event markers (footstep, hit frame)
- In: Hero creature (minotaur) rig handled as its own skeleton
- Out: Animation state machine/blending (render/game layer; Related: e02-player-controller)
- Out: Hand-keyed bespoke animations
Notes
Footstep and hit-frame markers feed audio (footstep surface set) and combat timing — markers must be data, not baked guesses. If Mixamo is used, raw FBX never enters the public repo (terms); only retargeted clips baked into our .glb.
Acceptance criteria
- AC-1 integration Given a source animation on a KayKit-style rig and a target model on the canonical skeleton, when the retarget script runs, then the output clip plays on the target with per-bone rotation error ≤ 2° versus the reference pose sample set.
- AC-2 unit Given the bone mapping tables, when a source rig is missing a mapped bone, then the script reports the missing bone and skips only affected tracks.
- AC-3 content Given the clip manifest, when validated, then every locomotion clip has
loop: true, every attack clip has ahitevent marker, and every walk/run clip has ≥ 2footstepmarkers. - AC-4 e2e Given each of the 4 class placeholders, when the animation showcase cycles idle/walk/run/attack/interact, then no clip errors appear in the console and screenshots show no collapsed limbs.
- AC-5 integration Edge: given a clip with root motion when the policy is in-place, when processed, then horizontal root translation is removed and stored as a velocity curve in the manifest.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artarea:toolsasset:modelepic:e37milestone:m2mw-e37.13Shared stylized lit material with ramp, rim and wet/emissive masks
Description
Why
One shader used by every model is the cheapest way to make kits, generated models and painted textures read as one world, and keeps the material count within budget.
What
As a player, I want every object lit in the same warm-key/cool-shadow painterly way so that the world feels hand-crafted and coherent. Soft 3-band ramp diffuse, shadow tint toward
dusk, rim light, optional wet mask (darker + sheen), emissive mask for glyphs/magic, per-location colour-grade LUT hook, High/Low variants.Scope
- In: Material implementation in
src/render/materials/ - In: Parameters exposed as data per location preset (style bible §3)
- In: Low variant without normal maps
- In: Visual regression screenshots
- Out: Lightmap baking tool choice (Related: e37-3d-pipeline-decision notes)
- Out: VFX shaders (Related: e29-vfx-framework)
Notes
Wet mask must be drivable at runtime by the
wetworld property (Related: e03-world-properties) so rain/water/ice states are visible.Acceptance criteria
- AC-1 e2e Given the material test scene, when rendered at High and Low, then screenshots match approved baselines within a 2 % pixel-difference threshold.
- AC-2 perf Given 200 instances of 20 distinct props using the material, when rendered on the reference machine (High), then GPU frame time for the scene ≤ 6 ms.
- AC-3 integration Given an object whose
wetproperty becomes true, when the next frame renders, then its albedo value drops by 20–30 % and specular sheen appears; reverting clears it. - AC-4 e2e Given the Low preset, when the material compiles, then no normal-map sampling occurs (shader variant check) and no console errors are logged.
- AC-5 manual Edge: given pure-black and pure-white albedo test textures, when lit, then shadows render toward
#3B3A5Aand highlights clamp below#FFF4DC-equivalent, never pure black/white.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
area:artarea:renderepic:e37milestone:m2- In: Material implementation in
mw-e37.14Legibility kit: visual cues for every exploitable world property
Description
Why
Systemic play ("Wait… I can do that?") only works if players can read what the world allows: climbable, flammable, breakable, movable, lockable, freezable, conductive, alternate routes (constitution §2, §8).
What
As a player, I want climbable ledges, flammable ropes, weak walls and other affordances to look consistently distinct so that I can discover solutions by observation. Build the style bible §7 cue set as reusable decals, trims and material variants, plus a validator rule linking world-property tags to required cues.
Scope
- In: Decals: cracks, scuffs, chalk thieves' marks, drag marks, oil stains
- In: Trim/material variants: pale ledge bevel, climb-ivy, tan hemp vs grey chain, pale breakable wood, brass wear
- In: Data rule: objects tagged
climbable/flammable/breakable… must reference a cue asset - In: Optional "highlight affordances" accessibility tint hook
- Out: Property simulation (see
e03-world-properties) - Out: Focus highlight UI (Related: e02-interaction)
Notes
Rule of the kit: a cue is never used decoratively. The content validator should flag a cue asset on an object lacking the matching property tag, and vice versa.
Acceptance criteria
- AC-1 content Given level/content data, when validated, then any object tagged
climbable,flammable,breakable,movableorlockablewithout its cue asset fails validation, and any cue asset on an object lacking the property also fails. - AC-2 manual Given the cue showcase scene on the Low preset, when 3 playtesters who have not read the bible are asked to identify climbable, flammable and breakable objects at 20 m, then ≥ 80 % of answers are correct.
- AC-3 e2e Given the "highlight affordances" setting enabled, when the showcase renders, then each cue category is tinted with its accessibility colour and screenshots match baselines.
- AC-4 manual Given deuteranopia and protanopia simulation filters, when the showcase is viewed, then every cue remains identifiable by shape/pattern.
- AC-5 content Edge: given an object whose property changes at runtime (a wall that has already been broken, a burnt rope), when its state updates, then its cue decal is removed or swapped so the world never advertises an affordance that no longer exists.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
area:artarea:designclass:allepic:e37milestone:m2mw-e37.15Build the style-target diorama as the visual and performance benchmark
Description
Why
A single finished "this is what the game looks like" slice settles style arguments, proves the pipeline end-to-end and becomes the perf benchmark before we art-up whole locations.
What
A small vertical diorama (~30 × 30 m): Briar Glen edge at golden hour — a cottage, a well that hides a tunnel, a stretch of forest, a ruined wall with a weak section, a climbable ledge, a flammable rope bridge, one class character, one creature, one spell VFX in flight, one lantern-lit shadow pocket. Built only from pipeline outputs (kits + generated textures + one hero model if available).
Scope
- In: Scene assembly with final-quality stand-ins
- In: High/Low presets
- In: Screenshot baselines and a camera flythrough path
- In: Perf capture on reference + Iris Xe-class device
- Out: Gameplay logic beyond walking around
- Out: Audio (see
e38-audio-style-target)
Notes
Treat as the living visual benchmark: later art passes are compared against it. Related: e24 Briar Glen level work reuses its pieces.
Acceptance criteria
- AC-1 perf Given the diorama flythrough on the reference machine at High (1440p-equivalent), when measured, then frame time p95 ≤ 16.7 ms, draw calls ≤ 250 and visible triangles ≤ 1.2 M.
- AC-2 perf Given the diorama on an Iris Xe-class device at Low (1080p), when measured, then frame time p95 ≤ 16.7 ms (floor ≤ 33.3 ms), draw calls ≤ 150, visible triangles ≤ 500 k.
- AC-3 integration Given the diorama package, when built, then its download size is ≤ 25 MB and every asset passes the validator.
- AC-4 manual Given the diorama screenshots, when reviewed against the style bible checklist (palette ratio, warm/cool, shape language, Legibility cues), then the owner signs off or files specific fix beads.
- AC-5 e2e Edge: given a texture or model missing at load, when the diorama loads, then a placeholder is shown, a warning is logged once, and the scene remains navigable.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.14 Legibility kit: visual cues for every exploitable world property open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artarea:perfepic:e37milestone:m2mw-e37.18Write player class characters concept prompts (4 images)
Description
Why
The four class fantasies (constitution pillar: every class is a different way to play) must read by silhouette alone in the class lineup and at 30 m; these are the most-seen models in the game and the first style anchor.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: character) fromcharacter-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Turnaround reference sheets for knight, archer, sorcerer and thief per style bible §5.1 (primary shape, signature prop, palette accent), then Claude-run image-to-3D, retopo to 8–12 k tris, one 1024² atlas, rig on the canonical humanoid skeleton.
Concept image ids (4):
char-knight-base-01char-archer-base-01char-sorcerer-base-01char-thief-base-01
Target model ids (4):
model-char-knight-01model-char-archer-01model-char-sorcerer-01model-char-thief-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-player-classes-3d-generate)
Notes
Milestone m2 (earlier than most assets) because the class-fantasy playtests (
e35-class-fantasy-tests) read much better with real silhouettes, and these sheets are style anchors fore37-asset-style-lock. Gameplay still runs one37-greybox-asset-kitplaceholders until integration. Held weapons are a separate set (e37-asset-weapons-*). Thief disguise recolour lives ine37-asset-thief-props-*.Budget: 8–12 k tris LOD0 + LOD1 50 %, one 1024² atlas, 1 material, ≤ 65 bones (style bible §13.2).
Acceptance criteria
- AC-1 content Given the 4 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (8–12 k tris LOD0 + LOD1 50 %, one 1024² atlas, 1 material, ≤ 65 bones (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.19Generate player class characters concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 4 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-player-classes-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 4 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.18 Write player class characters concept prompts (4 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.20Owner approval: player class characters flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 4 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-player-classes-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each class is identifiable by silhouette alone when filled solid ink at 64 px tall, and the four read as one family in a side-by-side lineup. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each class is identifiable by silhouette alone when filled solid ink at 64 px tall, and the four read as one family in a side-by-side lineup.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-player-classes-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.19 Generate player class characters concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelclass:allepic:e37milestone:m2needs-humanmw-e37.22Owner approval: player class characters turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 4 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-player-classes-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each class is identifiable by silhouette alone when filled solid ink at 64 px tall, and the four read as one family in a side-by-side lineup. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each class is identifiable by silhouette alone when filled solid ink at 64 px tall, and the four read as one family in a side-by-side lineup.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-player-classes-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.21 Generate player class characters 3D models via image-to-3D API in progress
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelclass:allepic:e37milestone:m2needs-humanmw-e37.23Integrate approved player class characters
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e19-class-definitions,e02-animation-hooks,e37-animation-pipeline,e37-stylized-material.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 4 models are underpublic/assets/model/within 8–12 k tris LOD0 + LOD1 50 %, one 1024² atlas, 1 material, ≤ 65 bones (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the class-select scene, when each class is chosen, then its final model loads, plays idle/walk/run/attack from the retargeted clip set with no console errors, and a Playwright screenshot shows all four in the lineup.
- AC-4 perf Given the combat stress scene with these models, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e02.6 Drive character animation from sim locomotion state and events done
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.22 Owner approval: player class characters turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.24Write school frames and glyphs prompts (26 assets)
Description
Why
Thirteen schools cannot be told apart by hue alone; the shared rim frame and glyph per school carry identity for colour-blind players (style bible §2.3, §9.2) and anchor every spell icon.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: icon-spell / ui) from templatespell-icon.md (frames) + ui-element.md (glyphs), with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.One painted medallion rim frame per school (school core/highlight/shadow colours, shape language from §2.3) and one flat ink-brush school glyph per school for grimoire ribbons, hotbar and book spines.
Asset ids (26):
ui-spell-frame-fire-01ui-spell-frame-frost-01ui-spell-frame-storm-01ui-spell-frame-arcane-01ui-spell-frame-illusion-01ui-spell-frame-alteration-01ui-spell-frame-conjuration-01ui-spell-frame-necromancy-01ui-spell-frame-nature-01ui-spell-frame-light-01ui-spell-frame-shadow-01ui-spell-frame-gravity-01ui-spell-frame-time-01ui-glyph-school-fire-01ui-glyph-school-frost-01ui-glyph-school-storm-01ui-glyph-school-arcane-01ui-glyph-school-illusion-01ui-glyph-school-alteration-01ui-glyph-school-conjuration-01ui-glyph-school-necromancy-01ui-glyph-school-nature-01ui-glyph-school-light-01ui-glyph-school-shadow-01ui-glyph-school-gravity-01ui-glyph-school-time-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-school-identity-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Frames are generated first because the spell-icon template attaches the school frame as a series anchor. Glyphs are flat single-colour 64² (§9.2 UI glyphs) and are vectorised to SVG during integration.
Acceptance criteria
- AC-1 content Given the 26 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (frames 1024² generate → 256²/128²/64² transparent PNG with an empty centre; glyphs flat single-colour ink on transparent, exported 64²) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the 13 frames are distinguishable in greyscale by shape/rim motif (not only hue) and every glyph reads at 32 px.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:iconclass:sorcererepic:e37milestone:m2mw-e37.25Generate school frames and glyphs via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 26 prompt files ofe37-asset-school-identity-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-school-identity-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: frames 1024² generate → 256²/128²/64² transparent PNG with an empty centre; glyphs flat single-colour ink on transparent, exported 64². Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 26 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (frames 1024² generate → 256²/128²/64² transparent PNG with an empty centre; glyphs flat single-colour ink on transparent, exported 64²), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.24 Write school frames and glyphs prompts (26 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:iconclass:sorcererepic:e37milestone:m2mw-e37.26Owner approval: school frames and glyphs
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 26 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-school-identity-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the 13 frames are distinguishable in greyscale by shape/rim motif (not only hue) and every glyph reads at 32 px. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 26 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the 13 frames are distinguishable in greyscale by shape/rim motif (not only hue) and every glyph reads at 32 px.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-school-identity-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.25 Generate school frames and glyphs via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconclass:sorcererepic:e37milestone:m2needs-humanmw-e37.27Integrate approved school frames and glyphs
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e07-grimoire-ui,e06-spell-hotbar-ui,e08-school-conventions.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 26 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the grimoire and hotbar, when a spell of each school is displayed, then its school frame and glyph render from the atlas and a greyscale screenshot comparison shows 13 distinct rim shapes.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e06.16 Select spells from a hotbar and quick-select wheel with mana HUD done
- mw-e07.10 Browse known spells, insights and verb chains in the grimoire open
- mw-e08.1 Document magic schools, spell data conventions and too-useful policy open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.26 Owner approval: school frames and glyphs open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconclass:sorcererepic:e37milestone:m2mw-e37.28Write spell icons prompts (23 assets)
Description
Why
The sorcerer picks spells from a hotbar in the middle of a fight; every icon must be readable at 48 px within a school frame (style bible §9.2).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: icon-spell) from templatespell-icon.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.One painted circular medallion per MVP spell (23): the approved school rim frame as anchor plus a single bold central symbol unique to the spell.
Asset ids (23):
icon-spell-fire-ember-01icon-spell-fire-firebolt-01icon-spell-fire-flame-jet-01icon-spell-fire-fire-wall-01icon-spell-frost-frostglass-01icon-spell-conjuration-wellspring-01icon-spell-storm-gust-01icon-spell-storm-thunderclap-01icon-spell-storm-arc-lash-01icon-spell-arcane-mage-hand-01icon-spell-arcane-telekinetic-throw-01icon-spell-arcane-blink-01icon-spell-arcane-arcane-sight-01icon-spell-gravity-levitate-01icon-spell-gravity-gravity-well-01icon-spell-illusion-mirror-self-01icon-spell-light-wisp-light-01icon-spell-light-prism-ray-01icon-spell-shadow-snuff-01icon-spell-nature-grasping-vines-01icon-spell-necromancy-animate-bones-01icon-spell-alteration-polymorph-01icon-spell-time-stasis-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-spell-icons-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Includes the four P2 spells (fire-wall, arcane-sight, snuff, stasis) because icons are cheap and needed for the grimoire silhouettes; drop those four ids if their spells are cut. One of the three style-anchor sets for
e37-asset-style-lock(hence m2). Generation needs the approved school frames as reference inputs.Acceptance criteria
- AC-1 content Given the 23 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 256²/128²/64² transparent PNG, medallion centred, school frame matches the approved anchor) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every symbol is distinguishable from its school siblings at 48 px and the frame matches the approved school frame.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.26 Owner approval: school frames and glyphs open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:iconclass:sorcererepic:e37milestone:m2mw-e37.29Generate spell icons via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 23 prompt files ofe37-asset-spell-icons-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-spell-icons-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 256²/128²/64² transparent PNG, medallion centred, school frame matches the approved anchor. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 23 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 256²/128²/64² transparent PNG, medallion centred, school frame matches the approved anchor), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.28 Write spell icons prompts (23 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:iconclass:sorcererepic:e37milestone:m2mw-e37.30Owner approval: spell icons
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 23 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-spell-icons-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every symbol is distinguishable from its school siblings at 48 px and the frame matches the approved school frame. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 23 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every symbol is distinguishable from its school siblings at 48 px and the frame matches the approved school frame.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-spell-icons-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.29 Generate spell icons via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconclass:sorcererepic:e37milestone:m2needs-humanmw-e37.31Integrate approved spell icons
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e06-spell-hotbar-ui,e07-grimoire-ui,e06-spell-schema.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 23 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell hotbar with all 23 spells assigned via debug console, when rendered at 1920×1080, then every slot shows its final icon (no placeholder flag) and no console errors occur.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e06.1 Define zod spell schema with delivery, effect ops and cue ids done
- mw-e06.16 Select spells from a hotbar and quick-select wheel with mana HUD done
- mw-e07.10 Browse known spells, insights and verb chains in the grimoire open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.30 Owner approval: spell icons open
area:artasset:iconclass:sorcererepic:e37milestone:m2mw-e37.32Write menu kit and journal UI prompts (21 assets)
Description
Why
Every menu (options, save/load, journal) shares the "illuminated field journal" look (style bible §9); one kit keeps them coherent and is the UI style anchor.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Generic parchment 9-slice panel, button states, focus ring, slider, checkbox, scrollbar, tooltip and divider, plus the journal parchment frame, tab icons (quest, rumour, note), quest state icons (active, resolved, failed), new-entry toast, compass nudge glyph and objective marker.
Asset ids (21):
ui-panel-parchment-9slice-01ui-button-normal-01ui-button-hover-01ui-button-pressed-01ui-button-disabled-01ui-focus-ring-01ui-slider-01ui-checkbox-01ui-scrollbar-01ui-tooltip-9slice-01ui-divider-flourish-01ui-panel-journal-9slice-01ui-glyph-tab-quest-01ui-glyph-tab-rumour-01ui-glyph-tab-note-01ui-glyph-quest-active-01ui-glyph-quest-resolved-01ui-glyph-quest-failed-01ui-toast-journal-01ui-glyph-compass-nudge-01ui-marker-objective-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-menus-journal-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
The generic menu kit (panel/button/focus/slider/checkbox/scrollbar/tooltip/divider) is not in
plan/gameplay-asset-needs.mdbut every menu bead needs it; added here. Wayfinding glyphs also servee23-wayfinding-hints(P2). m2 because it is one of the three style-anchor sets. Focus ring must satisfye31-menu-navigationvisible-focus contrast.Acceptance criteria
- AC-1 content Given the 21 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate, transparent, 9-slice insets recorded per frame, glyphs flat single-colour 64²) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: panels tile cleanly under 9-slice stretch from 200 px to 1600 px wide and body text on panels meets ≥ 7:1 contrast (§9).
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiepic:e37milestone:m2mw-e37.33Generate menu kit and journal UI via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 21 prompt files ofe37-asset-ui-menus-journal-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-menus-journal-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate, transparent, 9-slice insets recorded per frame, glyphs flat single-colour 64². Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 21 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate, transparent, 9-slice insets recorded per frame, glyphs flat single-colour 64²), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.32 Write menu kit and journal UI prompts (21 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiepic:e37milestone:m2mw-e37.34Owner approval: menu kit and journal UI
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 21 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-menus-journal-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: panels tile cleanly under 9-slice stretch from 200 px to 1600 px wide and body text on panels meets ≥ 7:1 contrast (§9). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 21 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then panels tile cleanly under 9-slice stretch from 200 px to 1600 px wide and body text on panels meets ≥ 7:1 contrast (§9).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-menus-journal-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.33 Generate menu kit and journal UI via Codex CLI open
area:artasset:uiepic:e37milestone:m2needs-humanmw-e37.35Integrate approved menu kit and journal UI
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e23-journal-ui,e30-save-load-ui,e31-settings-framework.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 21 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the options, save/load and journal screens, when opened at 75 % and 200 % UI scale, then all frames render from the final kit with no 9-slice seams visible in Playwright screenshots and no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e23.6 Build in-character journal for quests, rumors and notes done
- mw-e30.11 Build Continue / Load / Save menu screens done
- mw-e31.1 Create typed settings store and options menu skeleton done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.34 Owner approval: menu kit and journal UI open
area:artasset:uiepic:e37milestone:m2mw-e37.36Write player weapons concept prompts (12 images)
Description
Why
Each class has one signature prop always visible in silhouette (shield, bow, staff, hood) — the weapons carry the class fantasy and must match the approved character sheets.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.One flat front-facing concept image per held weapon or arrow variant (sword, wood and metal shields, bow, daggers, staff/focus, standard + fire/water/rope/noise/blunt arrows), then Claude-run image-to-3D (or CC0 kit match), retopo to 300–1.5 k tris, sharing the character or prop atlas.
Concept image ids (12):
prop-weapon-sword-01prop-weapon-shield-wood-01prop-weapon-shield-metal-01prop-weapon-bow-01prop-weapon-dagger-01prop-weapon-staff-01prop-weapon-arrow-standard-01prop-weapon-arrow-fire-01prop-weapon-arrow-water-01prop-weapon-arrow-rope-01prop-weapon-arrow-noise-01prop-weapon-arrow-blunt-01
Target model ids (12):
model-weapon-sword-01model-weapon-shield-wood-01model-weapon-shield-metal-01model-weapon-bow-01model-weapon-dagger-01model-weapon-staff-01model-weapon-arrow-standard-01model-weapon-arrow-fire-01model-weapon-arrow-water-01model-weapon-arrow-rope-01model-weapon-arrow-noise-01model-weapon-arrow-blunt-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-weapons-3d-generate)
Notes
m2 alongside the class models (a knight without a sword breaks the class playtests). Arrow variants are one shaft with swappable heads/payloads where possible. Concept images are one object per image (image-to-3D friendly), not a multi-object sheet. Post-MVP trick arrows (
e05-trick-arrows-extended) are out.Budget: 300–1.5 k tris per held weapon, arrows ≤ 300, no LODs, character or prop atlas, shared material (style bible §13.2).
Acceptance criteria
- AC-1 content Given the 12 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (300–1.5 k tris per held weapon, arrows ≤ 300, no LODs, character or prop atlas, shared material (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.20 Owner approval: player class characters flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.37Generate player weapons concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 12 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-weapons-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 12 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.36 Write player weapons concept prompts (12 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.38Owner approval: player weapons flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 12 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-weapons-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each weapon matches the approved class sheet palette and remains readable at 30 m game distance. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each weapon matches the approved class sheet palette and remains readable at 30 m game distance.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-weapons-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.37 Generate player weapons concepts via Codex CLI open
area:artasset:modelclass:allepic:e37milestone:m2needs-humanmw-e37.39Generate player weapons 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 12 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-weapons-turntable-approve)
Notes
Budget: 300–1.5 k tris per held weapon, arrows ≤ 300, no LODs, character or prop atlas, shared material (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 12 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets 300–1.5 k tris per held weapon, arrows ≤ 300, no LODs, character or prop atlas, shared material (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.38 Owner approval: player weapons flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.40Owner approval: player weapons turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 12 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-weapons-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each weapon matches the approved class sheet palette and remains readable at 30 m game distance. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each weapon matches the approved class sheet palette and remains readable at 30 m game distance.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-weapons-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.39 Generate player weapons 3D models via image-to-3D API open
area:artasset:modelclass:allepic:e37milestone:m2needs-humanmw-e37.41Integrate approved player weapons
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e04-melee-core,e05-bow-core,e05-arrow-schema,e10-takedowns,e06-casting-input-aiming.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 12 models are underpublic/assets/model/within 300–1.5 k tris per held weapon, arrows ≤ 300, no LODs, character or prop atlas, shared material (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the combat sandbox and archery range, when each class equips its kit, then the final weapon models attach to the correct sockets, arrows stick and are retrievable, and no console errors occur.
- AC-4 perf Given the combat stress scene with these models, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e05.1 Data schema for arrow types and property-based payloads done
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e06.15 Aim and cast spells with reticle, ground targeting and cancel done
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.40 Owner approval: player weapons turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelclass:allepic:e37milestone:m2mw-e37.86Write combat impact VFX prompts (10 assets)
Description
Why
Souls-like weight lives in the moment of impact; material-specific hits tell the player what they struck and whether the parry landed.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Impact flipbooks by material (metal sparks, wood splinters, bone dust, ichor puff, stone chips), block spark, parry flash, guard-break shatter, critical burst and arrow deflect sparks.
Asset ids (10):
vfx-hit-spark-metal-01vfx-hit-splinter-wood-01vfx-hit-bone-dust-01vfx-hit-ichor-puff-01vfx-hit-stone-chips-01vfx-block-spark-01vfx-parry-flash-01vfx-guard-break-shatter-01vfx-critical-burst-01vfx-deflect-sparks-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-combat-impacts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
m2 because combat feel is validated in
e04-skeleton-fun-validation/e35-class-fantasy-tests; the procedural placeholders still keep gameplay unblocked. No gore: ichor is a stylised puff. Parry flash obeys the ≤ 25 % screen-area flash rule.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 10 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet, 4×4 exact grid, black background for additive sparks/flashes, alpha for dust/chips, 18–24 fps, non-looping) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each material hit is distinguishable without sound and parry flash is clearly different from a normal block.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxclass:knightepic:e37milestone:m2mw-e37.87Generate combat impact VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 10 prompt files ofe37-asset-vfx-combat-impacts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-combat-impacts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet, 4×4 exact grid, black background for additive sparks/flashes, alpha for dust/chips, 18–24 fps, non-looping. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet, 4×4 exact grid, black background for additive sparks/flashes, alpha for dust/chips, 18–24 fps, non-looping), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.86 Write combat impact VFX prompts (10 assets) open
area:artasset:vfxclass:knightepic:e37milestone:m2mw-e37.88Owner approval: combat impact VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 10 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-combat-impacts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each material hit is distinguishable without sound and parry flash is clearly different from a normal block. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each material hit is distinguishable without sound and parry flash is clearly different from a normal block.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-combat-impacts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.87 Generate combat impact VFX via Codex CLI open
area:artasset:vfxclass:knightepic:e37milestone:m2needs-humanmw-e37.89Integrate approved combat impact VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-hit-sparks,e04-parry-riposte,e05-weak-points.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 10 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the combat sandbox, when the knight strikes metal, wood, bone, ichor and stone dummies and parries, then each final effect plays within the particle budget with no console errors.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e04.12 Parry window and riposte critical attack done
- mw-e05.6 Headshots, weak points and armored regions that deflect done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.4 Impact VFX by material: sparks, splinters, bone dust, parry flash done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.88 Owner approval: combat impact VFX open
area:artasset:vfxclass:knightepic:e37milestone:m2mw-e37.142Lock approved style anchor images for all later image prompts
Description
Why
Generative tools drift; locking a few approved images as series anchors keeps 300+ later assets looking like one game (style bible §12).
What
After the first anchor sets (player class sheets, school frames + spell icons, core menu kit) are approved, Claude proposes 1–3 anchor images per category (character, prop, icon, UI, VFX, key art) with rationale; the owner confirms. Anchors are recorded in
assets/prompts/_anchors.json(category → asset ids + SHA-256) and every later prompt file must list its category anchors as reference inputs.Scope
- In: Anchor proposal + owner confirmation
- In: Anchor registry file
- In: Validator rule: non-anchor prompt files must cite their category anchors
- Out: Changing the style bible itself (bible bead + version bump)
Notes
Anchor sets:
e37-asset-player-classes-*,e37-asset-school-identity-*,e37-asset-spell-icons-*,e37-asset-ui-menus-journal-*. VFX anchors may be added after the first VFX set is approved (registry is append-only with owner sign-off).Acceptance criteria
- AC-1 manual Given the approved anchor sets, when the owner reviews the proposal, then anchors for character, prop, icon and UI categories are confirmed and recorded in
assets/approvals.jsonwith gate "style-lock". - AC-2 content Given
assets/prompts/_anchors.json, when validated, then every anchor id resolves to an approved asset whose SHA-256 matches its approval record. - AC-3 integration Given a non-anchor image prompt file that omits its category anchors, when the asset validator runs, then it fails naming the file.
- AC-4 integration Edge: given an anchor asset that is later replaced (hash change), when CI runs, then the validator flags every prompt file citing the old hash for re-review instead of passing silently.
Waits on
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.20 Owner approval: player class characters flat concepts open
- mw-e37.30 Owner approval: spell icons open
- mw-e37.34 Owner approval: menu kit and journal UI open
area:artasset:conceptepic:e37milestone:m2needs-humanmw-e37.12Texture atlas and palette-remap build step for prop sets
Description
Why
Draw-call budgets (≤ 150 on Low) require one material per prop set; kits from different families only look like one world after palette remapping.
What
As a developer, I want prop sets declared in a data file to be packed into one atlas with UVs rewritten, and kit gradient textures remapped to
tex-palette-master-01, so that each set uses one material. Supports palette-atlas (KayKit-style) and painted-texture atlases, padding/bleed for mips, High/Low sizes.Scope
- In: Set definition data (
assets/sets/*.json) - In: Atlas packer with UV rewrite in glTF
- In: Palette remap table (kit colour → palette token)
- In: Integration with
assets:build - Out: Tileable textures (not atlased)
- Out: Character atlases (authored per character)
Notes
Mip bleed: ≥ 8 px padding at 2048² so Low (1024²) mips do not bleed.
Acceptance criteria
- AC-1 integration Given a set of 12 props with 12 materials, when atlased, then output meshes reference exactly 1 material and render pixel-identical within ΔE ≤ 3 against the unatlased screenshot at LOD0.
- AC-2 unit Given a palette remap table, when applied to a kit gradient texture, then every mapped source colour is replaced by its palette token hex and unmapped colours are reported.
- AC-3 integration Given the atlas at Low size, when mip level 2 is inspected, then no island samples a neighbour island (bleed check passes).
- AC-4 integration Edge: given a set whose islands cannot fit in the maximum atlas size, when packed, then the build fails with the set id and the required size instead of shrinking texel density silently.
Waits on
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artarea:toolsasset:textureepic:e37milestone:m2- In: Set definition data (
M3 MVP content 385
mw-e37.16Style consistency review gate before MVP content lock
Description
Why
Generated assets drift: each prompt is "on style" alone but the set diverges. A scheduled gate catches drift before it ships.
What
A structured review of all integrated art per location against the style bible and the style-target diorama: contact sheets of icons, portraits, props and textures; in-engine captures per location; silhouette lineup of classes and creatures; Legibility cue audit. Output: pass, or fix beads per offending asset.
Scope
- In: Contact-sheet generator script output (icons/portraits/textures side by side)
- In: Per-location capture set
- In: Review checklist + findings file in
docs/art/reviews/ - Out: Fixing assets (filed as beads)
- Out: Audio review (see
e38-mix-review)
Notes
Run once at m2 exit (informal) and once before m3 content lock (formal gate).
Acceptance criteria
- AC-1 integration Given all icon, portrait and texture assets, when the contact-sheet script runs, then it produces one sheet per category ordered by asset id with each tile labelled.
- AC-2 manual Given the class and creature silhouette lineup rendered in solid ink at 64 px, when reviewed, then every class and creature family is identified correctly by a reviewer unfamiliar with the models.
- AC-3 manual Given the review checklist, when completed, then every failing asset has a filed fix bead referencing its asset id, and the review file records pass/fail per location.
- AC-4 manual Edge: given an asset that fails review but is on the MVP critical path, when triaged, then it is either fixed before m3 or explicitly waived by the owner with a reason in the review file.
Waits on
- mw-e37.15 Build the style-target diorama as the visual and performance benchmark open
area:artepic:e37milestone:m3needs-humanmw-e37.42Write spell-summoned models concept prompts (3 images)
Description
Why
Polymorph, Grasping Vines and Wisp Light create physical things in the world; they need bespoke models that read as magical but belong in the world.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature / prop) fromcreature-reference-sheet.md (critter) + prop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.The polymorph critter (a harmless farm fowl — matches
sfx-...-critter-cluck), a restraining vine entity (erupt/hold/wither poses) and the wisp orb shell, generated as flat concepts then Claude-run image-to-3D; the critter is rigged with a simple walk/flee/idle set.Concept image ids (3):
creature-polymorph-critter-01prop-vine-entity-01prop-wisp-orb-01
Target model ids (3):
model-creature-polymorph-critter-01model-prop-vine-entity-01model-prop-wisp-orb-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-magic-summon-models-3d-generate)
Notes
The Forgotten (animated remains) raised by Animate Bones reuse the world group's Forgotten creature model (see
plan/world-asset-needs.md). Vine and wisp are mostly shader/VFX driven; models are low-poly shells (≤ 2 k tris).Budget: critter 2–4 k tris, T1 512², ≤ 40 bones; vine ≤ 2 k tris with 3 morph/pose targets; wisp ≤ 300 tris emissive.
Acceptance criteria
- AC-1 content Given the 3 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (critter 2–4 k tris, T1 512², ≤ 40 bones; vine ≤ 2 k tris with 3 morph/pose targets; wisp ≤ 300 tris emissive) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelclass:sorcererepic:e37milestone:m3mw-e37.43Generate spell-summoned models concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 3 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-magic-summon-models-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 3 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.42 Write spell-summoned models concept prompts (3 images) open
area:artasset:modelclass:sorcererepic:e37milestone:m3mw-e37.44Owner approval: spell-summoned models flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 3 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-magic-summon-models-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the critter reads as harmless and comic, never as a threat, and all three sit in the school palettes (§2.3 alteration/nature/light). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the critter reads as harmless and comic, never as a threat, and all three sit in the school palettes (§2.3 alteration/nature/light).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-magic-summon-models-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.43 Generate spell-summoned models concepts via Codex CLI open
area:artasset:modelclass:sorcererepic:e37milestone:m3needs-humanmw-e37.45Generate spell-summoned models 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 3 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-magic-summon-models-turntable-approve)
Notes
Budget: critter 2–4 k tris, T1 512², ≤ 40 bones; vine ≤ 2 k tris with 3 morph/pose targets; wisp ≤ 300 tris emissive. API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 3 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets critter 2–4 k tris, T1 512², ≤ 40 bones; vine ≤ 2 k tris with 3 morph/pose targets; wisp ≤ 300 tris emissive, or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.44 Owner approval: spell-summoned models flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelclass:sorcererepic:e37milestone:m3mw-e37.46Owner approval: spell-summoned models turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 3 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-magic-summon-models-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the critter reads as harmless and comic, never as a threat, and all three sit in the school palettes (§2.3 alteration/nature/light). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the critter reads as harmless and comic, never as a threat, and all three sit in the school palettes (§2.3 alteration/nature/light).
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-magic-summon-models-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.45 Generate spell-summoned models 3D models via image-to-3D API open
area:artasset:modelclass:sorcererepic:e37milestone:m3needs-humanmw-e37.47Integrate approved spell-summoned models
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e08-spell-polymorph,e08-spell-grasping-vines,e08-spell-wisp-light,e06-summon-framework.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 3 models are underpublic/assets/model/within critter 2–4 k tris, T1 512², ≤ 40 bones; vine ≤ 2 k tris with 3 morph/pose targets; wisp ≤ 300 tris emissive and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the spell sandbox, when Polymorph, Grasping Vines and Wisp Light are cast, then the final models spawn, animate and despawn with no console errors.
- AC-4 perf Given the combat stress scene with these models, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e06.10 Summon temporary creatures and objects with allegiance and caps open
- mw-e08.17 Add Wisp Light: a floating orb that lights darkness and repels open
- mw-e08.20 Add Grasping Vines: roots burst up and restrain creatures open
- mw-e08.22 Add Polymorph: temporarily turn a creature into a harmless animal open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.46 Owner approval: spell-summoned models turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelclass:sorcererepic:e37milestone:m3mw-e37.48Write thief props concept prompts (4 images)
Description
Why
Thief play depends on reading which objects hide you, which traps are armed and what can be thrown (Legibility Language, style bible §7).
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Three prop sheets (hiding props: wardrobe, barrel, tall grass, curtain alcove; traps: bear trap, caltrops, tripwire, noisemaker; throwables: bottle, coin, smoke bomb, rope coil) plus a guard-uniform recolour texture for disguises. Models come from CC0 kits recoloured to the palette atlas first; only unmatched props go through image-to-3D.
Concept image ids (4):
prop-hiding-set-01prop-thief-traps-set-01prop-throwables-set-01tex-disguise-guard-uniform-01
Target model ids (12):
model-prop-wardrobe-hide-01model-prop-barrel-hide-01model-prop-tall-grass-01model-prop-curtain-alcove-01model-prop-trap-bear-01model-prop-caltrops-01model-prop-trap-tripwire-01model-prop-trap-noisemaker-01model-prop-bottle-throwable-01model-prop-coin-01model-prop-smoke-bomb-01model-prop-rope-coil-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-thief-props-3d-generate)
Notes
Wardrobe and barrel may overlap with the world group's town props — whichever set lands first owns the model; the other references it. Disguise recolour serves
e10-disguises(P2) and trap-placement preview servese10-trap-placement(P2).Budget: small props 50–400 tris, medium 400–2 k, prop atlas, shared material (style bible §13.2).
Acceptance criteria
- AC-1 content Given the 4 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (small props 50–400 tris, medium 400–2 k, prop atlas, shared material (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.49Generate thief props concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 4 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-thief-props-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 4 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.48 Write thief props concept prompts (4 images) open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.50Owner approval: thief props flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 4 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-thief-props-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: hiding props visibly signal "enterable" per the Legibility Language and armed traps are distinguishable from disarmed ones at 10 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then hiding props visibly signal "enterable" per the Legibility Language and armed traps are distinguishable from disarmed ones at 10 m.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-thief-props-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.49 Generate thief props concepts via Codex CLI open
area:artasset:modelclass:thiefepic:e37milestone:m3needs-humanmw-e37.51Generate thief props 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 12 target models: first try a CC0 kit match recoloured to the palette atlas; otherwise call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-thief-props-turntable-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, prop atlas, shared material (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 12 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, prop atlas, shared material (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.50 Owner approval: thief props flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.52Owner approval: thief props turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 12 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-thief-props-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: hiding props visibly signal "enterable" per the Legibility Language and armed traps are distinguishable from disarmed ones at 10 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then hiding props visibly signal "enterable" per the Legibility Language and armed traps are distinguishable from disarmed ones at 10 m.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-thief-props-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.51 Generate thief props 3D models via image-to-3D API open
area:artasset:modelclass:thiefepic:e37milestone:m3needs-humanmw-e37.53Integrate approved thief props
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e09-hiding-places,e03-hiding-containers,e10-trap-disarm,e10-thrown-distractions.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 12 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, prop atlas, shared material (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the stealth grey-box course, when the thief hides, throws and disarms, then every final prop loads with its legibility cue, draw calls stay within budget, and no console errors occur.
- AC-4 perf Given the combat stress scene with these models, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e03.23 Hide in wardrobes, barrels and haystacks and search containers done
- mw-e09.8 Hiding places: wardrobes, barrels, tall grass and shadowed alcoves open
- mw-e10.12 Detect and disarm world traps; failures trigger them open
- mw-e10.2 Throw objects to create noise distractions guards investigate open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.52 Owner approval: thief props turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.54Write stealth animations motion briefs (10 clips)
Description
Why
Stealth reads through bodies: a guard scanning upward or yawning tells the thief when to move; takedowns and carries must look deliberate (constitution: stealth is systemic and readable).
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.md: asset table, brief, sourcing (CC0 library clip or provider animation API prompt), motion spec (frames, loop, root motion, event markers), readability checklist and iteration log.Motion briefs, then Claude sources each clip from a CC0 animation library or generates it via the image-to-3D provider's animation API, retargets it to the canonical skeleton with
e37-animation-pipeline, and adds event markers (footstep, hit, grab).Clip ids (10):
anim-humanoid-climb-01anim-humanoid-carry-body-01anim-humanoid-takedown-choke-01anim-humanoid-backstab-01anim-humanoid-lean-peek-left-01anim-humanoid-lean-peek-right-01anim-humanoid-pick-lock-01anim-humanoid-guard-idle-stretch-01anim-humanoid-guard-idle-yawn-01anim-humanoid-guard-look-up-scan-01
Scope
- In: One brief per clip id
- In: Event marker list per clip (footstep, hit, grab, release)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Core locomotion/combat clips
Notes
No animation prompt template exists in
assets/prompts/_templates/; briefs use the common sections (asset, brief, output spec, consistency checklist, iteration log) with a motion spec (frames, loop, root motion, markers) instead of an image prompt. Lean/peek servese10-peek-lean(P2). Core locomotion/combat clips come from the animation pipeline's base library, not this set.Acceptance criteria
- AC-1 content Given the 10 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-humanoid-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states frame count, loop flag, root-motion policy and the required event markers.
- AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo).
- AC-4 content Edge: given a brief with no event markers for a clip that emits noise or contact, when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.55Source and retarget stealth animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief, retarget to the canonical skeleton with
e37-animation-pipeline, add markers, and render preview GIFs on the class placeholder model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-stealth-actions-approve)
Notes
glb clip on the canonical skeleton, 30 fps, in-place with root velocity curve, loop flag and event markers per the clip manifest
Acceptance criteria
- AC-1 integration Given the 10 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags and event markers match each brief.
- AC-3 integration Given each clip, when previewed, then a GIF exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.54 Write stealth animations motion briefs (10 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.56Owner approval: stealth animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 10 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-stealth-actions-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each clip reads clearly from the default third-person camera distance and shows no foot sliding or limb interpenetration in the preview. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each clip reads clearly from the default third-person camera distance and shows no foot sliding or limb interpenetration in the preview.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-stealth-actions-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.55 Source and retarget stealth animations open
area:artasset:modelclass:thiefepic:e37milestone:m3needs-humanmw-e37.57Integrate approved stealth animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads)
Notes
Consumers:
e10-climbing,e10-body-carry-hide,e10-takedowns,e10-lockpicking,e11-patrol-routines.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 10 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence. - AC-3 e2e Given the thief grey-box challenge, when climbing, carrying, taking down, lockpicking and peeking, then the final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
- mw-e10.5 Carry and hide unconscious or dead bodies open
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
- mw-e37.56 Owner approval: stealth animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelclass:thiefepic:e37milestone:m3mw-e37.58Write spellbook covers prompts (24 assets)
Description
Why
Books are the sorcerer's progression; a cover glimpsed on a bookshop shelf should hint at its school and make the player want it.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: book) from templatebook-cover-and-page.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.One cover per MVP spellbook (23) with a school-coloured binding and embossed motif (no text), plus one generic cover palette-remapped per school for placeholder/lore books.
Asset ids (24):
book-spell-ember-cover-01book-spell-firebolt-cover-01book-spell-flame-jet-cover-01book-spell-fire-wall-cover-01book-spell-frostglass-cover-01book-spell-wellspring-cover-01book-spell-gust-cover-01book-spell-thunderclap-cover-01book-spell-arc-lash-cover-01book-spell-mage-hand-cover-01book-spell-telekinetic-throw-cover-01book-spell-blink-cover-01book-spell-arcane-sight-cover-01book-spell-levitate-cover-01book-spell-gravity-well-cover-01book-spell-mirror-self-cover-01book-spell-wisp-light-cover-01book-spell-prism-ray-cover-01book-spell-snuff-cover-01book-spell-grasping-vines-cover-01book-spell-animate-bones-cover-01book-spell-polymorph-cover-01book-spell-stasis-cover-01book-generic-cover-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-book-covers-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Input list used
ui-book-cover-<slug>; ids follow the style biblebook-<subject>-cover-01pattern instead. School-colour placeholder covers are one generic cover recoloured bye37-texture-atlas-buildpalette remap rather than 13 generations. Titles are rendered by the game (IM Fell), never painted.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 24 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (cover 2:3 1024×1536, transparent around the book object, blank title plate, text-safe rectangle recorded) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each cover's school is identifiable from binding colour + motif without reading, and no letters appear anywhere.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:conceptclass:sorcererepic:e37milestone:m3mw-e37.59Generate spellbook covers via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 24 prompt files ofe37-asset-book-covers-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-book-covers-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: cover 2:3 1024×1536, transparent around the book object, blank title plate, text-safe rectangle recorded. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 24 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (cover 2:3 1024×1536, transparent around the book object, blank title plate, text-safe rectangle recorded), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.58 Write spellbook covers prompts (24 assets) open
area:artasset:conceptclass:sorcererepic:e37milestone:m3mw-e37.60Owner approval: spellbook covers
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 24 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-book-covers-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each cover's school is identifiable from binding colour + motif without reading, and no letters appear anywhere. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 24 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each cover's school is identifiable from binding colour + motif without reading, and no letters appear anywhere.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-book-covers-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.59 Generate spellbook covers via Codex CLI open
area:artasset:conceptclass:sorcererepic:e37milestone:m3needs-humanmw-e37.61Integrate approved spellbook covers
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e07-book-reader-ui,e21-bookshop-ui,e07-book-item-schema.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 24 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the fixture bookshop and book reader, when every spellbook is shown, then its final cover renders with the title overlaid in the text-safe area and no placeholder flags remain.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e07.1 Define book item schema: spell, language, condition, legality done
- mw-e07.9 Read books in a page-turning reader with a study action open
- mw-e21.8 Browse bookshop shelves with covers, previews and known markers open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.60 Owner approval: spellbook covers open
area:artasset:conceptclass:sorcererepic:e37milestone:m3mw-e37.62Write magic and grimoire UI prompts (17 assets)
Description
Why
Casting, reading and shopping for books are the sorcerer's core loop; the UI must feel like a well-loved grimoire while staying legible mid-fight.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Hotbar frame, quick-select wheel, mana brushstroke bar, cooldown sweep mask, spell aiming reticle, ground-target marker, revealed-object outline ramp; grimoire spread with school ribbon tab and locked-verb silhouette; two-page reader spread with parchment, torn-page and garbled-text styles; bookshop shelf; forbidden and "already known" markers.
Asset ids (17):
ui-hotbar-frame-01ui-spell-wheel-01ui-bar-mana-01ui-cooldown-sweep-01ui-reticle-spell-aim-01ui-marker-ground-target-01ui-outline-reveal-ramp-01ui-grimoire-spread-01ui-grimoire-ribbon-tab-01ui-grimoire-verb-locked-01ui-book-reader-spread-01ui-book-page-parchment-01ui-book-page-torn-01ui-book-page-garbled-01ui-bookshop-shelf-01ui-marker-book-forbidden-01ui-marker-book-known-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-magic-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Ribbon tab is tinted per school at runtime (school core colours, §9). Torn/garbled pages also serve
e07-damaged-books/e07-untranslated-books(P2); outline ramp also servese08-spell-arcane-sight(P2).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 17 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate, transparent, 9-slice insets for frames/spreads, page spreads 3:2 1536×1024 opaque) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the reticle and ground marker stay readable over both bright and dark scenes and the grimoire matches the approved menu kit.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiclass:sorcererepic:e37milestone:m3mw-e37.63Generate magic and grimoire UI via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 17 prompt files ofe37-asset-ui-magic-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-magic-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate, transparent, 9-slice insets for frames/spreads, page spreads 3:2 1536×1024 opaque. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 17 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate, transparent, 9-slice insets for frames/spreads, page spreads 3:2 1536×1024 opaque), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.62 Write magic and grimoire UI prompts (17 assets) open
area:artasset:uiclass:sorcererepic:e37milestone:m3mw-e37.64Owner approval: magic and grimoire UI
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 17 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-magic-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the reticle and ground marker stay readable over both bright and dark scenes and the grimoire matches the approved menu kit. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 17 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the reticle and ground marker stay readable over both bright and dark scenes and the grimoire matches the approved menu kit.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-magic-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.63 Generate magic and grimoire UI via Codex CLI open
area:artasset:uiclass:sorcererepic:e37milestone:m3needs-humanmw-e37.65Integrate approved magic and grimoire UI
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e06-spell-hotbar-ui,e06-casting-input-aiming,e07-grimoire-ui,e07-book-reader-ui,e21-bookshop-ui.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 17 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when casting, opening the grimoire, reading a book and browsing the fixture bookshop, then every element renders from final art with no placeholder flags and no console errors.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e06.15 Aim and cast spells with reticle, ground targeting and cancel done
- mw-e06.16 Select spells from a hotbar and quick-select wheel with mana HUD done
- mw-e07.10 Browse known spells, insights and verb chains in the grimoire open
- mw-e07.9 Read books in a page-turning reader with a study action open
- mw-e21.8 Browse bookshop shelves with covers, previews and known markers open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.64 Owner approval: magic and grimoire UI open
area:artasset:uiclass:sorcererepic:e37milestone:m3mw-e37.66Write school default and miscast VFX prompts (26 assets)
Description
Why
Any spell without bespoke art (NPC casts, data-only spells) must still read as its school, and botched casts must look distinct from successful ones (failure creates stories).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Per school: one default cast/impact flipbook in the school's colours and motion signature (§2.3), and one miscast flipbook (sputter, backfire) that keeps the school identity but signals failure.
Asset ids (26):
vfx-spell-fire-default-01vfx-spell-frost-default-01vfx-spell-storm-default-01vfx-spell-arcane-default-01vfx-spell-illusion-default-01vfx-spell-alteration-default-01vfx-spell-conjuration-default-01vfx-spell-necromancy-default-01vfx-spell-nature-default-01vfx-spell-light-default-01vfx-spell-shadow-default-01vfx-spell-gravity-default-01vfx-spell-time-default-01vfx-spell-fire-miscast-01vfx-spell-frost-miscast-01vfx-spell-storm-miscast-01vfx-spell-arcane-miscast-01vfx-spell-illusion-miscast-01vfx-spell-alteration-miscast-01vfx-spell-conjuration-miscast-01vfx-spell-necromancy-miscast-01vfx-spell-nature-miscast-01vfx-spell-light-miscast-01vfx-spell-shadow-miscast-01vfx-spell-gravity-miscast-01vfx-spell-time-miscast-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-school-defaults-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
These are final-quality fallbacks, distinct from the procedural placeholders of
e29-placeholder-vfx. Storm flashes obey the ≤ 3 Hz photosensitivity cap.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 26 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps, loop flag recorded) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each school default is identifiable in greyscale by motion/shape (not only hue) and every miscast reads as "went wrong" beside its default.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.67Generate school default and miscast VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 26 prompt files ofe37-asset-vfx-school-defaults-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-school-defaults-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps, loop flag recorded. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 26 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps, loop flag recorded), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.66 Write school default and miscast VFX prompts (26 assets) open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.68Owner approval: school default and miscast VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 26 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-school-defaults-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each school default is identifiable in greyscale by motion/shape (not only hue) and every miscast reads as "went wrong" beside its default. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 26 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each school default is identifiable in greyscale by motion/shape (not only hue) and every miscast reads as "went wrong" beside its default.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-school-defaults-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.67 Generate school default and miscast VFX via Codex CLI open
area:artasset:vfxclass:sorcererepic:e37milestone:m3needs-humanmw-e37.69Integrate approved school default and miscast VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-spell-vfx-hooks,e06-spell-cue-binding,e06-botched-casts.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 26 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when a data-only test spell of each school is cast and botched, then the default and miscast effects play from final textures within the particle budget and with no console errors.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e06.13 Resolve botched casts into data-driven miscast outcomes open
- mw-e06.17 Bind spell events to placeholder VFX and SFX cues open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.10 Spell VFX slots and templates for the spell pipeline done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.68 Owner approval: school default and miscast VFX open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.70Write elemental spell VFX prompts (18 assets)
Description
Why
Elemental spells are how the sorcerer changes the systemic world (burn, freeze, soak, shock); their visuals must communicate the resulting world state instantly.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Phase flipbooks for ember, firebolt, flame-jet, frostglass, wellspring, gust, thunderclap, arc-lash (cast/travel/impact/channel/cone/wave/beam/chain/ice-sheet/puddle as listed).
Asset ids (18):
vfx-spell-fire-ember-cast-01vfx-spell-fire-ember-travel-01vfx-spell-fire-ember-impact-01vfx-spell-fire-firebolt-cast-01vfx-spell-fire-firebolt-travel-01vfx-spell-fire-firebolt-impact-01vfx-spell-fire-flame-jet-channel-01vfx-spell-frost-frostglass-cast-01vfx-spell-frost-frostglass-travel-01vfx-spell-frost-frostglass-impact-01vfx-spell-frost-frostglass-ice-sheet-01vfx-spell-conjuration-wellspring-cast-01vfx-spell-conjuration-wellspring-burst-01vfx-spell-conjuration-wellspring-puddle-01vfx-spell-storm-gust-cone-01vfx-spell-storm-thunderclap-wave-01vfx-spell-storm-arc-lash-beam-01vfx-spell-storm-arc-lash-chain-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-spells-elemental-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Wellspring is conjuration school (water palette
#3F7FA0with conjuration portal accents). Ice-sheet and puddle textures must match the elemental surface colours used bye29-elemental-fx.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 18 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores (alpha for smoke/steam), 12–24 fps) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each spell's phases read as one effect and match its school motion signature (§2.3).
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.71Generate elemental spell VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 18 prompt files ofe37-asset-vfx-spells-elemental-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-spells-elemental-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores (alpha for smoke/steam), 12–24 fps. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 18 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores (alpha for smoke/steam), 12–24 fps), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.70 Write elemental spell VFX prompts (18 assets) open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.72Owner approval: elemental spell VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 18 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-spells-elemental-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each spell's phases read as one effect and match its school motion signature (§2.3). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 18 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each spell's phases read as one effect and match its school motion signature (§2.3).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-spells-elemental-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.71 Generate elemental spell VFX via Codex CLI open
area:artasset:vfxclass:sorcererepic:e37milestone:m3needs-humanmw-e37.73Integrate approved elemental spell VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-spell-vfx-hooks,e08-spell-ember,e08-spell-firebolt-flame-jet,e08-spell-frostglass,e08-spell-wellspring,e08-spell-gust,e08-spell-thunderclap,e08-spell-arc-lash.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 18 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when each elemental spell is cast at a target, then every phase plays from final textures and live particles stay ≤ 800 on the Low preset.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e08.2 Add Ember: precise ignition of flammable things done
- mw-e08.3 Add Firebolt and its Flame Jet follow-on verb open
- mw-e08.5 Add Frostglass: freeze targets and create temporary ice surfaces open
- mw-e08.6 Add Wellspring: conjure a burst of water that soaks an area open
- mw-e08.7 Add Gust: blast creatures back, clear gas and propel objects open
- mw-e08.8 Add Thunderclap: a violent shockwave that breaks and deafens open
- mw-e08.9 Add Arc Lash: lightning that chains through conductive and wet open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.10 Spell VFX slots and templates for the spell pipeline done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.72 Owner approval: elemental spell VFX open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.74Write arcane and mystic spell VFX prompts (22 assets)
Description
Why
Kinetic, illusion, light and life spells are the "too useful" toolbox; their effects must make the systemic consequence (grabbed, pulled, doubled, lit, rooted, raised, transformed) obvious.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Phase flipbooks for mage-hand, telekinetic-throw, blink, levitate, gravity-well, mirror-self, wisp-light, prism-ray, grasping-vines, animate-bones, polymorph across schools arcane, gravity, illusion, light, nature, necromancy, alteration.
Asset ids (22):
vfx-spell-arcane-mage-hand-tether-01vfx-spell-arcane-mage-hand-grab-01vfx-spell-arcane-telekinetic-throw-launch-01vfx-spell-arcane-telekinetic-throw-catch-01vfx-spell-arcane-blink-depart-01vfx-spell-arcane-blink-arrive-01vfx-spell-gravity-levitate-loop-01vfx-spell-gravity-gravity-well-loop-01vfx-spell-gravity-gravity-well-collapse-01vfx-spell-illusion-mirror-self-spawn-01vfx-spell-illusion-mirror-self-shimmer-01vfx-spell-illusion-mirror-self-dispel-01vfx-spell-light-wisp-light-orb-01vfx-spell-light-prism-ray-beam-01vfx-spell-light-prism-ray-reflect-01vfx-spell-nature-grasping-vines-erupt-01vfx-spell-nature-grasping-vines-hold-01vfx-spell-nature-grasping-vines-wither-01vfx-spell-necromancy-animate-bones-assemble-01vfx-spell-necromancy-animate-bones-crumble-01vfx-spell-alteration-polymorph-poof-in-01vfx-spell-alteration-polymorph-poof-out-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-spells-arcane-mystic-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Prism Ray beam and reflect must work with
e03-light-beamsmirrors; Gravity Well uses the lensing rim; Polymorph poofs are comic, not horrific.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 22 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each spell's phases read as one effect and match its school motion signature (§2.3).
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.75Generate arcane and mystic spell VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 22 prompt files ofe37-asset-vfx-spells-arcane-mystic-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-spells-arcane-mystic-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 22 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.74 Write arcane and mystic spell VFX prompts (22 assets) open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.76Owner approval: arcane and mystic spell VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 22 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-spells-arcane-mystic-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each spell's phases read as one effect and match its school motion signature (§2.3). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 22 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each spell's phases read as one effect and match its school motion signature (§2.3).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-spells-arcane-mystic-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.75 Generate arcane and mystic spell VFX via Codex CLI open
area:artasset:vfxclass:sorcererepic:e37milestone:m3needs-humanmw-e37.77Integrate approved arcane and mystic spell VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-spell-vfx-hooks,e29-spectacle-shaders,e08-spell-mage-hand,e08-spell-telekinetic-throw,e08-spell-blink,e08-spell-levitate,e08-spell-gravity-well,e08-spell-mirror-self,e08-spell-wisp-light,e08-spell-prism-ray,e08-spell-grasping-vines,e08-spell-animate-bones,e08-spell-polymorph.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 22 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when each spell in the set is cast, then every phase plays from final textures and live particles stay ≤ 800 on the Low preset.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e08.10 Add Mage Hand: grab, move and use distant objects done
- mw-e08.11 Add Telekinetic Throw & Catch: hurl objects, creatures and arrows open
- mw-e08.12 Add Blink: teleport several metres through open space open
- mw-e08.13 Add Levitate: float the caster or an object upward slowly open
- mw-e08.14 Add Gravity Well: pull creatures and loose objects to a point open
- mw-e08.15 Add Mirror Self: an illusory double that draws attention open
- mw-e08.17 Add Wisp Light: a floating orb that lights darkness and repels open
- mw-e08.18 Add Prism Ray: a light beam that reflects off reflective surfaces open
- mw-e08.20 Add Grasping Vines: roots burst up and restrain creatures open
- mw-e08.21 Add Animate Bones: raise nearby remains as a temporary servant open
- mw-e08.22 Add Polymorph: temporarily turn a creature into a harmless animal open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.10 Spell VFX slots and templates for the spell pipeline done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.76 Owner approval: arcane and mystic spell VFX open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.82Write base VFX textures and decals prompts (16 assets)
Description
Why
Most effects are built from a handful of shared textures; painting them once in the house style makes every derived effect consistent and cheap.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md (+ tileable-texture.md for decals), with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Base particle textures (soft circle, spark streak, smoke noise, ring, shard, flame flipbook), the six spell VFX templates (cast wind-up, projectile head, projectile trail, impact, area ring, beam) and projected decals (scorch, frost, wet, arrow hole).
Asset ids (16):
vfx-base-soft-circle-01vfx-base-spark-streak-01vfx-base-smoke-noise-01vfx-base-ring-01vfx-base-shard-01vfx-base-flame-8x8-01vfx-template-cast-windup-01vfx-template-projectile-head-01vfx-template-projectile-trail-01vfx-template-impact-01vfx-template-area-ring-01vfx-template-beam-01vfx-decal-scorch-01vfx-decal-frost-01vfx-decal-wet-01vfx-decal-arrow-hole-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-base-textures-decals-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Ids must match those already emitted by
e29-placeholder-vfxso integration is a file swap. Decals also servee29-decals(P2).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 16 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² or 512² sheets, exact grid for flipbooks, black (additive) or transparent (alpha) background; decals transparent with soft edges) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: base textures look painted (no photographic noise) and decals sit in the world palette without hard rectangular edges.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxepic:e37milestone:m3mw-e37.83Generate base VFX textures and decals via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 16 prompt files ofe37-asset-vfx-base-textures-decals-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-base-textures-decals-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² or 512² sheets, exact grid for flipbooks, black (additive) or transparent (alpha) background; decals transparent with soft edges. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 16 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² or 512² sheets, exact grid for flipbooks, black (additive) or transparent (alpha) background; decals transparent with soft edges), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.82 Write base VFX textures and decals prompts (16 assets) open
area:artasset:vfxepic:e37milestone:m3mw-e37.84Owner approval: base VFX textures and decals
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 16 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-base-textures-decals-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: base textures look painted (no photographic noise) and decals sit in the world palette without hard rectangular edges. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 16 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then base textures look painted (no photographic noise) and decals sit in the world palette without hard rectangular edges.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-base-textures-decals-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.83 Generate base VFX textures and decals via Codex CLI open
area:artasset:vfxepic:e37milestone:m3needs-humanmw-e37.85Integrate approved base VFX textures and decals
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-vfx-framework,e29-placeholder-vfx,e29-spell-vfx-hooks.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 16 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the VFX stress scene, when effects built on the base textures run, then no placeholder flags remain for these ids and no console errors occur.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.10 Spell VFX slots and templates for the spell pipeline done
- mw-e29.2 Procedural placeholder VFX textures keyed by final asset ids done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.84 Owner approval: base VFX textures and decals open
area:artasset:vfxepic:e37milestone:m3mw-e37.90Write trail, telegraph and stealth VFX prompts (16 assets)
Description
Why
Gameplay cues — a charged swing, a focused arrow, an enemy wind-up, a noise the guards will hear, a smoke escape — must be readable at a glance (Legibility Language, §7–8).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Weapon trail, charge glow and ready burst; arrow trails (standard, focused), water burst, rope unfurl, noise-arrow ring; stealth noise ripple, smoke-bomb cloud, flash-powder burst, trap reveal highlight; warm-red enemy telegraph decals (circle, cone, line); last-known-position ghost ramp.
Asset ids (16):
vfx-weapon-trail-default-01vfx-charge-glow-01vfx-charge-ready-burst-01vfx-arrow-trail-01vfx-arrow-trail-focused-01vfx-water-burst-01vfx-rope-unfurl-01vfx-noise-ring-01vfx-noise-ripple-01vfx-smoke-bomb-cloud-01vfx-flash-powder-burst-01vfx-trap-reveal-highlight-01vfx-telegraph-circle-01vfx-telegraph-cone-01vfx-telegraph-line-01vfx-lkp-ghost-ramp-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-cues-trails-stealth-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Telegraphs use
#C8553Dat 40 % alpha (style bible §8) and must stay distinguishable from player AoEs. LKP ghost ramp servese09-lkp-ghost(P2). Flash powder obeys photosensitivity limits.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 16 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet or 512² strip, exact grid, black (additive) or transparent (alpha) background; telegraph decals transparent) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: hostile telegraphs are never confusable with friendly effects and the smoke cloud visibly blocks sight lines.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxepic:e37milestone:m3mw-e37.91Generate trail, telegraph and stealth VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 16 prompt files ofe37-asset-vfx-cues-trails-stealth-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-cues-trails-stealth-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet or 512² strip, exact grid, black (additive) or transparent (alpha) background; telegraph decals transparent. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 16 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet or 512² strip, exact grid, black (additive) or transparent (alpha) background; telegraph decals transparent), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.90 Write trail, telegraph and stealth VFX prompts (16 assets) open
area:artasset:vfxepic:e37milestone:m3mw-e37.92Owner approval: trail, telegraph and stealth VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 16 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-cues-trails-stealth-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: hostile telegraphs are never confusable with friendly effects and the smoke cloud visibly blocks sight lines. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 16 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then hostile telegraphs are never confusable with friendly effects and the smoke cloud visibly blocks sight lines.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-cues-trails-stealth-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.91 Generate trail, telegraph and stealth VFX via Codex CLI open
area:artasset:vfxepic:e37milestone:m3needs-humanmw-e37.93Integrate approved trail, telegraph and stealth VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-weapon-trails,e29-arrow-fx,e29-stealth-feedback-fx,e10-smoke-escape,e10-trap-disarm,e04-creature-attack-hooks.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 16 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the combat sandbox and stealth grey-box course, when charged attacks, arrows, telegraphs, noise and smoke are triggered, then every final effect plays with no console errors.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e10.12 Detect and disarm world traps; failures trigger them open
- mw-e10.13 Smoke bombs and flash powder break line of sight for escapes open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.11 Arrow VFX: trails, payload bursts, stuck and broken arrows done
- mw-e29.12 World-space awareness indicators and optional noise ripples done
- mw-e29.6 Weapon swing trails and charge build-up glow done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.92 Owner approval: trail, telegraph and stealth VFX open
area:artasset:vfxepic:e37milestone:m3mw-e37.94Write element and destruction VFX prompts (13 assets)
Description
Why
The systemic world only works if the player can see what is burning, wet, frozen, charged or about to break (constitution: systemic interactions over scripted ones).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Burning (small/large flame), smoke plume, steam puff, frost crust, wet drips, electric arc, gas fog, extinguish puff; destruction dust cloud and two crack stages; creature dissolve-on-death.
Asset ids (13):
vfx-debris-dust-cloud-01vfx-crack-stage-1-01vfx-crack-stage-2-01vfx-fire-flame-small-01vfx-fire-flame-large-01vfx-smoke-plume-01vfx-steam-puff-01vfx-frost-crust-01vfx-wet-drips-01vfx-electric-arc-01vfx-gas-fog-01vfx-extinguish-puff-01vfx-dissolve-death-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-elemental-destruction-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Physical elements follow §2.3 (water
#3F7FA0, poison gas#9BBF3A). Extinguish puff is shared by stealth light-dousing.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 13 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet, 8×8 loops for flames/smoke, 4×4 one-shots, alpha for smoke/steam/dust, additive for flame/arc cores) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each element state is identifiable in greyscale by shape and motion, and crack stages read as progressively weaker.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxepic:e37milestone:m3mw-e37.95Generate element and destruction VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 13 prompt files ofe37-asset-vfx-elemental-destruction-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-elemental-destruction-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet, 8×8 loops for flames/smoke, 4×4 one-shots, alpha for smoke/steam/dust, additive for flame/arc cores. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 13 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet, 8×8 loops for flames/smoke, 4×4 one-shots, alpha for smoke/steam/dust, additive for flame/arc cores), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.94 Write element and destruction VFX prompts (13 assets) open
area:artasset:vfxepic:e37milestone:m3mw-e37.96Owner approval: element and destruction VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 13 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-elemental-destruction-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each element state is identifiable in greyscale by shape and motion, and crack stages read as progressively weaker. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 13 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each element state is identifiable in greyscale by shape and motion, and crack stages read as progressively weaker.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-elemental-destruction-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.95 Generate element and destruction VFX via Codex CLI open
area:artasset:vfxepic:e37milestone:m3needs-humanmw-e37.97Integrate approved element and destruction VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-elemental-fx,e29-destruction-fx,e29-spectacle-shaders,e03-state-feedback-bridge.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 13 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the greybox chaos room, when objects are burned, soaked, frozen, charged and smashed, then every final effect plays and live particles stay ≤ 800 on Low.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e03.28 Show burning, wet, frozen and charged states through VFX and audio done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
- mw-e29.8 Visuals derived from element states: burning, wet, frozen, shock done
- mw-e29.9 Destruction VFX: fracture chunks, dust and crack stages open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.96 Owner approval: element and destruction VFX open
area:artasset:vfxepic:e37milestone:m3mw-e37.102Write combat HUD prompts (8 assets)
Description
Why
Combat decisions depend on reading health, stamina, poise and lock-on in a split second, in the brushstroke HUD style (§9).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Curved brushstroke health and stamina bars with frame and damage-chip overlay, lock-on reticle, target health/poise bar, hit marker and low-health edge vignette.
Asset ids (8):
ui-bar-health-01ui-bar-stamina-01ui-bar-frame-vitals-01ui-bar-damage-chip-01ui-reticle-lockon-01ui-bar-target-poise-01ui-marker-hit-01ui-vignette-low-health-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-combat-hud-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
No full-screen red vignette (§8): the low-health vignette is a subtle edge treatment. Must pass
e31-colorblind-support(shape + position, not colour alone).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 8 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate, transparent, fill-able bar masks with recorded fill axis, reticle 256²) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: bars read over bright snow and dark dungeon backgrounds and health vs stamina are distinguishable without colour.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiepic:e37milestone:m3mw-e37.103Generate combat HUD via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 8 prompt files ofe37-asset-ui-combat-hud-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-combat-hud-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate, transparent, fill-able bar masks with recorded fill axis, reticle 256². Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate, transparent, fill-able bar masks with recorded fill axis, reticle 256²), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.102 Write combat HUD prompts (8 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiepic:e37milestone:m3mw-e37.104Owner approval: combat HUD
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 8 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-combat-hud-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: bars read over bright snow and dark dungeon backgrounds and health vs stamina are distinguishable without colour. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then bars read over bright snow and dark dungeon backgrounds and health vs stamina are distinguishable without colour.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-combat-hud-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.103 Generate combat HUD via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3needs-humanmw-e37.105Integrate approved combat HUD
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e04-hud-vitals,e04-hud-target,e29-screen-feedback.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 8 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the combat sandbox, when the player takes damage, spends stamina and locks on, then the final HUD renders, damage chip animates, and no placeholder flags remain.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e04.10 Combat HUD: health and stamina bars with damage feedback done
- mw-e04.21 Lock-on reticle, target health/poise bar and hit markers done
- mw-e29.5 Camera shake, damage vignette and low-health effects done
- mw-e37.104 Owner approval: combat HUD open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3mw-e37.106Write stealth HUD and overlays prompts (25 assets)
Description
Why
The thief needs to know how visible and how loud they are and what each guard believes, without a full-screen alarm (§8 stealth feedback).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Light gem in 5 bands, noise ring, stance/gait glyphs, awareness chevrons in 6 distinct shapes (one per alert state) with eye/ear/hand sense glyphs, in-world lockpicking overlay, pickpocket list panel, throw trajectory arc + landing marker, trap placement ghost.
Asset ids (25):
ui-light-gem-band-01ui-light-gem-band-02ui-light-gem-band-03ui-light-gem-band-04ui-light-gem-band-05ui-noise-ring-01ui-glyph-stance-stand-01ui-glyph-stance-crouch-01ui-glyph-gait-sneak-01ui-glyph-gait-walk-01ui-glyph-gait-run-01ui-awareness-unaware-01ui-awareness-suspicious-01ui-awareness-investigating-01ui-awareness-searching-01ui-awareness-alerted-01ui-awareness-combat-01ui-glyph-sense-eye-01ui-glyph-sense-ear-01ui-glyph-sense-hand-01ui-lockpick-overlay-01ui-pickpocket-panel-01ui-throw-arc-01ui-marker-throw-landing-01ui-trap-placement-ghost-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-stealth-hud-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Awareness shapes must differ by shape, not only colour (parchment → hearth → ember). Trap placement ghost serves
e10-trap-placement(P2).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 25 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (glyphs flat single-colour 64² (SVG after vectorising), light gem 256² per band, overlays 1024² transparent) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the 6 awareness chevrons are distinguishable in greyscale at 24 px and light gem bands are ordered by value.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiclass:thiefepic:e37milestone:m3mw-e37.107Generate stealth HUD and overlays via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 25 prompt files ofe37-asset-ui-stealth-hud-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-stealth-hud-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: glyphs flat single-colour 64² (SVG after vectorising), light gem 256² per band, overlays 1024² transparent. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 25 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (glyphs flat single-colour 64² (SVG after vectorising), light gem 256² per band, overlays 1024² transparent), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.106 Write stealth HUD and overlays prompts (25 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiclass:thiefepic:e37milestone:m3mw-e37.108Owner approval: stealth HUD and overlays
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 25 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-stealth-hud-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the 6 awareness chevrons are distinguishable in greyscale at 24 px and light gem bands are ordered by value. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 25 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the 6 awareness chevrons are distinguishable in greyscale at 24 px and light gem bands are ordered by value.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-stealth-hud-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.107 Generate stealth HUD and overlays via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiclass:thiefepic:e37milestone:m3needs-humanmw-e37.109Integrate approved stealth HUD and overlays
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e09-visibility-hud,e09-awareness-indicators,e10-lockpicking,e10-pickpocketing,e10-thrown-distractions.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 25 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the stealth grey-box course, when the thief moves through light bands and guards cycle all six alert states, then every final HUD element renders with no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e09.10 Stealth HUD: light exposure, noise ring and stance readout open
- mw-e09.11 Directional awareness indicators showing each enemy’s alert state open
- mw-e10.2 Throw objects to create noise distractions guards investigate open
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e37.108 Owner approval: stealth HUD and overlays open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiclass:thiefepic:e37milestone:m3mw-e37.110Write kit item icons prompts (21 assets)
Description
Why
Players manage their kit in the inventory and quick slots; every starting weapon, arrow type and thief tool needs a readable icon that matches its world model.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: icon-item) from templateitem-icon.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Painted ¾ top-down item icons for the four class kits: sword, shields, bow, arrow variants, dagger, staff, and thief tools (lockpick, coin, bottle, smoke bomb, noisemaker and tripwire traps, caltrops, water flask, sap).
Asset ids (21):
icon-item-sword-01icon-item-shield-wood-01icon-item-shield-metal-01icon-item-bow-01icon-item-arrow-standard-01icon-item-arrow-fire-01icon-item-arrow-water-01icon-item-arrow-rope-01icon-item-arrow-noise-01icon-item-arrow-blunt-01icon-item-dagger-01icon-item-staff-01icon-item-lockpick-iron-01icon-item-coin-01icon-item-bottle-01icon-item-smoke-bomb-01icon-item-trap-noisemaker-01icon-item-trap-tripwire-01icon-item-caltrops-01icon-item-water-flask-01icon-item-sap-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-icons-items-kit-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Weapon icons use the approved weapon concepts as reference inputs. Loot/world item icons (potions, keys, treasures) are owned by the world/items asset set.
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 21 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every icon is identifiable at 64 px and arrow variants are distinguishable by head/payload shape, not colour alone.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.38 Owner approval: player weapons flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:iconepic:e37milestone:m3mw-e37.111Generate kit item icons via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 21 prompt files ofe37-asset-icons-items-kit-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-icons-items-kit-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 21 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.110 Write kit item icons prompts (21 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:iconepic:e37milestone:m3mw-e37.112Owner approval: kit item icons
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 21 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-icons-items-kit-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every icon is identifiable at 64 px and arrow variants are distinguishable by head/payload shape, not colour alone. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 21 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every icon is identifiable at 64 px and arrow variants are distinguishable by head/payload shape, not colour alone.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-icons-items-kit-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.111 Generate kit item icons via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconepic:e37milestone:m3needs-humanmw-e37.113Integrate approved kit item icons
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e17-inventory-ui,e17-item-schema,e10-thief-tool-items,e05-arrow-schema.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 21 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the inventory with every kit item granted via debug console, when opened, then all 21 icons render from the atlas with no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e05.1 Data schema for arrow types and property-based payloads done
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.2 Define item definition schema and content validator done
- mw-e37.112 Owner approval: kit item icons open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconepic:e37milestone:m3mw-e37.114Write inventory UI prompts (16 assets)
Description
Why
The inventory is opened constantly; category tabs and the stolen-item marker must be instantly readable (theft has consequences).
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md (+ item-icon.md for gold coin), with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.12 category tab glyphs, gold coin icon, stolen-item marker, quick-slot frame and inventory panel 9-slice.
Asset ids (16):
ui-glyph-cat-weapon-01ui-glyph-cat-armor-01ui-glyph-cat-shield-01ui-glyph-cat-ammo-01ui-glyph-cat-book-01ui-glyph-cat-key-01ui-glyph-cat-consumable-01ui-glyph-cat-tool-01ui-glyph-cat-quest-01ui-glyph-cat-artifact-01ui-glyph-cat-currency-01ui-glyph-cat-misc-01icon-item-gold-coin-01ui-marker-stolen-01ui-quickslot-frame-01ui-panel-inventory-9slice-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-inventory-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Quick-slot frame matches the brass-rimmed circles of the HUD (§9).
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 16 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (glyphs flat single-colour 64²; panel 1024² transparent 9-slice; coin as item icon 256²) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: all 12 category glyphs are distinguishable at 24 px and the stolen marker is visible on any icon.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiepic:e37milestone:m3mw-e37.115Generate inventory UI via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 16 prompt files ofe37-asset-ui-inventory-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-inventory-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: glyphs flat single-colour 64²; panel 1024² transparent 9-slice; coin as item icon 256². Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 16 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (glyphs flat single-colour 64²; panel 1024² transparent 9-slice; coin as item icon 256²), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.114 Write inventory UI prompts (16 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiepic:e37milestone:m3mw-e37.116Owner approval: inventory UI
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 16 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-inventory-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: all 12 category glyphs are distinguishable at 24 px and the stolen marker is visible on any icon. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 16 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then all 12 category glyphs are distinguishable at 24 px and the stolen marker is visible on any icon.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-inventory-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.115 Generate inventory UI via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3needs-humanmw-e37.117Integrate approved inventory UI
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e17-inventory-ui,e17-consumables-quickslots,e18-ownership-stolen.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 16 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the inventory screen, when switching every category tab and viewing a stolen item, then all elements render from final art with no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.6 Use consumables from inventory and assignable quick slots done
- mw-e18.6 Track item ownership and flag stolen items on witnessed theft open
- mw-e37.116 Owner approval: inventory UI open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3mw-e37.118Write equipment and loot UI prompts (14 assets)
Description
Why
Finding a unique artifact should feel like a discovery, and equipping gear should make trade-offs (verbs, stealth) visible.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Nine equipment slot silhouettes, compare up/down arrows, container window 9-slice, pickup toast frame and artifact discovery banner.
Asset ids (14):
ui-slot-head-01ui-slot-body-01ui-slot-hands-01ui-slot-feet-01ui-slot-main-hand-01ui-slot-off-hand-01ui-slot-trinket-01ui-slot-ammo-01ui-slot-tool-01ui-glyph-compare-up-01ui-glyph-compare-down-01ui-panel-container-9slice-01ui-toast-pickup-01ui-banner-artifact-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-equipment-loot-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Compare arrows must differ by shape, not only colour.
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 14 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (slot silhouettes flat ink 128²; panels/banners 1024² transparent 9-slice) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every slot silhouette is recognisable at 48 px and the artifact banner feels celebratory but on-style.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiepic:e37milestone:m3mw-e37.119Generate equipment and loot UI via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 14 prompt files ofe37-asset-ui-equipment-loot-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-equipment-loot-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: slot silhouettes flat ink 128²; panels/banners 1024² transparent 9-slice. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 14 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (slot silhouettes flat ink 128²; panels/banners 1024² transparent 9-slice), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.118 Write equipment and loot UI prompts (14 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiepic:e37milestone:m3mw-e37.120Owner approval: equipment and loot UI
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 14 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-equipment-loot-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every slot silhouette is recognisable at 48 px and the artifact banner feels celebratory but on-style. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every slot silhouette is recognisable at 48 px and the artifact banner feels celebratory but on-style.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-equipment-loot-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.119 Generate equipment and loot UI via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3needs-humanmw-e37.121Integrate approved equipment and loot UI
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e17-equipment-ui,e18-container-ui,e18-unique-artifacts.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 14 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the equipment and container screens, when an artifact is looted and equipped, then all elements render from final art with no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e17.11 Add equipment paper-doll view with verb and stealth comparison open
- mw-e18.4 Build container window and loot pickup notifications done
- mw-e18.8 Add unique artifacts with active verbs and lore open
- mw-e37.120 Owner approval: equipment and loot UI open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3mw-e37.122Write class select art prompts (8 assets)
Description
Why
Class selection is the player's first real choice; each card should sell a different way to play.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: keyart / ui) from templatekey-art-title-screen.md (cards) + ui-element.md (emblems), with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Four class card illustrations depicting the approved character designs in a signature moment, and four class emblems (flat, heraldic, one per class).
Asset ids (8):
keyart-class-card-knight-01keyart-class-card-archer-01keyart-class-card-sorcerer-01keyart-class-card-thief-01ui-emblem-class-knight-01ui-emblem-class-archer-01ui-emblem-class-sorcerer-01ui-emblem-class-thief-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-class-select-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Cards use the approved character sheets as reference inputs so the card and the model match.
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 8 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (cards 2:3 1024×1536 opaque; emblems 1024² transparent → 256²/128²) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each card matches its approved character sheet and the four emblems are distinguishable by silhouette.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.20 Owner approval: player class characters flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:conceptclass:allepic:e37milestone:m3mw-e37.123Generate class select art via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 8 prompt files ofe37-asset-ui-class-select-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-class-select-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: cards 2:3 1024×1536 opaque; emblems 1024² transparent → 256²/128². Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (cards 2:3 1024×1536 opaque; emblems 1024² transparent → 256²/128²), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.122 Write class select art prompts (8 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptclass:allepic:e37milestone:m3mw-e37.124Owner approval: class select art
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 8 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-class-select-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each card matches its approved character sheet and the four emblems are distinguishable by silhouette. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each card matches its approved character sheet and the four emblems are distinguishable by silhouette.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-class-select-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.123 Generate class select art via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptclass:allepic:e37milestone:m3needs-humanmw-e37.125Integrate approved class select art
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e19-class-selection.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 8 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given a new game, when the class select screen opens, then four final cards and emblems render with no placeholder flags and no console errors.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e19.5 Add class selection at new game that applies kit and capabilities done
- mw-e37.124 Owner approval: class select art open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptclass:allepic:e37milestone:m3mw-e37.126Write class verb icons prompts (25 assets)
Description
Why
Progression is capability unlocks, not numbers; each new verb deserves an icon and a moment.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Verb glyphs per class (knight 6, archer 6, thief 8, sorcerer 4) and the "new verb learned" banner frame.
Asset ids (25):
ui-glyph-verb-knight-block-01ui-glyph-verb-knight-parry-01ui-glyph-verb-knight-riposte-01ui-glyph-verb-knight-heavy-charge-01ui-glyph-verb-knight-shield-bash-01ui-glyph-verb-knight-kick-01ui-glyph-verb-archer-draw-aim-01ui-glyph-verb-archer-focused-release-01ui-glyph-verb-archer-breath-hold-01ui-glyph-verb-archer-quickstep-01ui-glyph-verb-archer-aerial-shot-01ui-glyph-verb-archer-retrieve-arrow-01ui-glyph-verb-thief-lockpick-01ui-glyph-verb-thief-pickpocket-01ui-glyph-verb-thief-takedown-01ui-glyph-verb-thief-carry-body-01ui-glyph-verb-thief-climb-01ui-glyph-verb-thief-disguise-01ui-glyph-verb-thief-place-trap-01ui-glyph-verb-thief-lean-peek-01ui-glyph-verb-sorcerer-study-book-01ui-glyph-verb-sorcerer-cast-01ui-glyph-verb-sorcerer-channel-01ui-glyph-verb-sorcerer-experiment-insight-01ui-banner-verb-learned-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-capabilities-verbs-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Verb list is provisional; reconcile with the capability registry in
e19-class-definitionsbefore writing prompts (add/remove ids in this bead's description). Sorcerer spells use spell icons, so only meta-verbs are here.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 25 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (glyphs flat single-colour 64²/128² transparent; banner 1024² transparent 9-slice) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: verb glyphs read at 32 px and each class's set shares a consistent visual motif.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiclass:allepic:e37milestone:m3mw-e37.127Generate class verb icons via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 25 prompt files ofe37-asset-ui-capabilities-verbs-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-capabilities-verbs-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: glyphs flat single-colour 64²/128² transparent; banner 1024² transparent 9-slice. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 25 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (glyphs flat single-colour 64²/128² transparent; banner 1024² transparent 9-slice), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.126 Write class verb icons prompts (25 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiclass:allepic:e37milestone:m3mw-e37.128Owner approval: class verb icons
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 25 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-capabilities-verbs-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: verb glyphs read at 32 px and each class's set shares a consistent visual motif. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 25 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then verb glyphs read at 32 px and each class's set shares a consistent visual motif.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-capabilities-verbs-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.127 Generate class verb icons via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiclass:allepic:e37milestone:m3needs-humanmw-e37.129Integrate approved class verb icons
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e19-capabilities-screen,e19-class-definitions.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 25 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the capabilities screen with every verb granted via debug console, when opened, then all verb glyphs render and the verb-learned banner plays on unlock with no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e19.12 Build capabilities screen and "new verb learned" feedback open
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
- mw-e37.128 Owner approval: class verb icons open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiclass:allepic:e37milestone:m3mw-e37.130Write input glyphs prompts (24 assets)
Description
Why
Contextual prompts and remapping must show the player's actual bindings in the house ink-brush style.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Blank key caps (key label rendered by the game), mouse buttons/wheel/move, and generic gamepad glyphs (face buttons by position, bumpers, triggers, sticks, d-pad, start/select).
Asset ids (24):
ui-glyph-input-key-cap-01ui-glyph-input-key-cap-wide-01ui-glyph-input-mouse-left-01ui-glyph-input-mouse-right-01ui-glyph-input-mouse-middle-01ui-glyph-input-mouse-wheel-01ui-glyph-input-mouse-move-01ui-glyph-input-pad-face-south-01ui-glyph-input-pad-face-east-01ui-glyph-input-pad-face-west-01ui-glyph-input-pad-face-north-01ui-glyph-input-pad-bumper-left-01ui-glyph-input-pad-bumper-right-01ui-glyph-input-pad-trigger-left-01ui-glyph-input-pad-trigger-right-01ui-glyph-input-pad-stick-left-01ui-glyph-input-pad-stick-right-01ui-glyph-input-pad-stick-press-01ui-glyph-input-pad-dpad-up-01ui-glyph-input-pad-dpad-down-01ui-glyph-input-pad-dpad-left-01ui-glyph-input-pad-dpad-right-01ui-glyph-input-pad-start-01ui-glyph-input-pad-select-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-input-glyphs-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
No letters in images: key labels and brand-specific face-button letters are rendered in Alegreya Sans SC over the blank glyph. Generic positional face buttons avoid platform trademarks. Glyphs may be traced to SVG in integration.
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 24 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (flat single-colour ink glyphs, 1024² generate → 64² PNG + SVG trace, transparent) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: glyphs are legible at 24 px beside Alegreya Sans text and share stroke weight.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiepic:e37milestone:m3mw-e37.131Generate input glyphs via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 24 prompt files ofe37-asset-ui-input-glyphs-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-input-glyphs-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: flat single-colour ink glyphs, 1024² generate → 64² PNG + SVG trace, transparent. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 24 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (flat single-colour ink glyphs, 1024² generate → 64² PNG + SVG trace, transparent), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.130 Write input glyphs prompts (24 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiepic:e37milestone:m3mw-e37.132Owner approval: input glyphs
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 24 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-input-glyphs-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: glyphs are legible at 24 px beside Alegreya Sans text and share stroke weight. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 24 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then glyphs are legible at 24 px beside Alegreya Sans text and share stroke weight.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-input-glyphs-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.131 Generate input glyphs via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3needs-humanmw-e37.133Integrate approved input glyphs
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e02-interaction,e02-input-gamepad,e31-remap-ui.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 24 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the remap screen and an interaction prompt, when switching between KB/M and gamepad, then the matching final glyphs render with the bound key label overlaid.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e02.9 Support gamepad input with deadzones and remappable bindings done
- mw-e31.2 Let players remap keyboard, mouse and gamepad bindings done
- mw-e37.132 Owner approval: input glyphs open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3mw-e37.134Write shop and dialogue UI prompts (18 assets)
Description
Why
Talking and trading are how the player learns the world and its people; class-gated options must be visible so each class feels its own way through a conversation.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: ui) from templateui-element.md (+ npc-portrait.md for the generic silhouette), with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Shop panel, merchant portrait frame, specialty badge, buyback glyph; dialogue box, portrait frame, generic silhouette portrait, class tag icons, persuasion approach glyphs (persuade, intimidate, deceive, charm), locked-option glyph, subtitle plate and off-screen speaker chevron.
Asset ids (18):
ui-panel-shop-9slice-01ui-frame-merchant-portrait-01ui-badge-specialty-01ui-glyph-buyback-01ui-panel-dialogue-9slice-01ui-frame-dialogue-portrait-01portrait-generic-silhouette-01ui-tag-class-knight-01ui-tag-class-archer-01ui-tag-class-sorcerer-01ui-tag-class-thief-01ui-glyph-persuade-01ui-glyph-intimidate-01ui-glyph-deceive-01ui-glyph-charm-01ui-glyph-option-locked-01ui-plate-subtitle-01ui-glyph-offscreen-chevron-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-ui-shop-dialogue-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
NPC portraits themselves are owned by the world/character asset set. Subtitle plate must keep text ≥ 7:1 contrast over any scene.
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 18 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (panels 1024² transparent 9-slice; glyphs flat single-colour 64²; generic portrait bust 1024² per npc-portrait template) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the four persuasion glyphs and four class tags are distinguishable at 24 px and panels match the approved menu kit.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:uiepic:e37milestone:m3mw-e37.135Generate shop and dialogue UI via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 18 prompt files ofe37-asset-ui-shop-dialogue-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-ui-shop-dialogue-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: panels 1024² transparent 9-slice; glyphs flat single-colour 64²; generic portrait bust 1024² per npc-portrait template. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 18 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (panels 1024² transparent 9-slice; glyphs flat single-colour 64²; generic portrait bust 1024² per npc-portrait template), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.134 Write shop and dialogue UI prompts (18 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:uiepic:e37milestone:m3mw-e37.136Owner approval: shop and dialogue UI
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 18 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ui-shop-dialogue-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the four persuasion glyphs and four class tags are distinguishable at 24 px and panels match the approved menu kit. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 18 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the four persuasion glyphs and four class tags are distinguishable at 24 px and panels match the approved menu kit.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ui-shop-dialogue-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.135 Generate shop and dialogue UI via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3needs-humanmw-e37.137Integrate approved shop and dialogue UI
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e20-shop-ui,e22-dialogue-ui,e22-class-dialogue-options,e22-persuasion-checks.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 18 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the reference quest fixture and a fixture merchant, when talking and trading, then every element renders from final art with no placeholder flags.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e20.10 Build shop UI with merchant identity, buy/sell and price breakdown done
- mw-e22.10 Build accessible dialogue UI with choices, tags and history done
- mw-e22.6 Add class- and capability-gated dialogue options with tags done
- mw-e22.7 Add deterministic persuasion checks with fail branches done
- mw-e37.136 Owner approval: shop and dialogue UI open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:uiepic:e37milestone:m3mw-e37.138Write title and loading art prompts (6 assets)
Description
Why
The title screen is the first impression and sets the tone: souls-like weight and mystery, but warm and hopeful.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: keyart) from templatekey-art-title-screen.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Title screen key art with negative space for the logo, a painted logo emblem (the wordmark itself is typeset in Cinzel Decorative, never generated), and four loading-screen illustrations, one per class.
Asset ids (6):
keyart-title-screen-01ui-logo-emblem-01keyart-loading-knight-01keyart-loading-archer-01keyart-loading-sorcerer-01keyart-loading-thief-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-keyart-title-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Prompts wait for
e39-title-decision(logo) and the approved character sheets (classes appear in the art). Related: no dedicated title/main-menu bead exists yet —e30-save-load-ui(Continue/Load) is the nearest consumer; flag for the integration pass.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 6 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (title 16:9 1536×1024 master (upscaled to 3840×2160 in pipeline), loading 16:9 opaque, emblem 1024² transparent) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the title art leaves a clean logo area, reads warm rather than bleak, and no letters appear anywhere.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.20 Owner approval: player class characters flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.2 Decide the shipping title and record it as an ADR done
area:artasset:conceptepic:e37milestone:m3mw-e37.139Generate title and loading art via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 6 prompt files ofe37-asset-keyart-title-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-keyart-title-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: title 16:9 1536×1024 master (upscaled to 3840×2160 in pipeline), loading 16:9 opaque, emblem 1024² transparent. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 6 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (title 16:9 1536×1024 master (upscaled to 3840×2160 in pipeline), loading 16:9 opaque, emblem 1024² transparent), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.138 Write title and loading art prompts (6 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.140Owner approval: title and loading art
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 6 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-keyart-title-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the title art leaves a clean logo area, reads warm rather than bleak, and no letters appear anywhere. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 6 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the title art leaves a clean logo area, reads warm rather than bleak, and no letters appear anywhere.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-keyart-title-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.139 Generate title and loading art via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.141Integrate approved title and loading art
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e30-save-load-ui,e32-asset-loading.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 6 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given a cold boot, when the title and a loading screen show, then final art renders with the typeset logo overlaid and the initial-download budget (≤ 50 MB) still passes.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e01.2 Title screen flows into class select, spawn and continue done
- mw-e30.11 Build Continue / Load / Save menu screens done
- mw-e32.7 Stream and compress assets with a progress-reporting loader open
- mw-e37.140 Owner approval: title and loading art open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.143Write River Wend environment concepts prompts (5 assets)
Description
Why
The River Wend links Briar Glen to the sea and adds three class-route set pieces (Stone Stair, Millweir, Weeping Adit) plus the towpath and Kestrel Lock; they need a shared look before any river asset is made (style bible §2.2 River Wend row).
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Briar Glen Quay with the stone barge *Patient Ann* (ochre hull stripe, no painted name) taking on dressed glenstone blocks from their timber cradles, a quay crane, and the Stone Stair, a stepped slipway for blocks coming down from the Deepworks' cutting hall, pale with stone dust; the Vesperine Millweir with a bell-mark keystone, clear walkable sluice top and two water levels; the Weeping Adit mouth where the Wend springs from the hill (cold, dripping, a faint Nightjar shimmer #CFC6F2 on the water), flooded tunnel with air pockets and an empty stone barge being winched back up on its chain; the towpath down the valley; Kestrel Lock with balance-beam gates, windlasses, a flaking toll board (no legible text) and Old Reed's cottage.
Asset ids (5):
env-briar-glen-quay-01env-millweir-sluice-walk-01env-weeping-adit-mouth-01env-towpath-valley-01env-kestrel-lock-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-river-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Water is standing water only (no flowing-river sim); the barge ride is a narrated card (
e37-asset-barge-ride-cards-*), soenv-barge-ride-card-01from the needs list is produced there as runtime key art.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 5 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each painting sits in the River Wend palette (brown water #6B5A3E, reeds #8FA05A, tarred timber #3A2E26, lock-gate red #A8473A), water reads as water but not as a current, and every class route (climb the Stone Stair, walk the sluice top, swim the Adit air pockets) is visible.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.144Generate River Wend environment concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 5 prompt files ofe37-asset-env-river-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-river-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 5 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.143 Write River Wend environment concepts prompts (5 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.145Owner approval: River Wend environment concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 5 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-river-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each painting sits in the River Wend palette (brown water #6B5A3E, reeds #8FA05A, tarred timber #3A2E26, lock-gate red #A8473A), water reads as water but not as a current, and every class route (climb the Stone Stair, walk the sluice top, swim the Adit air pockets) is visible. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 5 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each painting sits in the River Wend palette (brown water #6B5A3E, reeds #8FA05A, tarred timber #3A2E26, lock-gate red #A8473A), water reads as water but not as a current, and every class route (climb the Stone Stair, walk the sluice top, swim the Adit air pockets) is visible.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-river-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.144 Generate River Wend environment concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.146Integrate approved River Wend environment concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e25-quay-stone-stair,e25-millweir-sluice-walk,e25-weeping-adit,e25-towpath-kestrel-lock. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-river-art-audio.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for the River Wend locations, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e25.12 Build the Millweir and its sluice walk to the Briarwood far bank open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e37.145 Owner approval: River Wend environment concepts open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.147Write Wendmouth environment concepts prompts (12 assets)
Description
Why
Wendmouth is loud, cheerful, crooked and built like a city out of the valley's glenstone — the opposite of Briar Glen's golden-hour cosiness (style bible §2.2 Wendmouth row) — and it is the thief's playground; every harbour asset is judged against these paintings.
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Establishing harbour view of a monumental glenstone harbour (colossal stepped quays worn smooth by hulls, the great curving breakwater of mason-marked reject blocks, vaulted stone warehouses, the columned Customs House, treadwheel cranes on stone piers and block yards of cut stone, with timber net lofts, tarred sheds and bright shopfronts perched on the stone; people, sheds and boats small against it; the Gilded Tern in the pool); the harbour at night (lantern pools on wet flagstones, ship lamps, lighthouse sweep); the Stone Wharf on Tidefair day (stone barges unloading dressed blocks, treadwheel cranes, cargo nets, contests on the open flagstones, the gated Silver Quay with imperial guards at its far end, seen not entered); the columned glenstone Customs House (Lantern Court flag, cargo scale, Cray's office, strongroom); the vaulted stone Bonded Warehouse with bay nine, skylights and hoists; the Drowned Gull; the Tidemarket; the Undertow in the low vaulted block-vaults under the fish market; the net-loft rooftop route; the Tern's deck and rigging; the Tern's hold, cabin and locked berth; the mason-marked block breakwater with the sunken figurehead and sea-door at low and high tide.
Asset ids (12):
env-wendmouth-harbour-01env-wendmouth-night-01env-wendmouth-stone-wharf-tidefair-01env-wendmouth-customs-house-01env-wendmouth-bonded-warehouse-01env-drowned-gull-interior-01env-wendmouth-tidemarket-01env-wendmouth-undertow-01env-wendmouth-rooftops-net-lofts-01env-gilded-tern-deck-01env-gilded-tern-hold-cabin-01env-wendmouth-breakwater-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-wendmouth-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Also mood targets for
e40-stone-wharf,e40-customs-house,e40-bonded-warehouse,e40-drowned-gull,e40-tidemarket,e40-undertow,e40-rooftops-net-lofts,e40-gilded-tern,e40-breakwater-sea-door,e40-tidefair-strength. Night concept also informse40-harbor-lighting-audio.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 12 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: Wendmouth reads high-key, breezy and busier in colour than Briar Glen (salt-bleached #B8AE9A, tar #2F2A28, bunting turquoise/sunflower/coral), climbable taut nets and rigging read distinct from loose drying nets, and the rooftop route reads as one continuous line from the wharf.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.148Generate Wendmouth environment concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 12 prompt files ofe37-asset-env-wendmouth-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-wendmouth-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 12 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.147 Write Wendmouth environment concepts prompts (12 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.149Owner approval: Wendmouth environment concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 12 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-wendmouth-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Wendmouth reads high-key, breezy and busier in colour than Briar Glen (salt-bleached #B8AE9A, tar #2F2A28, bunting turquoise/sunflower/coral), climbable taut nets and rigging read distinct from loose drying nets, and the rooftop route reads as one continuous line from the wharf. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Wendmouth reads high-key, breezy and busier in colour than Briar Glen (salt-bleached #B8AE9A, tar #2F2A28, bunting turquoise/sunflower/coral), climbable taut nets and rigging read distinct from loose drying nets, and the rooftop route reads as one continuous line from the wharf.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-wendmouth-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.148 Generate Wendmouth environment concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.150Integrate approved Wendmouth environment concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e40-harbor-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-art-dress.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for Wendmouth, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:artasset:conceptepic:e37milestone:m3mw-e37.151Write River Wend tileables prompts (5 assets)
Description
Why
Brown river water, tarred barge timber, towpath clay and green lock gates carry the river palette across the quay, weir, lock and adit.
What
Write one prompt file per asset in
assets/prompts/texture/<asset-id>.md(category: texture) from templatetileable-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Brown river water surface (standing water, readable as freezable/swimmable), tarred timber, lock-gate paint, towpath clay, and Vesperine weir stone with the bell-mark keystone as a trim.
Asset ids (5):
tex-water-river-brown-01tex-timber-tarred-01tex-lock-gate-green-01tex-towpath-clay-01tex-trim-millweir-stone-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-tex-river-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
tex-towpath-clay-01andtex-trim-millweir-stone-01are added from the style bible River Wend row. The style bible names lock-gate red #A8473A while the needs list saystex-lock-gate-green-01; the id is kept and the prompt follows the bible colour (flagged for reconciliation).tex-timber-tarred-01is shared with Wendmouth.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 5 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: surfaces tile invisibly, the river water mirrors the sky and reads as water without implying a current, and the weir trim carries the bell mark as a shape only.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.145 Owner approval: River Wend environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.152Generate River Wend tileables via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 5 prompt files ofe37-asset-tex-river-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-tex-river-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 5 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha), then dimensions, transparency/background, seamless tiling on both axes (automated edge-difference check) and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.151 Write River Wend tileables prompts (5 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.153Owner approval: River Wend tileables
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 5 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-tex-river-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: surfaces tile invisibly, the river water mirrors the sky and reads as water without implying a current, and the weir trim carries the bell mark as a shape only. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 5 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then surfaces tile invisibly, the river water mirrors the sky and reads as water without implying a current, and the weir trim carries the bell mark as a shape only.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-tex-river-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.152 Generate River Wend tileables via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.154Integrate approved River Wend tileables
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, KTX2 + mip build, vertex-colour blend variants
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e25-quay-stone-stair,e25-towpath-kestrel-lock. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-river-art-audio.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 5 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the quay, weir, towpath and lock, when Playwright captures each, then final textures load with no placeholder materials and no console errors.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e37.153 Owner approval: River Wend tileables open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3mw-e37.155Write Wendmouth tileables and trims prompts (9 assets)
Description
Why
Salt-weathered glenstone, bleached planks, tar, rope and canvas dominate every harbour frame; the loud tin roof versus quiet tar roof is a thief-route rule.
What
Write one prompt file per asset in
assets/prompts/texture/<asset-id>.md(category: texture) from templatetileable-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Salt-bleached planks, hawser rope trim, cargo net alpha (climbable/flammable legibility), sailcloth, salt-weathered glenstone blocks with mason's marks (breakwater, stepped quays and warehouse walls), grey harbour water, loud tin roof vs quiet tar roof, and a Wendmouth painted-shopfront trim sheet.
Asset ids (9):
tex-planks-salt-bleached-01tex-rope-hawser-trim-01tex-net-cargo-alpha-01tex-sailcloth-01tex-stone-breakwater-01tex-water-harbour-grey-01tex-roof-tin-harbour-01tex-roof-tar-harbour-01tex-trim-wendmouth-shopfront-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-tex-harbour-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
tex-trim-wendmouth-shopfront-01is added for §6.3 trims.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 9 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the tin and tar roofs are distinguishable at 10 m in greyscale, the cargo net alpha reads taut/climbable per style bible §5.4, and the palette stays salt-bleached and high-key.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.156Generate Wendmouth tileables and trims via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 9 prompt files ofe37-asset-tex-harbour-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-tex-harbour-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 9 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha), then dimensions, transparency/background, seamless tiling on both axes (automated edge-difference check) and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.155 Write Wendmouth tileables and trims prompts (9 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.157Owner approval: Wendmouth tileables and trims
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 9 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-tex-harbour-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the tin and tar roofs are distinguishable at 10 m in greyscale, the cargo net alpha reads taut/climbable per style bible §5.4, and the palette stays salt-bleached and high-key. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the tin and tar roofs are distinguishable at 10 m in greyscale, the cargo net alpha reads taut/climbable per style bible §5.4, and the palette stays salt-bleached and high-key.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-tex-harbour-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.156 Generate Wendmouth tileables and trims via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.158Integrate approved Wendmouth tileables and trims
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, KTX2 + mip build, vertex-colour blend variants
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e40-harbor-greybox,e40-rooftops-net-lofts. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-art-dress.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 9 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Wendmouth, when Playwright captures the wharf and the rooftop route, then final textures load, the tin and tar roofs use distinct surface-acoustic material ids, and no console errors occur.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e37.157 Owner approval: Wendmouth tileables and trims open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
- mw-e40.13 Build a continuous rooftop and net-loft route across the waterfront open
area:artasset:textureepic:e37milestone:m3mw-e37.159Write harbour cast portraits prompts (9 assets)
Description
Why
Wendmouth's shenanigans are social; text-only dialogue leans on portraits for Mags's gruffness, Nell's gold tooth and Cray's starch.
What
Write one prompt file per asset in
assets/prompts/portrait/<asset-id>.md(category: portrait) from templatenpc-portrait.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Mags Oakum (pipe, forearms like hawsers), Nell Gannet (tiny, gold-toothed grin), Inspector Prudence Cray (starched, spectacles, brass lantern-in-ring badge), Tereza Maelo (elegant Saltmarch cut, scarred hands), Jasper Quell (ink-stained cuffs, nervous stoop), Big Oona (dockhand champion), Old Reed (lock keeper), Captain Hesketh, and "Mr. S—'s agent" as a veiled silhouette only.
Asset ids (9):
portrait-npc-mags-bargewomanportrait-npc-nell-gull-keeperportrait-npc-cray-inspectorportrait-npc-tereza-merchantportrait-npc-jasper-shipping-agentportrait-npc-oona-dockhandportrait-npc-reed-lock-keeperportrait-npc-hesketh-captainportrait-npc-agent-veiled
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-portraits-harbour-cast-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 9 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each matches story bible §5.13/§5.14 and the style bible §5.4 silhouette hooks, the set matches the approved Briar Glen cast portraits in style, and the veiled agent shows no face.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:portraitepic:e37milestone:m3mw-e37.160Generate harbour cast portraits via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 9 prompt files ofe37-asset-portraits-harbour-cast-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-portraits-harbour-cast-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 9 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.159 Write harbour cast portraits prompts (9 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:portraitepic:e37milestone:m3mw-e37.161Owner approval: harbour cast portraits
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 9 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-portraits-harbour-cast-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each matches story bible §5.13/§5.14 and the style bible §5.4 silhouette hooks, the set matches the approved Briar Glen cast portraits in style, and the veiled agent shows no face. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each matches story bible §5.13/§5.14 and the style bible §5.4 silhouette hooks, the set matches the approved Briar Glen cast portraits in style, and the veiled agent shows no face.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-portraits-harbour-cast-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.160 Generate harbour cast portraits via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:portraitepic:e37milestone:m3needs-humanmw-e37.162Integrate approved harbour cast portraits
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e22-dialogue-ui,e25-barge-travel,e25-towpath-kestrel-lock,e40-drowned-gull,e40-customs-house,e40-tidemarket,e40-bonded-warehouse,e40-gilded-tern,e40-tidefair-strength.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 9 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given each harbour character's dialogue opened in the dialogue preview runner, when rendered, then the final portrait shows with no placeholder flags and no console errors.
- AC-5 perf Given the harbour portrait atlas, when
e32-perf-budgetsruns, then it streams with the Wendmouth package and resident texture memory stays ≤ 256 MB on Low. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e22.10 Build accessible dialogue UI with choices, tags and history done
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e37.161 Owner approval: harbour cast portraits open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
area:artasset:portraitepic:e37milestone:m3mw-e37.163Write barge ride card art prompts (2 assets)
Description
Why
The barge journey is a narrated ride card, not a simulated trip (story bible §3.6); two painted cards make the fast travel feel like a place.
What
Write one prompt file per asset in
assets/prompts/keyart/<asset-id>.md(category: keyart) from templatekey-art-title-screen.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.*Patient Ann* drifting downriver on a misty morning (Mags at the tiller, pipe smoke), and the barge towed back upriver by the towpath horse — no painted ship name, space left for narration text.
Asset ids (2):
keyart-barge-ride-down-01keyart-barge-ride-up-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-barge-ride-cards-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Replaces the needs-list id
env-barge-ride-card-01(one concept) with two runtime cards (down/up) because they ship as UI art.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1920×1080 generate → KTX2 UASTC full-screen card, safe area for font-rendered narration text) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: both cards read as the same barge and river as the approved river concepts, leave a clear text-safe area, and contain no letters.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.145 Owner approval: River Wend environment concepts open
- mw-e37.161 Owner approval: harbour cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:conceptepic:e37milestone:m3mw-e37.164Generate barge ride card art via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 2 prompt files ofe37-asset-barge-ride-cards-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-barge-ride-cards-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1920×1080 generate → KTX2 UASTC full-screen card, safe area for font-rendered narration text. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 2 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1920×1080 generate → KTX2 UASTC full-screen card, safe area for font-rendered narration text), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.163 Write barge ride card art prompts (2 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.165Owner approval: barge ride card art
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 2 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-barge-ride-cards-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: both cards read as the same barge and river as the approved river concepts, leave a clear text-safe area, and contain no letters. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then both cards read as the same barge and river as the approved river concepts, leave a clear text-safe area, and contain no letters.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-barge-ride-cards-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.164 Generate barge ride card art via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.166Integrate approved barge ride card art
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e25-barge-travel.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 2 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Briar Glen Quay, when the player pays Mags and rides down and back up, then the matching final card shows with narration text in the safe area and no console errors.
- AC-5 perf Given the ride cards, when
e32-perf-budgetsruns, then each card streams with the river package and resident texture memory stays ≤ 256 MB on Low. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e37.165 Owner approval: barge ride card art open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.167Write harbour and Saltmarch item icons prompts (22 assets)
Description
Why
The harbour adds heist evidence, smuggler tools, Saltmarch weapons and exotic arrows; imports must read as foreign in the inventory grid (style bible §5.5).
What
Write one prompt file per asset in
assets/prompts/icon-item/<asset-id>.md(category: icon-item) from templateitem-icon.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Heist and story items (Hornfolk name-stone, bay-nine manifest, Quell's honest manifest, foundry invoice, Mr. S—'s letter, customs-seal stamp, Lantern Court writ, dockhand disguise bundle, prize ribbon, barge fare token); Undertow tools (tide-silk rope, glue-soled boots, smoke pearl, skeleton-key ring); Saltmarch weapons (sabre, harpoon-spear, lacquered round shield); exotic arrows with distinct heads (whistling, netting, tar, glass-headed, grapple).
Asset ids (22):
icon-item-name-stone-hornfolk-01icon-item-manifest-bay-nine-01icon-item-manifest-honest-01icon-item-foundry-invoice-01icon-item-mr-s-letter-01icon-item-customs-seal-stamp-01icon-item-writ-lantern-court-01icon-item-dockhand-disguise-01icon-item-prize-ribbon-01icon-item-barge-fare-token-01icon-item-tide-silk-rope-01icon-item-glue-soled-boots-01icon-item-smoke-pearl-01icon-item-skeleton-key-ring-01icon-item-saltmarch-sabre-01icon-item-harpoon-spear-01icon-item-lacquered-round-shield-01icon-item-arrow-whistling-01icon-item-arrow-netting-01icon-item-arrow-tar-01icon-item-arrow-glass-01icon-item-arrow-grapple-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-icons-harbour-items-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Netting, tar and glass-headed arrows are added from style bible §5.5 (the needs list named only whistling and grapple).
icon-item-sharkskin-brigandine-01lives ine37-asset-icons-armour-equipment-*with the other armour. Foreign spellbook covers follow part A's book-cover pattern and are not in this set.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 22 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every icon is identifiable at 64 px, Saltmarch items share a lacquer or sea-glass accent, the five exotic arrows differ by head shape not colour alone, and documents show seals and marks but no legible text.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.112 Owner approval: kit item icons open
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:iconepic:e37milestone:m3mw-e37.168Generate harbour and Saltmarch item icons via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 22 prompt files ofe37-asset-icons-harbour-items-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-icons-harbour-items-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 22 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.167 Write harbour and Saltmarch item icons prompts (22 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:iconepic:e37milestone:m3mw-e37.169Owner approval: harbour and Saltmarch item icons
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 22 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-icons-harbour-items-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every icon is identifiable at 64 px, Saltmarch items share a lacquer or sea-glass accent, the five exotic arrows differ by head shape not colour alone, and documents show seals and marks but no legible text. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 22 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every icon is identifiable at 64 px, Saltmarch items share a lacquer or sea-glass accent, the five exotic arrows differ by head shape not colour alone, and documents show seals and marks but no legible text.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-icons-harbour-items-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.168 Generate harbour and Saltmarch item icons via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconepic:e37milestone:m3needs-humanmw-e37.170Integrate approved harbour and Saltmarch item icons
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e17-inventory-ui,e17-item-schema,e40-tidemarket,e40-undertow,e40-customs-house,e40-crate-heist,e25-barge-travel,e40-dockhand-disguises. Lower-priority consumers (e40-dockhand-disguises) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 22 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the inventory with every item in this set granted via the debug console, when opened, then all 22 icons render from the atlas with no placeholder flags.
- AC-5 perf Given the rebuilt icon atlas, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.2 Define item definition schema and content validator done
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e37.169 Owner approval: harbour and Saltmarch item icons open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
- mw-e40.20 Place The Crate for Mr. S— heist with all six solution routes open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
area:artasset:iconepic:e37milestone:m3mw-e37.171Write armour and equipment icons prompts (16 assets)
Description
Why
The head, body, hands and feet slots had no icons in either earlier pass; players compare armour by weight and noise, so the icons must show the material class at a glance.
What
Write one prompt file per asset in
assets/prompts/icon-item/<asset-id>.md(category: icon-item) from templateitem-icon.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Local barony and valley kit (squared, iron-and-oak, leather, plain): iron helm, leather cap, cloth hood; plate cuirass, chain hauberk, leather jerkin, cloth robe; plate gauntlets, leather gloves; plate sabatons, leather boots, soft-soled boots. Saltmarch imports (crescents, lacquer, sharkskin, knotted cord): crescent-crest lacquered helm, sharkskin brigandine, sharkskin gloves, cord-wrapped sea boots.
Asset ids (16):
icon-item-helm-iron-01icon-item-cap-leather-01icon-item-hood-cloth-01icon-item-cuirass-plate-01icon-item-hauberk-chain-01icon-item-jerkin-leather-01icon-item-robe-cloth-01icon-item-gauntlets-plate-01icon-item-gloves-leather-01icon-item-sabatons-plate-01icon-item-boots-leather-01icon-item-boots-soft-01icon-item-helm-saltmarch-lacquer-01icon-item-sharkskin-brigandine-01icon-item-gloves-sharkskin-01icon-item-boots-saltmarch-cord-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-icons-armour-equipment-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Gap noted by the part B pass: part A covered weapons, arrows and tools but no armour. Materials mirror the audio armour layers (plate, chain, leather, cloth) so icon, stats and sound agree. Worn armour appearance on the player models is out of scope (class models ship with their outfit).
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 16 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each icon is identifiable at 64 px, plate/chain/leather/cloth read apart by shape and surface (knight plate reads heaviest), and Saltmarch pieces read foreign at a glance (curves, lacquer, sharkskin) against the squared local kit.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.112 Owner approval: kit item icons open
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:iconepic:e37milestone:m3mw-e37.172Generate armour and equipment icons via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 16 prompt files ofe37-asset-icons-armour-equipment-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-icons-armour-equipment-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 16 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.171 Write armour and equipment icons prompts (16 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:iconepic:e37milestone:m3mw-e37.173Owner approval: armour and equipment icons
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 16 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-icons-armour-equipment-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each icon is identifiable at 64 px, plate/chain/leather/cloth read apart by shape and surface (knight plate reads heaviest), and Saltmarch pieces read foreign at a glance (curves, lacquer, sharkskin) against the squared local kit. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 16 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each icon is identifiable at 64 px, plate/chain/leather/cloth read apart by shape and surface (knight plate reads heaviest), and Saltmarch pieces read foreign at a glance (curves, lacquer, sharkskin) against the squared local kit.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-icons-armour-equipment-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.172 Generate armour and equipment icons via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconepic:e37milestone:m3needs-humanmw-e37.174Integrate approved armour and equipment icons
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e17-equipment-ui,e17-inventory-ui,e17-equipment-slots,e04-armor-weight,e40-tidemarket.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 16 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the equipment paper-doll with every armour piece granted via the debug console, when each slot is filled, then the final icon shows in the slot and in the Tidemarket stock list with no placeholder flags.
- AC-5 perf Given the rebuilt icon atlas, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e04.16 Armor weight sets roll class, stamina regen, poise and noise done
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.11 Add equipment paper-doll view with verb and stealth comparison open
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
- mw-e37.173 Owner approval: armour and equipment icons open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
area:artasset:iconepic:e37milestone:m3mw-e37.175Write Patient Ann barge models concept prompts (1 images)
Description
Why
Mags Oakum's *Patient Ann* is seen at the quay, on the ride cards and (as a winched twin) in the Weeping Adit; she is the river's signature object.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Flat-bottomed, low, tarred stone barge with dressed glenstone blocks lashed on timber cradles, stern tiller, small cabin, tow-rope point and ochre hull stripe (no painted name) (style bible §5.6); plus the winched Adit stone barge variant (empty cradles, rusted, chain-hauled, half-flooded).
Concept image ids (1):
prop-barge-patient-ann-01
Target model ids (2):
model-barge-patient-ann-01model-barge-stone-adit-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-barge-patient-ann-3d-generate)
Notes
Budget: hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 1 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: *Patient Ann* is identifiable by her ochre stripe and silhouette from the quay steps, the Adit barge reads as the same class of boat abandoned, and no ship name is painted.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.145 Owner approval: River Wend environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.176Generate Patient Ann barge models concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 1 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-barge-patient-ann-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 1 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.175 Write Patient Ann barge models concept prompts (1 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.177Owner approval: Patient Ann barge models flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 1 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-barge-patient-ann-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: *Patient Ann* is identifiable by her ochre stripe and silhouette from the quay steps, the Adit barge reads as the same class of boat abandoned, and no ship name is painted. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 1 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then *Patient Ann* is identifiable by her ochre stripe and silhouette from the quay steps, the Adit barge reads as the same class of boat abandoned, and no ship name is painted.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-barge-patient-ann-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.176 Generate Patient Ann barge models concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.178Generate Patient Ann barge models 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 2 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-barge-patient-ann-turntable-approve)
Notes
Budget: hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 2 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.177 Owner approval: Patient Ann barge models flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.179Owner approval: Patient Ann barge models turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 2 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-barge-patient-ann-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: *Patient Ann* is identifiable by her ochre stripe and silhouette from the quay steps, the Adit barge reads as the same class of boat abandoned, and no ship name is painted. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then *Patient Ann* is identifiable by her ochre stripe and silhouette from the quay steps, the Adit barge reads as the same class of boat abandoned, and no ship name is painted.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-barge-patient-ann-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.178 Generate Patient Ann barge models 3D models via image-to-3D API open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.180Integrate approved Patient Ann barge models
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e25-barge-travel,e25-weeping-adit,e25-quay-stone-stair.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 2 models are underpublic/assets/model/within hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the quay and the Weeping Adit, when Playwright captures both barges, then the final models load with the correct colliders (standable deck, winch attach point) and no console errors.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.179 Owner approval: Patient Ann barge models turntables open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.181Write Gilded Tern ship models concept prompts (2 images)
Description
Why
The Gilded Tern is a climbable, boardable heist location and leaves in Act III; it is the largest hero model in the game.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Three-masted merchantman built from modular pieces: exterior hull, masts and climbable rigging (ratlines and shrouds per style bible §5.4) with sails furled and set states; interior hold, captain's cabin and locked passenger berth sections; gilded tern figurehead.
Concept image ids (2):
prop-ship-gilded-tern-01prop-ship-gilded-tern-interior-01
Target model ids (4):
model-ship-gilded-tern-hull-01model-ship-gilded-tern-rigging-01model-ship-gilded-tern-interior-01model-prop-tern-figurehead-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-ship-gilded-tern-3d-generate)
Notes
The "sailed" absence in Act III is a world-variant state of the pool (
e27-world-variantsviae40-gilded-tern), not a model; sails furled/set are material/mesh states of the rigging module.Budget: hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: the ship reads as a proud merchantman from the wharf, rigging handholds show the climbable cue, the berth door reads locked, and interior sections match the exterior scale.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.182Generate Gilded Tern ship models concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-ship-gilded-tern-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.181 Write Gilded Tern ship models concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.183Owner approval: Gilded Tern ship models flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ship-gilded-tern-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the ship reads as a proud merchantman from the wharf, rigging handholds show the climbable cue, the berth door reads locked, and interior sections match the exterior scale. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the ship reads as a proud merchantman from the wharf, rigging handholds show the climbable cue, the berth door reads locked, and interior sections match the exterior scale.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ship-gilded-tern-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.182 Generate Gilded Tern ship models concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.184Generate Gilded Tern ship models 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 4 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-ship-gilded-tern-turntable-approve)
Notes
Budget: hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 4 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.183 Owner approval: Gilded Tern ship models flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.185Owner approval: Gilded Tern ship models turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 4 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ship-gilded-tern-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the ship reads as a proud merchantman from the wharf, rigging handholds show the climbable cue, the berth door reads locked, and interior sections match the exterior scale. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the ship reads as a proud merchantman from the wharf, rigging handholds show the climbable cue, the berth door reads locked, and interior sections match the exterior scale.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ship-gilded-tern-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.184 Generate Gilded Tern ship models 3D models via image-to-3D API open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.186Integrate approved Gilded Tern ship models
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e40-gilded-tern.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 4 models are underpublic/assets/model/within hero props 2–5 k tris each (modular ship pieces ≤ 5 k per module, LOD1 50 %), T2 1024² atlas, ≤ 1 material per model (style bible §13.2, §5.6) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the harbour pool, when the player climbs the rigging, enters the hold, cabin and berth, and the Act III fact is set, then the final modules load and the ship is absent after sailing, with no console errors.
- AC-4 perf Given the Wendmouth waterfront with the Tern in view, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.185 Owner approval: Gilded Tern ship models turntables open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
area:artasset:modelepic:e37milestone:m3mw-e37.187Write Saltmarch weapon and arrow concept prompts (2 images)
Description
Why
Tidemarket gear is plainly better and foreign; bought weapons and arrows must appear in hand and in flight, not just as icons.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Curved Saltmarch sabre ("curved like a gull's wing"), harpoon-spear, lacquered round shield; exotic arrow heads/projectiles: whistling (pierced bone), netting (weighted pod), tar (black-dipped), glass-headed (sea-glass), grapple (hooked iron).
Concept image ids (2):
prop-weapon-saltmarch-set-01prop-weapon-arrow-exotic-set-01
Target model ids (8):
model-prop-saltmarch-sabre-01model-prop-harpoon-spear-01model-prop-lacquered-round-shield-01model-prop-arrow-whistling-01model-prop-arrow-netting-01model-prop-arrow-tar-01model-prop-arrow-glass-01model-prop-arrow-grapple-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-saltmarch-weapons-3d-generate)
Notes
Complements part A
e37-asset-weapons-*(local kit). Tereza wields the sabre model in her duel (e40-tereza-duel, P2).Budget: held weapon 300–1.5 k tris, arrows ≤ 300, character or prop atlas, shared material (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (held weapon 300–1.5 k tris, arrows ≤ 300, character or prop atlas, shared material (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: Saltmarch weapons read foreign against part A's local weapons (curves, lacquer red #8E2B2B, sharkskin grips), and each arrow is distinguishable in flight by head shape.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.38 Owner approval: player weapons flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.188Generate Saltmarch weapon and arrow concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-saltmarch-weapons-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.187 Write Saltmarch weapon and arrow concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.189Owner approval: Saltmarch weapon and arrow flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-saltmarch-weapons-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Saltmarch weapons read foreign against part A's local weapons (curves, lacquer red #8E2B2B, sharkskin grips), and each arrow is distinguishable in flight by head shape. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Saltmarch weapons read foreign against part A's local weapons (curves, lacquer red #8E2B2B, sharkskin grips), and each arrow is distinguishable in flight by head shape.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-saltmarch-weapons-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.188 Generate Saltmarch weapon and arrow concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.190Generate Saltmarch weapon and arrow 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 8 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-saltmarch-weapons-turntable-approve)
Notes
Budget: held weapon 300–1.5 k tris, arrows ≤ 300, character or prop atlas, shared material (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 8 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets held weapon 300–1.5 k tris, arrows ≤ 300, character or prop atlas, shared material (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.189 Owner approval: Saltmarch weapon and arrow flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.191Owner approval: Saltmarch weapon and arrow turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 8 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-saltmarch-weapons-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Saltmarch weapons read foreign against part A's local weapons (curves, lacquer red #8E2B2B, sharkskin grips), and each arrow is distinguishable in flight by head shape. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Saltmarch weapons read foreign against part A's local weapons (curves, lacquer red #8E2B2B, sharkskin grips), and each arrow is distinguishable in flight by head shape.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-saltmarch-weapons-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.190 Generate Saltmarch weapon and arrow 3D models via image-to-3D API open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.192Integrate approved Saltmarch weapon and arrow
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e40-tidemarket,e05-arrow-schema,e17-equipment-slots,e40-tereza-duel. Lower-priority consumers (e40-tereza-duel) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 8 models are underpublic/assets/model/within held weapon 300–1.5 k tris, arrows ≤ 300, character or prop atlas, shared material (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the Tidemarket, when each weapon is bought and equipped and each arrow fired via the debug console, then final models attach to the correct sockets and fly with their trails, with no console errors.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e05.1 Data schema for arrow types and property-based payloads done
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.191 Owner approval: Saltmarch weapon and arrow turntables open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
area:artasset:modelepic:e37milestone:m3mw-e37.193Select CC0 kit pieces for river quay, weir and lock props (14 models)
Description
Why
The quay, weir, lock and adit are mechanism-heavy spaces; kit pieces keep them consistent and cheap while the puzzles read through legibility cues.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Bollards, dressed-block stacks on timber cradles, quay crane, toll shed, Stone Stair slipway segments; weir sluice gates, lock gates, paddles and windlass, beam walk, toll board (no legible text), lock cottage interior; adit drainage timbering, adit winch, air-pocket rock shelves.
Kit brief ids (3):
prop-river-quay-set-01prop-river-weir-lock-set-01prop-adit-set-01
Target model ids (14):
model-prop-bollard-01model-prop-stone-block-stack-01model-prop-quay-crane-01model-prop-toll-shed-01model-prop-stone-stair-slipway-01model-prop-weir-sluice-gate-01model-prop-lock-gate-01model-prop-lock-windlass-01model-prop-lock-beam-walk-01model-prop-toll-board-01model-prop-lock-cottage-set-01model-prop-adit-timbering-01model-prop-adit-winch-01model-prop-adit-rock-shelf-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-river-kit-kit-recolour)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 3 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 14 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.145 Owner approval: River Wend environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.194Recolour river quay, weir and lock props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-river-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 14 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.193 Select CC0 kit pieces for river quay, weir and lock props (14 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.195Owner approval: river quay, weir and lock props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 14 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-river-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: windlasses, paddles and gates read as operable mechanisms, the chute reads climbable/slideable, and the set matches the approved river concepts. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then windlasses, paddles and gates read as operable mechanisms, the chute reads climbable/slideable, and the set matches the approved river concepts.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-river-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.194 Recolour river quay, weir and lock props from CC0 kits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.196Integrate approved river quay, weir and lock props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e25-quay-stone-stair,e25-millweir-sluice-walk,e25-towpath-kestrel-lock,e25-weeping-adit. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-river-art-audio.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 14 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the quay, weir, lock and adit, when the lock is cycled and the sluice opened via the debug console, then the final mechanism models animate on their pivots and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e25.12 Build the Millweir and its sluice walk to the Briarwood far bank open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.195 Owner approval: river quay, weir and lock props kit turntables open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.197Select CC0 kit pieces for waterfront and Tidefair props (16 models)
Description
Why
The wharf, rooftops, Undertow and Tidefair are the systemic harbour: cranes swing, nets are cut, lofts are climbed, booms are shot.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Treadwheel wharf cranes on stone piers (jib + winch), cargo nets (cuttable load line with the red-white band), crate stacks, block-yard stacks of dressed glenstone, Tidefair markers, Lantern Court notice; net lofts, plank bridges, drying nets (loose), gull hoard nests; smuggler crates, lantern hooks, fence counter; Tidefair swinging boom with wooden gulls, prize ribbons, bunting, rowing boats/lighters with pennants.
Kit brief ids (4):
prop-harbour-wharf-set-01prop-harbour-rooftop-set-01prop-harbour-undertow-set-01prop-tidefair-set-01
Target model ids (16):
model-prop-wharf-crane-01model-prop-cargo-net-01model-prop-crate-stack-01model-prop-tidefair-marker-01model-prop-notice-lantern-court-01model-prop-net-loft-01model-prop-plank-bridge-01model-prop-drying-net-01model-prop-gull-hoard-nest-01model-prop-smuggler-crate-01model-prop-lantern-hook-01model-prop-fence-counter-01model-prop-tidefair-boom-01model-prop-wooden-gull-target-01model-prop-bunting-01model-prop-rowing-boat-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-harbour-waterfront-kit-kit-recolour)
Notes
Block-yard stacks reuse
model-prop-stone-block-stack-01frome37-asset-props-river-kit-*. Rowing boats double as harbour lighters and as regatta boats (e40-tidefair-regatta, P2).Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 4 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 16 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.198Recolour waterfront and Tidefair props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-harbour-waterfront-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 16 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.197 Select CC0 kit pieces for waterfront and Tidefair props (16 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.199Owner approval: waterfront and Tidefair props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 16 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-harbour-waterfront-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: cuttable cargo lines carry the mechanism-for-arrows band, taut climbable nets read distinct from loose drying nets, the Tidefair boom reads as a target, and colours follow the Wendmouth palette. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 16 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then cuttable cargo lines carry the mechanism-for-arrows band, taut climbable nets read distinct from loose drying nets, the Tidefair boom reads as a target, and colours follow the Wendmouth palette.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-harbour-waterfront-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.198 Recolour waterfront and Tidefair props from CC0 kits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.200Integrate approved waterfront and Tidefair props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e40-stone-wharf,e40-rooftops-net-lofts,e40-undertow,e40-tidefair-shoot-pockets,e40-tidefair-strength,e40-tidefair-regatta,e40-harbor-ambient-life. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-art-dress. Lower-priority consumers (e40-tidefair-regatta,e40-harbor-ambient-life) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 16 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the Stone Wharf, when a crane is swung, a cargo net cut by an arrow and the Gull Shoot boom hit, then the final models respond and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.199 Owner approval: waterfront and Tidefair props kit turntables open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
- mw-e40.13 Build a continuous rooftop and net-loft route across the waterfront open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
- mw-e40.8 Stage the Tidefair Gull Shoot and the Pocket Race open
area:artasset:modelepic:e37milestone:m3mw-e37.201Select CC0 kit pieces for harbour interior props (14 models)
Description
Why
The Customs House, Bonded Warehouse, Drowned Gull and Tidemarket are four busy interiors that need consistent furniture without bespoke modelling.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Customs desks and strongroom shelves; warehouse hoist beams and manifest desk, Quell's office furniture; the Gull's arm-wrestling table, dice table, tankards and trapdoor; Tidemarket stalls, spice sacks, silk bolts, foreign book cart and weapon racks. Story pieces (bay-door customs seal, relic crate, cargo scale, strongroom door, Cray's shelf, sea-door) come from
e37-asset-props-harbour-story-*.Kit brief ids (4):
prop-harbour-customs-set-01prop-harbour-warehouse-set-01prop-harbour-tavern-set-01prop-harbour-market-set-01
Target model ids (14):
model-prop-customs-desk-01model-prop-strongroom-shelf-01model-prop-hoist-beam-01model-prop-manifest-desk-01model-prop-office-quell-set-01model-prop-arm-wrestle-table-01model-prop-dice-table-01model-prop-tankard-01model-prop-trapdoor-gull-01model-prop-market-stall-harbour-01model-prop-spice-sacks-01model-prop-silk-bolts-01model-prop-book-cart-foreign-01model-prop-weapon-rack-saltmarch-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-harbour-interiors-kit-kit-recolour)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 4 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 14 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.202Recolour harbour interior props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-harbour-interiors-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 14 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.201 Select CC0 kit pieces for harbour interior props (14 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.203Owner approval: harbour interior props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 14 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-harbour-interiors-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each interior reads as its owner (Cray's order, Quell's nerves, Nell's rowdy tavern, Tereza's elegant stall), the trapdoor reads as a hidden exit only on inspection, and Saltmarch goods read foreign. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each interior reads as its owner (Cray's order, Quell's nerves, Nell's rowdy tavern, Tereza's elegant stall), the trapdoor reads as a hidden exit only on inspection, and Saltmarch goods read foreign.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-harbour-interiors-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.202 Recolour harbour interior props from CC0 kits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.204Integrate approved harbour interior props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e40-customs-house,e40-bonded-warehouse,e40-drowned-gull,e40-tidemarket. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-art-dress.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 14 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given each harbour interior, when Playwright loads it, then final props render with no placeholder models, the Gull trapdoor opens, and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.203 Owner approval: harbour interior props kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
area:artasset:modelepic:e37milestone:m3mw-e37.205Write harbour heist and story props concept prompts (2 images)
Description
Why
The Crate for Mr. S— heist turns on a few specific objects — bay nine's customs seal, the relic crate, the strongroom, the sea-door — and the sea-serpent rumour on the sunken figurehead.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Heist sheet: bay door with the customs-seal entity in intact/broken/re-sealed states, the relic crate and its ballast twin, cargo scale, strongroom door (black-steel lock), Cray's well-read romance shelf, the barnacled iron-bound sea-door; figurehead sheet: the sunken serpent-headed figurehead with two glass "lantern eyes".
Concept image ids (2):
prop-harbour-story-set-01prop-breakwater-figurehead-01
Target model ids (8):
model-prop-bay-door-seal-01model-prop-relic-crate-01model-prop-ballast-crate-01model-prop-cargo-scale-01model-prop-strongroom-door-01model-prop-shelf-cray-01model-prop-sea-door-01model-prop-figurehead-serpent-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-props-harbour-story-3d-generate)
Notes
prop-harbour-story-set-01is a new concept id grouping heist pieces from the warehouse, customs and undertow sets of the needs list.Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: the three seal states read apart at 3 m, the relic crate and its ballast twin are identical outside but distinguishable when opened, the strongroom lock reads hard, and the figurehead reads as a monster from above water and a carving up close.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.206Generate harbour heist and story props concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-props-harbour-story-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.205 Write harbour heist and story props concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.207Owner approval: harbour heist and story props flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-harbour-story-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the three seal states read apart at 3 m, the relic crate and its ballast twin are identical outside but distinguishable when opened, the strongroom lock reads hard, and the figurehead reads as a monster from above water and a carving up close. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the three seal states read apart at 3 m, the relic crate and its ballast twin are identical outside but distinguishable when opened, the strongroom lock reads hard, and the figurehead reads as a monster from above water and a carving up close.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-harbour-story-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.206 Generate harbour heist and story props concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.208Generate harbour heist and story props 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 8 target models: first try a CC0 kit match recoloured to the palette atlas; otherwise call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-props-harbour-story-turntable-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 8 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.207 Owner approval: harbour heist and story props flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.209Owner approval: harbour heist and story props turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 8 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-harbour-story-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the three seal states read apart at 3 m, the relic crate and its ballast twin are identical outside but distinguishable when opened, the strongroom lock reads hard, and the figurehead reads as a monster from above water and a carving up close. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the three seal states read apart at 3 m, the relic crate and its ballast twin are identical outside but distinguishable when opened, the strongroom lock reads hard, and the figurehead reads as a monster from above water and a carving up close.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-harbour-story-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.208 Generate harbour heist and story props 3D models via image-to-3D API open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.210Integrate approved harbour heist and story props
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e40-bonded-warehouse,e40-crate-heist,e40-customs-house,e40-breakwater-sea-door. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-art-dress.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 8 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the Bonded Warehouse and the breakwater, when a seal is broken and re-stamped, the crate swapped for ballast and the sea-door opened at low tide, then the final models swap states and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.209 Owner approval: harbour heist and story props turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.15 Build the breakwater, sunken figurehead, sea-door and harbour tides open
- mw-e40.20 Place The Crate for Mr. S— heist with all six solution routes open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
area:artasset:modelepic:e37milestone:m3mw-e37.211Write named harbour NPC characters concept prompts (8 images)
Description
Why
The harbour cast are bargained with, arm-wrestled, dueled and robbed; they must match their portraits and silhouette hooks in the world.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: character) fromcharacter-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Turnaround sheets matching the approved portraits for Mags Oakum, Nell Gannet, Inspector Cray, Tereza Maelo (with sabre socket), Jasper Quell, Big Oona, Old Reed and Captain Hesketh. The veiled agent is never seen in the MVP.
Concept image ids (8):
char-npc-mags-01char-npc-nell-01char-npc-cray-01char-npc-tereza-01char-npc-jasper-01char-npc-oona-01char-npc-reed-01char-npc-hesketh-01
Target model ids (8):
model-npc-mags-01model-npc-nell-01model-npc-cray-01model-npc-tereza-01model-npc-jasper-01model-npc-oona-01model-npc-reed-01model-npc-hesketh-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-npcs-harbour-named-3d-generate)
Notes
Needs-list ids
char-npc-oona-01,char-npc-reed-01,char-npc-hesketh-01are added so every modelled NPC has an approved flat concept (image-to-3D input).Budget: key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 8 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: each NPC is recognisable against their portrait at 10 m and follows the style bible §5.4 silhouette hooks (Mags's pipe, Nell's size, Cray's starch, Tereza's Saltmarch cut, Quell's stoop).
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.161 Owner approval: harbour cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.212Generate named harbour NPC characters concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 8 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-npcs-harbour-named-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 8 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.211 Write named harbour NPC characters concept prompts (8 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.213Owner approval: named harbour NPC characters flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 8 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-npcs-harbour-named-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each NPC is recognisable against their portrait at 10 m and follows the style bible §5.4 silhouette hooks (Mags's pipe, Nell's size, Cray's starch, Tereza's Saltmarch cut, Quell's stoop). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each NPC is recognisable against their portrait at 10 m and follows the style bible §5.4 silhouette hooks (Mags's pipe, Nell's size, Cray's starch, Tereza's Saltmarch cut, Quell's stoop).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-npcs-harbour-named-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.212 Generate named harbour NPC characters concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.214Generate named harbour NPC characters 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 8 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig on the canonical humanoid skeleton viae37-animation-pipelineso shared clips retarget, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-npcs-harbour-named-turntable-approve)
Notes
Budget: key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 8 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.213 Owner approval: named harbour NPC characters flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.215Owner approval: named harbour NPC characters turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 8 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-npcs-harbour-named-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each NPC is recognisable against their portrait at 10 m and follows the style bible §5.4 silhouette hooks (Mags's pipe, Nell's size, Cray's starch, Tereza's Saltmarch cut, Quell's stoop). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each NPC is recognisable against their portrait at 10 m and follows the style bible §5.4 silhouette hooks (Mags's pipe, Nell's size, Cray's starch, Tereza's Saltmarch cut, Quell's stoop).
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-npcs-harbour-named-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.214 Generate named harbour NPC characters 3D models via image-to-3D API open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.216Integrate approved named harbour NPC characters
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e25-barge-travel,e25-towpath-kestrel-lock,e40-drowned-gull,e40-customs-house,e40-tidemarket,e40-bonded-warehouse,e40-tidefair-strength,e40-gilded-tern.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 8 models are underpublic/assets/model/within key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given Wendmouth and the river, when each named NPC is spawned via the debug console, then the final model plays idle and talk clips with no console errors.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.215 Owner approval: named harbour NPC characters turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
area:artasset:modelepic:e37milestone:m3mw-e37.217Select CC0 kit pieces for harbour crowd and faction models (5 models)
Description
Why
Dense crowds give the thief cover and pockets, and the three opposition groups must read apart at 30 m by hat and coat shape (style bible §5.4 disguise table).
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/character/<brief-id>.mdfromcharacter-reference-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Harbour crowd (10–14 variants: dockhands, fishwives, sailors, merchants; pickpocketable ones show a purse on a cord), harbour watch (long coat, round brimmed hat, lantern-pole or cudgel, navy), Low Tide Company smugglers (mixed dockside clothes, sea-green neckerchief tell), Gilded Tern crew (short jacket, tarred pigtail, bare feet), and the dockhand disguise outfit the player can wear.
Kit brief ids (1):
char-npc-harbour-crowd-set-01
Target model ids (5):
model-npc-harbour-crowd-set-01model-npc-harbour-watch-set-01model-npc-low-tide-smuggler-set-01model-npc-tern-crew-set-01model-char-disguise-dockhand-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-npc-harbour-crowd-kit-kit-recolour)
Notes
Built on the same CC0 modular character kit as
e37-asset-npc-crowd-kit-*. The disguise outfit is used bye40-dockhand-disguises(P2); it is cheap to include here because it is a dockhand variant.Budget: background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 1 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 5 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.161 Owner approval: harbour cast portraits open
- mw-e37.361 Owner approval: crowd NPC models kit turntables open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.218Recolour harbour crowd and faction models from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-npc-harbour-crowd-kit-kit-approve)
Notes
Budget: background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 5 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.217 Select CC0 kit pieces for harbour crowd and faction models (5 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.219Owner approval: harbour crowd and faction models kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 5 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-npc-harbour-crowd-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the four groups read apart by silhouette at 30 m (colour second), pickpocketable crowd members show the purse cue and filler does not, and the disguise keeps one small class tell. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 5 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the four groups read apart by silhouette at 30 m (colour second), pickpocketable crowd members show the purse cue and filler does not, and the disguise keeps one small class tell.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-npc-harbour-crowd-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.218 Recolour harbour crowd and faction models from CC0 kits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.220Integrate approved harbour crowd and faction models
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e40-harbor-crowds,e40-harbor-opposition,e40-dockhand-disguises. Lower-priority consumers (e40-dockhand-disguises) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 5 models are underpublic/assets/model/within background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the Stone Wharf on market day, when Playwright captures the crowd and a watch patrol, then final crowd and faction models render with purse cues where pickpocketable and no console errors.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.219 Owner approval: harbour crowd and faction models kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.16 Fill the harbour with pickpocketable crowds that give stealth cover open
- mw-e40.18 Add the harbour watch, Low Tide Company and Tern crew as humans open
area:artasset:modelepic:e37milestone:m3mw-e37.227Write harbour NPC and contest animations motion briefs (14 clips)
Description
Why
Arm-wrestling, brawls and working dockhands are how Wendmouth reads as loud, cheerful and physical.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Arm-wrestle loop, win and lose (paired), brawl punches and a knockout, rowing, crane operating, net hauling, drinking, dice throw, deck swabbing, rigging-climb idle, barge poling (Mags).
Clip ids (14):
anim-humanoid-arm-wrestle-loop-01anim-humanoid-arm-wrestle-win-01anim-humanoid-arm-wrestle-lose-01anim-humanoid-brawl-punch-01anim-humanoid-brawl-punch-02anim-humanoid-brawl-knockout-01anim-humanoid-rowing-01anim-humanoid-crane-operate-01anim-humanoid-net-haul-01anim-humanoid-drink-01anim-humanoid-dice-throw-01anim-humanoid-deck-swab-01anim-humanoid-rigging-climb-idle-01anim-humanoid-barge-pole-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Player-side climbing on rigging uses part A's
anim-humanoid-climb-01; the rigging idle here is for Tern crew.Rig: canonical humanoid skeleton (harbour NPCs and crowd kit share it); paired clips (arm-wrestle, brawl) share a sync marker. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 14 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.228Source and retarget harbour NPC and contest animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-harbour-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 14 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.215 Owner approval: named harbour NPC characters turntables open
- mw-e37.219 Owner approval: harbour crowd and faction models kit turntables open
- mw-e37.227 Write harbour NPC and contest animations motion briefs (14 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.229Owner approval: harbour NPC and contest animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 14 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-harbour-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: paired arm-wrestle clips stay in contact without interpenetration, brawl knockouts read comic and non-lethal, and work idles read as their trade at 15 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then paired arm-wrestle clips stay in contact without interpenetration, brawl knockouts read comic and non-lethal, and work idles read as their trade at 15 m.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-harbour-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.228 Source and retarget harbour NPC and contest animations open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.230Integrate approved harbour NPC and contest animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e40-drowned-gull,e40-tidefair-strength,e40-harbor-crowds,e40-stone-wharf,e25-barge-travel,e40-tidefair-regatta. Lower-priority consumers (e40-tidefair-regatta) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 14 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the Drowned Gull and the Stone Wharf, when an arm-wrestling bout, a brawl and dock work run via the debug console, then final clips play in sync with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e37.216 Integrate approved named harbour NPC characters open
- mw-e37.220 Integrate approved harbour crowd and faction models open
- mw-e37.229 Owner approval: harbour NPC and contest animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.16 Fill the harbour with pickpocketable crowds that give stealth cover open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
area:artasset:modelepic:e37milestone:m3mw-e37.239Write river and harbour VFX prompts (6 assets)
Description
Why
Big splashes, the Adit current, the lock churn, the lighthouse sweep and a frozen harbour pool sell water that is standing water in the sim.
What
Write one prompt file per asset in
assets/prompts/vfx/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Large water splash (net drop, thrown off the pier), Adit current foam, lock fill churn, lighthouse sweep beam, regatta sail gust, harbour-pool freeze sheet.
Asset ids (6):
vfx-water-splash-large-01vfx-adit-current-foam-01vfx-lock-fill-churn-01vfx-lighthouse-sweep-01vfx-regatta-sail-gust-01vfx-harbour-pool-freeze-sheet-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-river-harbour-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
The lighthouse sweep is consumed by
e40-harbor-lighting-audio(edge).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 6 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (512² or 1024² flipbook sheets (4×4 or 8×8), alpha or additive on black, loopable where marked) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: water effects read in the river/harbour palettes, the freeze sheet matches part A's frost language, and the lighthouse sweep never strobes faster than 3 Hz.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxepic:e37milestone:m3mw-e37.240Generate river and harbour VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 6 prompt files ofe37-asset-vfx-river-harbour-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-river-harbour-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 512² or 1024² flipbook sheets (4×4 or 8×8), alpha or additive on black, loopable where marked. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 6 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (512² or 1024² flipbook sheets (4×4 or 8×8), alpha or additive on black, loopable where marked), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.239 Write river and harbour VFX prompts (6 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:vfxepic:e37milestone:m3mw-e37.241Owner approval: river and harbour VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 6 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-river-harbour-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: water effects read in the river/harbour palettes, the freeze sheet matches part A's frost language, and the lighthouse sweep never strobes faster than 3 Hz. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 6 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then water effects read in the river/harbour palettes, the freeze sheet matches part A's frost language, and the lighthouse sweep never strobes faster than 3 Hz.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-river-harbour-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.240 Generate river and harbour VFX via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:vfxepic:e37milestone:m3needs-humanmw-e37.242Integrate approved river and harbour VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e29-vfx-framework,e40-stone-wharf,e25-weeping-adit,e25-towpath-kestrel-lock,e40-gilded-tern,e40-tidefair-regatta. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-lighting-audio. Lower-priority consumers (e40-tidefair-regatta) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 6 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the wharf, lock, Adit and harbour pool, when each effect is triggered via the debug console, then final textures render and VFX GPU time stays within its 2 ms share.
- AC-5 perf Given the harbour at night with the lighthouse and splashes active, when
e32-perf-budgetsruns, then live particles stay ≤ 800 and full-screen transparent layers ≤ 2 on Low. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.241 Owner approval: river and harbour VFX open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
area:artasset:vfxepic:e37milestone:m3mw-e37.243Write Briar Glen environment concepts prompts (11 assets)
Description
Why
Briar Glen is the first playable area and the "one warm room, one uneasy thing" tone target (style bible §2.2); every town texture, prop and light is judged against these paintings.
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Eye-level third-person concept paintings for the Market Green (notice board, the Sleeping Ox, the Lamplit Stacks leaning on Brand's Forge, Vane House on the rise), the town at night with Tansy sleepwalking, and the key interiors: inn common room, three-storey bookshop, forge (bell mould on the wall), Fletchery & Simples, the bakery at 4 a.m., Watch House jail with loose drain stone, Vane House exterior and study, and the Glen Well shaft down to the flooded Vesperine cistern.
Asset ids (11):
env-briar-glen-market-green-01env-briar-glen-night-01env-sleeping-ox-common-room-01env-lamplit-stacks-interior-01env-brand-forge-interior-01env-fenn-fletchery-interior-01env-pell-bakery-interior-01env-watch-house-jail-01env-vane-house-exterior-01env-vane-house-study-01env-glen-well-cistern-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-town-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Also consumed as mood targets by
e24-first-night,e24-sleeping-ox,e24-lamplit-stacks,e24-trade-row,e24-watch-house-jail,e24-vane-house,e24-glen-well-cistern. The night concept shows Tansy as a sleepwalker (nightclothes, barefoot, eyes open, serene — style bible §5.2). These concepts become reference inputs for the town texture, prop and sky sets.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 11 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each painting reads as Briar Glen golden-hour/lantern palette (thatch #C9A66B, plaster #EADFC8, lantern #FFB85C), shows its gameplay affordances (hiding spots, climbable routes, the loose drain stone, the well rung) and carries one uneasy detail without horror.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.244Generate Briar Glen environment concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 11 prompt files ofe37-asset-env-town-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-town-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 11 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.243 Write Briar Glen environment concepts prompts (11 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.245Owner approval: Briar Glen environment concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 11 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-town-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each painting reads as Briar Glen golden-hour/lantern palette (thatch #C9A66B, plaster #EADFC8, lantern #FFB85C), shows its gameplay affordances (hiding spots, climbable routes, the loose drain stone, the well rung) and carries one uneasy detail without horror. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 11 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each painting reads as Briar Glen golden-hour/lantern palette (thatch #C9A66B, plaster #EADFC8, lantern #FFB85C), shows its gameplay affordances (hiding spots, climbable routes, the loose drain stone, the well rung) and carries one uneasy detail without horror.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-town-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.244 Generate Briar Glen environment concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.246Integrate approved Briar Glen environment concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e24-town-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-art-dress.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for Briar Glen, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.247Write Briarwood and Mooring's Watch concepts prompts (8 assets)
Description
Why
The wilderness is where the song's wrongness first shows in nature — silent wolves, trampled ferns, a menhir covering its ears — and it must feel cooler and bluer the deeper you go (style bible §2.2).
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Briarwood trail (oak, holly, bramble, dappled light), the Sleepers' Path under moonlight, the Standing Horn clearing (menhir with hands over its ears), the wolf den hollow with overlooks and bramble cover, the millpond/sluice/drowned lane with the woodcutter's hut, Mooring's Watch exterior (goblin lookout below, signal mirror on the roof — no beacon brazier), the armour hall with names chalked on the stands, and the boarded Deepworks gate with the Bell Line carving (shape only), winch pulley and vent chimney.
Asset ids (8):
env-briarwood-trail-01env-sleepers-path-night-01env-standing-horn-clearing-01env-wolf-den-hollow-01env-millpond-drowned-lane-01env-moorings-watch-exterior-01env-moorings-watch-armour-hall-01env-deepworks-gate-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-wilds-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Also consumed as mood targets by
e25-sleepers-path,e25-wolf-den,e25-millpond-drowned-lane,e25-mooring-watch-encounters,e25-mine-gate-approachand (P2)e25-standing-horn.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 8 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: forest paintings sit in the Briarwood palette (canopy #2E5E3E, fern #5E8C3A, shafts #F3E6A8), Mooring's Watch reads windy and high-contrast with the signal mirror as the only flash, and every class route (climb, sneak, swim, break) is visible in at least one painting.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.248Generate Briarwood and Mooring's Watch concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 8 prompt files ofe37-asset-env-wilds-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-wilds-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.247 Write Briarwood and Mooring's Watch concepts prompts (8 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.249Owner approval: Briarwood and Mooring's Watch concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 8 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-wilds-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: forest paintings sit in the Briarwood palette (canopy #2E5E3E, fern #5E8C3A, shafts #F3E6A8), Mooring's Watch reads windy and high-contrast with the signal mirror as the only flash, and every class route (climb, sneak, swim, break) is visible in at least one painting. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then forest paintings sit in the Briarwood palette (canopy #2E5E3E, fern #5E8C3A, shafts #F3E6A8), Mooring's Watch reads windy and high-contrast with the signal mirror as the only flash, and every class route (climb, sneak, swim, break) is visible in at least one painting.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-wilds-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.248 Generate Briarwood and Mooring's Watch concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.250Integrate approved Briarwood and Mooring's Watch concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e25-briarwood-greybox,e25-mooring-watch-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-wilds-art-dress.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for the Briarwood, Mooring's Watch and the mine gate, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.249 Owner approval: Briarwood and Mooring's Watch concepts open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.251Write Deepworks and Bellwater Priory concepts prompts (8 assets)
Description
Why
The Deepworks (lantern pools in the dark) and Bellwater Priory (melancholy god-rays) are the two dungeon approaches; their contrast carries the Act II mood.
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Deepworks winch house and cage lift; the old glenstone cutting hall turned Rootcellar camp (kettle throne, hammocks, Duggan's crate); Vane's sealed gallery with pried Hornfolk carvings and the breach; Bellwater's roofless nave with god-rays and ivy; the choir of singing-monk statues and hidden choir door; the ruined scriptorium; the belfry with the cracked, tongueless Vesper Bell over the Sounding Shaft; the priory garden with the dead pear tree (plus a bloom variant as a second view in the same file).
Asset ids (8):
env-deepworks-winch-house-01env-deepworks-goblin-hall-01env-deepworks-sealed-gallery-01env-bellwater-nave-01env-bellwater-choir-01env-bellwater-scriptorium-01env-bellwater-belfry-01env-bellwater-garden-pear-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-mine-priory-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Also consumed as mood targets by
e26-deepworks-encounters,e26-vane-gallery-breach,e26-priory-choir,e26-priory-scriptorium,e26-priory-belfry,e26-vesper-bell-finale.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 8 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the Deepworks reads as lantern pools (#F0A04B) in #1C1A22 dark with the goblin camp lived-in rather than monstrous, the priory reads melancholy-beautiful in its sandstone/fresco palette, and the Vesper Bell's crack and missing tongue are readable.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.252Generate Deepworks and Bellwater Priory concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 8 prompt files ofe37-asset-env-mine-priory-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-mine-priory-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.251 Write Deepworks and Bellwater Priory concepts prompts (8 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.253Owner approval: Deepworks and Bellwater Priory concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 8 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-mine-priory-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the Deepworks reads as lantern pools (#F0A04B) in #1C1A22 dark with the goblin camp lived-in rather than monstrous, the priory reads melancholy-beautiful in its sandstone/fresco palette, and the Vesper Bell's crack and missing tongue are readable. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the Deepworks reads as lantern pools (#F0A04B) in #1C1A22 dark with the goblin camp lived-in rather than monstrous, the priory reads melancholy-beautiful in its sandstone/fresco palette, and the Vesper Bell's crack and missing tongue are readable.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-mine-priory-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.252 Generate Deepworks and Bellwater Priory concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.254Integrate approved Deepworks and Bellwater Priory concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e26-deepworks-greybox,e26-priory-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-mine-priory.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for the Deepworks and Bellwater Priory, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.253 Owner approval: Deepworks and Bellwater Priory concepts open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.255Write Knot environment concepts prompts (7 assets)
Description
Why
The Knot is the MVP's heart: cold rune-carved basalt, then the warm Fold reveal that Horn is not a monster, then the dreamlike Cradle (story bible §3.5, style bible §2.2).
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Outer Ring rune-banded basalt corridors with loom-spider webs copying the runes; the Ossuary bone galleries and Clatter's pale mushroom garden; the Chapel of Echoes with bells and the counterweighted portcullis; Horn's Hall arena; the Fold (warm moss beds, sleepers breathing, goblin bread, chalk tallies, skylight — the warm exception); the Sounding Shaft with bell-ropes, counterweights and ledges; the Cradle (round chamber, carved faces turned inward, dream-leak, the abbot's warm anchor lanterns the only warm light, the Nightjar only suggested).
Asset ids (7):
env-knot-outer-ring-01env-knot-ossuary-01env-knot-chapel-echoes-01env-knot-horns-hall-01env-knot-fold-01env-knot-sounding-shaft-01env-knot-cradle-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-knot-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Asset id
env-knot-fold-01follows the style bible §15.1 example (the world needs list saidenv-knot-the-fold-01). Also consumed as mood targets bye26-ossuary-galleries,e26-chapel-of-echoes,e26-horns-hall-fold,e26-sounding-shaft-climb,e26-cradle-arena.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 7 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: the Outer Ring, Ossuary and Chapel read cold and geometric (basalt #3A3A44, glyph glow #9FD8E0), the Fold is unmistakably warm and safe (moss #7A9A4A, light-well #F6E7B0), and the Cradle is unsettling beauty with the Nightjar never shown as a creature.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.256Generate Knot environment concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 7 prompt files ofe37-asset-env-knot-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-knot-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 7 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.255 Write Knot environment concepts prompts (7 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.257Owner approval: Knot environment concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 7 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-knot-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the Outer Ring, Ossuary and Chapel read cold and geometric (basalt #3A3A44, glyph glow #9FD8E0), the Fold is unmistakably warm and safe (moss #7A9A4A, light-well #F6E7B0), and the Cradle is unsettling beauty with the Nightjar never shown as a creature. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the Outer Ring, Ossuary and Chapel read cold and geometric (basalt #3A3A44, glyph glow #9FD8E0), the Fold is unmistakably warm and safe (moss #7A9A4A, light-well #F6E7B0), and the Cradle is unsettling beauty with the Nightjar never shown as a creature.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-knot-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.256 Generate Knot environment concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.258Integrate approved Knot environment concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e26-knot-outer-greybox,e26-knot-inner-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-knot.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for the Knot, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.257 Owner approval: Knot environment concepts open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.263Write Briar Glen tileables and trims prompts (8 assets)
Description
Why
Large town surfaces (cobbles, plaster, thatch, slate) dominate every Briar Glen frame; the noisy loose-tile roof must look different because it plays differently for thieves (style bible §7).
What
Write one prompt file per asset in
assets/prompts/texture/<asset-id>.md(category: texture) from templatetileable-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Cobblestone, plaster-and-timber, thatch, slate roof, a visibly different loose/noisy roof tile, inn wood floor, bookshop shelf trim and a general Briar Glen timber trim sheet (beams, sills, stair noses).
Asset ids (8):
tex-stone-cobble-briar-01tex-plaster-timber-briar-01tex-thatch-briar-01tex-roof-slate-briar-01tex-roof-loose-tile-01tex-wood-floor-inn-01tex-bookshop-shelves-trim-01tex-trim-briar-glen-timber-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-tex-town-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
tex-trim-briar-glen-timber-01is added to cover style bible §6.3 trim sheets (not in the world needs list).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 8 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every tile repeats without an obvious feature at 3×3, colours stay in the Briar Glen palette, and the loose roof tile is distinguishable from slate at 10 m in greyscale.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.264Generate Briar Glen tileables and trims via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 8 prompt files ofe37-asset-tex-town-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-tex-town-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha), then dimensions, transparency/background, seamless tiling on both axes (automated edge-difference check) and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.263 Write Briar Glen tileables and trims prompts (8 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.265Owner approval: Briar Glen tileables and trims
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 8 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-tex-town-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every tile repeats without an obvious feature at 3×3, colours stay in the Briar Glen palette, and the loose roof tile is distinguishable from slate at 10 m in greyscale. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every tile repeats without an obvious feature at 3×3, colours stay in the Briar Glen palette, and the loose roof tile is distinguishable from slate at 10 m in greyscale.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-tex-town-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.264 Generate Briar Glen tileables and trims via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.266Integrate approved Briar Glen tileables and trims
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, KTX2 + mip build, vertex-colour blend variants
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e24-town-greybox,e24-rooftops-thief-routes. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-art-dress.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 8 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Briar Glen scene on Low and High, when Playwright captures the Market Green and a rooftop route, then final textures load with no placeholder materials, the loose-tile roof uses its own material id, and no console errors occur.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e24.12 Build a rooftop network across Briar Glen for thieves and climbers open
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.265 Owner approval: Briar Glen tileables and trims open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3mw-e37.267Write Briarwood and Mooring's Watch tileables prompts (9 assets)
Description
Why
The Briarwood floor, bark and bramble walls set the wilderness mood; the trampled-fern decal is itself a gameplay clue (the Sleepers' Path).
What
Write one prompt file per asset in
assets/prompts/texture/<asset-id>.md(category: texture) from templatetileable-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Mossy forest floor, oak bark, trampled-fern decal (Sleepers' Path), bramble wall, wet mud lane, pond water surface, weathered lichen stone and faded barony banner for Mooring's Watch, plus a watchtower stone trim sheet.
Asset ids (9):
tex-forest-floor-moss-01tex-bark-oak-01tex-fern-trampled-decal-01tex-bramble-wall-01tex-mud-lane-wet-01tex-pond-water-01tex-stone-weathered-lichen-01tex-banner-faded-01tex-trim-moorings-watch-stone-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-tex-wilds-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
tex-trim-moorings-watch-stone-01is added for §6.3 trims. The trampled-fern decal pairs withe25-sleepers-pathreveal logic (night/tracker) — the decal is always present; visibility is a lighting/shader rule.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 9 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: forest surfaces sit in the Briarwood palette and tile invisibly, the trampled-fern decal is subtle at noon but readable under the night grade, and the pond water reads as freezable/swimmable per the Legibility Language.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.249 Owner approval: Briarwood and Mooring's Watch concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.268Generate Briarwood and Mooring's Watch tileables via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 9 prompt files ofe37-asset-tex-wilds-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-tex-wilds-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 9 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha), then dimensions, transparency/background, seamless tiling on both axes (automated edge-difference check) and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.267 Write Briarwood and Mooring's Watch tileables prompts (9 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.269Owner approval: Briarwood and Mooring's Watch tileables
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 9 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-tex-wilds-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: forest surfaces sit in the Briarwood palette and tile invisibly, the trampled-fern decal is subtle at noon but readable under the night grade, and the pond water reads as freezable/swimmable per the Legibility Language. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then forest surfaces sit in the Briarwood palette and tile invisibly, the trampled-fern decal is subtle at noon but readable under the night grade, and the pond water reads as freezable/swimmable per the Legibility Language.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-tex-wilds-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.268 Generate Briarwood and Mooring's Watch tileables via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.270Integrate approved Briarwood and Mooring's Watch tileables
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, KTX2 + mip build, vertex-colour blend variants
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e25-briarwood-greybox,e25-sleepers-path,e25-mooring-watch-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-wilds-art-dress.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 9 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Briarwood and Mooring's Watch scenes, when Playwright captures day and night presets, then final textures load with no placeholder materials, the fern decal renders along the path spline, and no console errors occur.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e25.2 Reveal the Sleepers' Path at night or to a tracker's eye open
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.269 Owner approval: Briarwood and Mooring's Watch tileables open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3mw-e37.271Write Deepworks and priory tileables prompts (10 assets)
Description
Why
Mine rock and shoring versus priory sandstone and fresco carry the Act II contrast; the carved Bell Line is a story landmark.
What
Write one prompt file per asset in
assets/prompts/texture/<asset-id>.md(category: texture) from templatetileable-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Mine rock, timber shoring, rail iron, carved Bell Line relief (shape only, no legible text), priory sandstone, grey marble, faded fresco, stained glass, plus Deepworks timber and priory moulding trim sheets.
Asset ids (10):
tex-rock-mine-01tex-timber-shoring-01tex-rail-iron-01tex-carved-bell-line-01tex-sandstone-priory-01tex-marble-grey-01tex-fresco-faded-01tex-stained-glass-01tex-trim-deepworks-timber-01tex-trim-priory-moulding-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-tex-mine-priory-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Two trim sheets are added for §6.3. Stained glass doubles as a light cookie for god-rays (
e03-light-fieldreads light, not the texture).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 10 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: mine surfaces stay dark and low-noise so lantern pools read, priory surfaces sit in the sandstone/fresco palette, the Bell Line carving has no legible letters, and stained glass works as an emissive/light-cookie source.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.253 Owner approval: Deepworks and Bellwater Priory concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.272Generate Deepworks and priory tileables via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 10 prompt files ofe37-asset-tex-mine-priory-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-tex-mine-priory-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha), then dimensions, transparency/background, seamless tiling on both axes (automated edge-difference check) and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.271 Write Deepworks and priory tileables prompts (10 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.273Owner approval: Deepworks and priory tileables
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 10 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-tex-mine-priory-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: mine surfaces stay dark and low-noise so lantern pools read, priory surfaces sit in the sandstone/fresco palette, the Bell Line carving has no legible letters, and stained glass works as an emissive/light-cookie source. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then mine surfaces stay dark and low-noise so lantern pools read, priory surfaces sit in the sandstone/fresco palette, the Bell Line carving has no legible letters, and stained glass works as an emissive/light-cookie source.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-tex-mine-priory-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.272 Generate Deepworks and priory tileables via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.274Integrate approved Deepworks and priory tileables
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, KTX2 + mip build, vertex-colour blend variants
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e26-deepworks-greybox,e26-priory-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-mine-priory.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 10 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Deepworks and priory scenes, when Playwright captures the winch house, goblin hall, nave and belfry, then final textures load with no placeholder materials and no console errors.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.273 Owner approval: Deepworks and priory tileables open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3mw-e37.275Write Knot tileables and rune bands prompts (10 assets)
Description
Why
The Knot is a sentence carved as corridors: its runes are the navigation puzzle (follow the guide-runes or loop forever), so the rune bands are gameplay-critical, not decoration (style bible §5.3).
What
Write one prompt file per asset in
assets/prompts/texture/<asset-id>.md(category: texture) from templatetileable-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Knot basalt, worn bronze, bone niche, Fold moss, the Cradle dream shader mask, a bronze trim sheet, and four Hornfolk rune bands: continuous right-angle meander lines at hand height with paired horn-crescent word breaks; guide-runes carry a small horn-crescent pointing along the route, ignored runes loop back on themselves.
Asset ids (10):
tex-basalt-knot-01tex-bronze-worn-01tex-bone-niche-01tex-moss-fold-01tex-cradle-dream-01tex-trim-knot-bronze-01tex-rune-band-hornfolk-01tex-rune-band-hornfolk-02tex-rune-band-hornfolk-03tex-rune-band-hornfolk-04
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-tex-knot-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Rune bands are kept in this set (not a separate one) so their palette matches the basalt;
tex-cradle-dream-01is a shader mask (painted greyscale noise), not an albedo.tex-trim-knot-bronze-01is added for §6.3.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 10 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha; rune bands 2048×256 strips with a matching normal map (High only)) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: guide-rune bands and ignored-rune bands are distinguishable at 5 m in greyscale by shape alone (horn-crescent direction, loop-back), runes are uncarved-stone coloured unless used as a light-reactive puzzle object, and the Fold moss is unmistakably warmer than the basalt.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.257 Owner approval: Knot environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.276Generate Knot tileables and rune bands via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 10 prompt files ofe37-asset-tex-knot-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-tex-knot-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha; rune bands 2048×256 strips with a matching normal map (High only). Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate (1536² for large ground/wall surfaces) → 1024²/512² KTX2 ETC1S with mips, seamless on both axes, low-contrast edges, plus vertex-colour blend variant; trims 1024×256; decals alpha; rune bands 2048×256 strips with a matching normal map (High only)), then dimensions, transparency/background, seamless tiling on both axes (automated edge-difference check) and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.275 Write Knot tileables and rune bands prompts (10 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.277Owner approval: Knot tileables and rune bands
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 10 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-tex-knot-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: guide-rune bands and ignored-rune bands are distinguishable at 5 m in greyscale by shape alone (horn-crescent direction, loop-back), runes are uncarved-stone coloured unless used as a light-reactive puzzle object, and the Fold moss is unmistakably warmer than the basalt. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then guide-rune bands and ignored-rune bands are distinguishable at 5 m in greyscale by shape alone (horn-crescent direction, loop-back), runes are uncarved-stone coloured unless used as a light-reactive puzzle object, and the Fold moss is unmistakably warmer than the basalt.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-tex-knot-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.276 Generate Knot tileables and rune bands via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.278Integrate approved Knot tileables and rune bands
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, KTX2 + mip build, vertex-colour blend variants
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e26-knot-outer-greybox,e26-knot-inner-greybox,e26-knot-rune-navigation. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-knot.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 10 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Knot scenes, when the rune-navigation replay walks a guide-rune path and an ignored-rune loop, then both bands render from their final textures, the replay passes, and no console errors occur.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
- mw-e26.11 Make the Knot loop unless you follow its runes open
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.277 Owner approval: Knot tileables and rune bands open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3mw-e37.279Write location skies and backdrops prompts (4 assets)
Description
Why
Sky and distant backdrop set time-of-day for the whole surface: golden-hour town, night with the song on still nights, overcast forest, and the ending dawn.
What
Write one prompt file per asset in
assets/prompts/sky/<asset-id>.md(category: sky) from templateskybox-backdrop.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Briar Glen golden hour, Briar Glen night (moon, no aurora), Briarwood overcast, and the valley dawn used by the endings.
Asset ids (4):
sky-briar-glen-golden-hour-01sky-briar-glen-night-01sky-briarwood-overcast-01sky-valley-dawn-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-skies-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
sky-valley-dawn-01is also consumed bye26-ending-states(epilogue); it is not linked here so the surface lighting beads do not wait on the finale.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 4 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (equirect 4096×2048 generate → KTX2 4096×2048 (High) / 2048×1024 (Low), seamless left-right wrap, horizon at vertical centre) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each sky wraps without a visible seam, matches the location light/mood column of style bible §2.2, and the night sky is dark enough that lantern light remains the key light.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.249 Owner approval: Briarwood and Mooring's Watch concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:textureepic:e37milestone:m3mw-e37.280Generate location skies and backdrops via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 4 prompt files ofe37-asset-skies-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-skies-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: equirect 4096×2048 generate → KTX2 4096×2048 (High) / 2048×1024 (Low), seamless left-right wrap, horizon at vertical centre. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 4 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (equirect 4096×2048 generate → KTX2 4096×2048 (High) / 2048×1024 (Low), seamless left-right wrap, horizon at vertical centre), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.279 Write location skies and backdrops prompts (4 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:textureepic:e37milestone:m3mw-e37.281Owner approval: location skies and backdrops
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 4 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-skies-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each sky wraps without a visible seam, matches the location light/mood column of style bible §2.2, and the night sky is dark enough that lantern light remains the key light. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each sky wraps without a visible seam, matches the location light/mood column of style bible §2.2, and the night sky is dark enough that lantern light remains the key light.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-skies-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.280 Generate location skies and backdrops via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3needs-humanmw-e37.282Integrate approved location skies and backdrops
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/texture/<asset-id>/, build KTX2 with mips (UASTC for any normal map), register each texture in the stylized material library with its vertex-colour blend partner, add CREDITS rows and approval records, and swap the grey-box materials in the consuming areas.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e24-town-greybox,e25-briarwood-greybox,e27-world-clock. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-lighting-audio,e25-wilds-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 4 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Briar Glen and the Briarwood, when the world clock is stepped through day, dusk and night via the debug console, then the matching final sky loads with no seam visible in a 360° Playwright capture and no console errors.
- AC-5 perf Given the sky textures, when
e32-perf-budgetsruns, then each sky is ≤ 4096×2048 on High and ≤ 2048×1024 on Low and resident texture memory stays ≤ 256 MB on Low. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.281 Owner approval: location skies and backdrops open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:textureepic:e37milestone:m3mw-e37.283Write named cast portraits prompts (19 assets)
Description
Why
Dialogue is text-only in the MVP, so portraits carry every character's voice and warmth; they also become the likeness reference for the 3D NPCs and hero creatures.
What
Write one prompt file per asset in
assets/prompts/portrait/<asset-id>.md(category: portrait) from templatenpc-portrait.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Portraits for the Briar Glen cast (Mirela Thorne, Captain Brannoc Hale, Harrow Vane, Odette "Dot" Farrow, Hollis Pell, Tansy Pell aged 12, Juniper Fenn, Oswin Brand, Duggan Marl), the six missing (Moss Greaves, Ettie Lark, Rowan Tate, Harlan Burr, Mina Coles, Fitch — also shown on the missing-persons board), and the Knot's talkers: Brother Horn (grey muzzle, bronze-wired horns, soft sad eyes), Clatter (rotted habit, apologetic), Mother Kettleback (copper kettle hat) and the Sleepless Abbot Corvin Ashgrove (lantern-lit dream-fog, courteous).
Asset ids (19):
portrait-npc-mirela-booksellerportrait-npc-hale-captainportrait-npc-vane-mine-ownerportrait-npc-dot-innkeeperportrait-npc-hollis-bakerportrait-npc-tansy-pellportrait-npc-juniper-fletcherportrait-npc-oswin-smithportrait-npc-duggan-foremanportrait-npc-moss-sleeperportrait-npc-ettie-sleeperportrait-npc-rowan-sleeperportrait-npc-harlan-sleeperportrait-npc-mina-sleeperportrait-npc-fitch-sleeperportrait-npc-horn-wardenportrait-npc-clatter-skeletonportrait-npc-kettleback-matriarchportrait-npc-abbot-corvin
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-portraits-cast-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Portraits are approved before the 3D NPC/creature concepts so the turnarounds match these faces. The dialogue portrait frame is part A's
ui-frame-dialogue-portrait-01.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 19 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every character matches their story bible §5 description, the set reads as one painter, faces are expressive but natural, Horn reads intimidating-then-sad (never a monster) and the abbot gentle rather than sinister.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:portraitepic:e37milestone:m3mw-e37.284Generate named cast portraits via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 19 prompt files ofe37-asset-portraits-cast-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-portraits-cast-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 19 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.283 Write named cast portraits prompts (19 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:portraitepic:e37milestone:m3mw-e37.285Owner approval: named cast portraits
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 19 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-portraits-cast-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every character matches their story bible §5 description, the set reads as one painter, faces are expressive but natural, Horn reads intimidating-then-sad (never a monster) and the abbot gentle rather than sinister. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 19 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every character matches their story bible §5 description, the set reads as one painter, faces are expressive but natural, Horn reads intimidating-then-sad (never a monster) and the abbot gentle rather than sinister.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-portraits-cast-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.284 Generate named cast portraits via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:portraitepic:e37milestone:m3needs-humanmw-e37.286Integrate approved named cast portraits
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e22-dialogue-ui,e24-npc-schedules,e14-clatter,e14-mother-kettleback,e14-horn-befriend-help,e13-abbot-encounter,e26-horns-hall-fold.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 19 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given each named character's dialogue opened via the dialogue preview runner, when the dialogue UI renders, then the final portrait shows in
ui-frame-dialogue-portrait-01with no placeholder flags and no console errors. - AC-5 perf Given the portrait atlas, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and portraits stream with their location package rather than the initial download. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e13.16 Build the Cradle encounter with fight, trick, persuade and join paths open
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
- mw-e14.6 Befriend and help Brother Horn without fighting open
- mw-e22.10 Build accessible dialogue UI with choices, tags and history done
- mw-e24.13 Place Briar Glen's NPCs on day/night schedules in progress
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:portraitepic:e37milestone:m3mw-e37.291Write story and loot item icons prompts (29 assets)
Description
Why
Story items and everyday loot need icons that match their world models and read in the inventory at 64 px; part A covered only the class kits.
What
Write one prompt file per asset in
assets/prompts/icon-item/<asset-id>.md(category: icon-item) from templateitem-icon.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Story items (the Vesperine Hours, horn-coin, Horn's ring, the Vesper Bell tongue, Deepworks ledger, rusted watch key, Tansy's ribbon, Anselm's psalter, Mortimer's tooth, goblin kettle helm, "friend below" note, gallery key, Fold horn-key, Hornfolk fragment, Vane's mine key, the Horn of Horn) and common loot/consumables (healing draught, Juniper's salve, wakeful tea, sleep-draught, bread loaf, oil flask, iron and brass keys, the slice skeleton key, silver ornament, goblin trinket, candle stub, folded note).
Asset ids (29):
icon-item-vesperine-hours-01icon-item-horn-coin-01icon-item-horn-ring-01icon-item-vesper-bell-tongue-01icon-item-deepworks-ledger-01icon-item-rusted-watch-key-01icon-item-tansy-ribbon-01icon-item-psalter-anselm-01icon-item-mortimer-tooth-01icon-item-goblin-kettle-helm-01icon-item-friend-below-note-01icon-item-gallery-key-01icon-item-fold-horn-key-01icon-item-hornfolk-fragment-01icon-item-mine-key-vane-01icon-item-horn-of-horn-01icon-item-healing-draught-01icon-item-salve-juniper-01icon-item-wakeful-tea-01icon-item-sleep-draught-01icon-item-bread-loaf-01icon-item-oil-flask-01icon-item-key-iron-01icon-item-key-brass-01icon-item-skeleton-key-01icon-item-silver-ornament-01icon-item-goblin-trinket-01icon-item-candle-stub-01icon-item-note-folded-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-icons-world-items-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Covers the "loot/world item icons" hand-off from
e37-asset-icons-items-kit-prompts; coins reuse part A'sicon-item-coin-01/icon-item-gold-coin-01. The loot list (draughts, salve, tea, bread, oil, keys, ornament, trinket, candle, note) is inferred from systems/world beads and the story bible — confirm against the item data whene18-loot-tablescontent lands.icon-item-gallery-key-01andicon-item-skeleton-key-01also serve the m1 slice (which ships on placeholders).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 29 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: every icon is identifiable at 64 px, the three keys and two draughts are distinguishable by shape not colour alone, and story items carry their canon marks (horn-coin ox, Lantern Court seal on the Hours, Vane mark on the key) without legible text.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.112 Owner approval: kit item icons open
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:iconepic:e37milestone:m3mw-e37.292Generate story and loot item icons via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 29 prompt files ofe37-asset-icons-world-items-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-icons-world-items-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 29 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 256²/128²/64² transparent, object ~80 % fill, ink drop shadow down-right), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.291 Write story and loot item icons prompts (29 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:iconepic:e37milestone:m3mw-e37.293Owner approval: story and loot item icons
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 29 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-icons-world-items-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every icon is identifiable at 64 px, the three keys and two draughts are distinguishable by shape not colour alone, and story items carry their canon marks (horn-coin ox, Lantern Court seal on the Hours, Vane mark on the key) without legible text. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 29 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every icon is identifiable at 64 px, the three keys and two draughts are distinguishable by shape not colour alone, and story items carry their canon marks (horn-coin ox, Lantern Court seal on the Hours, Vane mark on the key) without legible text.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-icons-world-items-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.292 Generate story and loot item icons via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconepic:e37milestone:m3needs-humanmw-e37.294Integrate approved story and loot item icons
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e17-inventory-ui,e17-item-schema,e17-consumables-quickslots,e18-loot-tables.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 29 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the inventory with every item in this set granted via the debug console, when opened, then all 29 icons render from the atlas with no placeholder flags.
- AC-5 perf Given the rebuilt icon atlas, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.2 Define item definition schema and content validator done
- mw-e17.6 Use consumables from inventory and assignable quick slots done
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.293 Owner approval: story and loot item icons open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:iconepic:e37milestone:m3mw-e37.295Select CC0 kit pieces for Briar Glen exterior props (10 models)
Description
Why
The Market Green is the systemic playground: stalls break, barrels hide, washing lines burn, lamps can be doused — every piece carries a legibility cue (style bible §7).
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Market stalls (breakable), crates, hay cart, water trough, washing line (flammable), sacks, hedges/fences, lamp posts, window lanterns and the lamplighter pole.
Kit brief ids (2):
prop-town-market-set-01prop-town-lighting-set-01
Target model ids (10):
model-prop-market-stall-01model-prop-crate-01model-prop-hay-cart-01model-prop-water-trough-01model-prop-washing-line-01model-prop-sack-pile-01model-prop-fence-hedge-01model-prop-lamp-post-01model-prop-window-lantern-01model-prop-lamplighter-pole-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-town-exterior-kit-kit-recolour)
Notes
Barrels and wardrobes used for hiding are not duplicated: the town reuses
model-prop-barrel-hide-01andmodel-prop-wardrobe-hide-01frome37-asset-thief-props-*(part A owns them).Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 2 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 10 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.296Recolour Briar Glen exterior props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-town-exterior-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 10 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.295 Select CC0 kit pieces for Briar Glen exterior props (10 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.297Owner approval: Briar Glen exterior props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 10 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-town-exterior-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every interactive prop shows its legibility cue (breakable, flammable, hideable, dousable) at 10 m and the set reads as one palette with the approved Briar Glen concepts. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every interactive prop shows its legibility cue (breakable, flammable, hideable, dousable) at 10 m and the set reads as one palette with the approved Briar Glen concepts.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-town-exterior-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.296 Recolour Briar Glen exterior props from CC0 kits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.298Integrate approved Briar Glen exterior props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e24-town-systemic-objects,e24-town-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-art-dress.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 10 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given Briar Glen, when the debug console spawns each exterior prop and the thief hides, breaks and douses, then the final models load with their legibility cues and no console errors.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e24.3 Place systemic objects and hiding spots throughout Briar Glen open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.297 Owner approval: Briar Glen exterior props kit turntables open
- mw-e37.53 Integrate approved thief props open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.299Select CC0 kit pieces for Briar Glen interior props (20 models)
Description
Why
Seven named interiors (inn, bookshop, forge, fletchery, bakery, Watch House, Vane House) need furniture that feels lived-in and consistent without bespoke modelling for every chair.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Generic furniture and trade kit: inn bar, tavern tables, chairs, beds, hearth; crooked bookshelves, rolling ladder, lectern; anvil, bellows, weapon racks; arrow bins, herb racks, salve jars; bread ovens, dough troughs; desks, cell bars; Vane's fine furniture and balcony railings. Story-specific pieces (red shelf, curtain, bell mould, strongbox, drain stone, missing-persons board, cellar kettle, lending shelf) come from
e37-asset-props-town-story-*.Kit brief ids (7):
prop-inn-set-01prop-bookshop-set-01prop-forge-set-01prop-fletchery-set-01prop-bakery-set-01prop-watch-house-set-01prop-vane-house-set-01
Target model ids (20):
model-prop-inn-bar-01model-prop-table-tavern-01model-prop-chair-01model-prop-bed-01model-prop-hearth-01model-prop-shelf-crooked-01model-prop-bookshop-ladder-01model-prop-lectern-01model-prop-anvil-01model-prop-bellows-01model-prop-weapon-rack-01model-prop-arrow-bin-01model-prop-herb-rack-01model-prop-salve-jars-01model-prop-bread-oven-01model-prop-dough-trough-01model-prop-desk-01model-prop-cell-bars-01model-prop-furniture-fine-set-01model-prop-balcony-railing-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-town-interiors-kit-kit-recolour)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 7 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 20 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.300Recolour Briar Glen interior props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-town-interiors-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 20 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.299 Select CC0 kit pieces for Briar Glen interior props (20 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.301Owner approval: Briar Glen interior props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 20 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-town-interiors-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each interior set reads as its owner (Dot's cosy inn, Mirela's crooked shop, Vane's cold wealth) while sharing one palette, and liftable/breakable pieces carry their cues. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 20 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each interior set reads as its owner (Dot's cosy inn, Mirela's crooked shop, Vane's cold wealth) while sharing one palette, and liftable/breakable pieces carry their cues.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-town-interiors-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.300 Recolour Briar Glen interior props from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.302Integrate approved Briar Glen interior props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e24-sleeping-ox,e24-lamplit-stacks,e24-trade-row,e24-watch-house-jail,e24-vane-house. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-art-dress.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 20 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given each Briar Glen interior, when Playwright loads it, then final interior props render with no placeholder models, the bookshop ladder and jail bars keep their colliders, and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e24.4 Build the Sleeping Ox inn: common room, rooms, cellar and rest open
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
- mw-e24.7 Build the Forge, Fletchery & Simples and Bakery with merchant stock open
- mw-e24.8 Build the Watch House and a jail cell whose drain leads out open
- mw-e24.9 Build Vane House as an infiltration target with strongbox and ledger open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.301 Owner approval: Briar Glen interior props kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.303Select CC0 kit pieces for wilderness props (15 models)
Description
Why
The Briarwood needs dense, instanced vegetation and a handful of set-dressing pieces for the millpond and Mooring's Watch without breaking the draw-call budget.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Oak and holly trees (instanced LODs + impostors), bramble clumps, fern clusters, fallen logs, a hollow oak, rock set; sluice gate, drowned cart, woodcutter's hut contents, the lost lantern; armour stands (Hollow Sentinel posts), tower bell and cord, keeper's log, goblin lookout junk. The rotatable signal mirror comes from
e37-asset-props-set-piece-hero-*.Kit brief ids (3):
prop-forest-set-01prop-millpond-set-01prop-watchtower-set-01
Target model ids (15):
model-prop-tree-oak-01model-prop-tree-holly-01model-prop-bramble-01model-prop-fern-cluster-01model-prop-log-fallen-01model-prop-oak-hollow-01model-prop-rock-set-01model-prop-sluice-gate-01model-prop-cart-drowned-01model-prop-hut-contents-01model-prop-lantern-lost-01model-prop-armour-stand-01model-prop-tower-bell-cord-01model-prop-keeper-log-01model-prop-goblin-lookout-junk-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-wilds-kit-kit-recolour)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 3 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 15 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.249 Owner approval: Briarwood and Mooring's Watch concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.304Recolour wilderness props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-wilds-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Trees get LOD1 + billboard impostor baked by the pipeline. The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 15 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.303 Select CC0 kit pieces for wilderness props (15 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.305Owner approval: wilderness props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 15 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-wilds-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: vegetation reads as oak/holly/bramble in the Briarwood palette with impostors that do not pop at 40 m, climbable and flammable pieces carry their cues, and the tower bell cord reads as pullable. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 15 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then vegetation reads as oak/holly/bramble in the Briarwood palette with impostors that do not pop at 40 m, climbable and flammable pieces carry their cues, and the tower bell cord reads as pullable.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-wilds-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.304 Recolour wilderness props from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.306Integrate approved wilderness props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e25-briarwood-greybox,e25-millpond-drowned-lane,e25-mooring-watch-greybox,e25-mooring-watch-encounters. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-wilds-art-dress.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 15 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the Briarwood and Mooring's Watch, when Playwright flies the debug camera along the priory road, then final vegetation and props render with impostor transitions and no console errors.
- AC-4 perf Given the Briarwood with final vegetation, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low with instancing enabled. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e25.5 Build the millpond and drowned lane with a Hushling and a lost lantern open
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
- mw-e25.7 Place the Hollow Sentinels and goblin lookout with class resolutions open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.305 Owner approval: wilderness props kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.307Select CC0 kit pieces for dungeon props (19 models)
Description
Why
Mine supports that break, bone piles that scatter and candle racks that burn are systemic dungeon furniture; they come from kits so the budget goes to hero pieces.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Mine supports (breakable), rails, carts, winch, cage lift, oil lamps, rubble; pews, altar, candle racks, bell frame; scriptorium lecterns, loose pages, candle store, ruined bookshelves; bone piles (physics), bone niches, holy-water font, Clatter's pale mushrooms. Singing-monk statues and the Vesper Bell come from
e37-asset-props-set-piece-hero-*.Kit brief ids (4):
prop-mine-shoring-set-01prop-priory-set-01prop-scriptorium-set-01prop-ossuary-set-01
Target model ids (19):
model-prop-mine-support-01model-prop-mine-rail-01model-prop-mine-cart-01model-prop-winch-01model-prop-cage-lift-01model-prop-oil-lamp-01model-prop-rubble-set-01model-prop-pew-01model-prop-altar-01model-prop-candle-rack-01model-prop-bell-frame-01model-prop-lectern-scriptorium-01model-prop-loose-pages-01model-prop-candle-store-01model-prop-bookshelf-ruined-01model-prop-bone-pile-01model-prop-bone-niche-01model-prop-holy-water-font-01model-prop-mushroom-pale-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-dungeon-kit-kit-recolour)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 4 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 19 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.253 Owner approval: Deepworks and Bellwater Priory concepts open
- mw-e37.257 Owner approval: Knot environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.308Recolour dungeon props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-dungeon-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 19 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.307 Select CC0 kit pieces for dungeon props (19 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.309Owner approval: dungeon props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 19 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-dungeon-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: breakable supports and physics bone piles carry their cues, the priory pieces share the sandstone/gilt palette, and nothing on the bone props reads as gore. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 19 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then breakable supports and physics bone piles carry their cues, the priory pieces share the sandstone/gilt palette, and nothing on the bone props reads as gore.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-dungeon-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.308 Recolour dungeon props from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.310Integrate approved dungeon props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e26-deepworks-greybox,e25-supply-tunnel,e26-priory-greybox,e26-priory-scriptorium,e26-ossuary-galleries. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-mine-priory,e26-art-dress-knot.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 19 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the Deepworks, priory and Ossuary, when the knight smashes a support and a bone pile, then the final fracture pieces and physics bones spawn, and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e25.9 Make the collapsed supply tunnel a class-gated shortcut to the mine open
- mw-e26.13 Populate the Ossuary: Forgotten habits, Clatter's garden, the psalter open
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
- mw-e26.7 Build the scriptorium with book mimics, tallow ooze and abbot letters open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.309 Owner approval: dungeon props kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.311Select CC0 kit pieces for puzzle reskin and ending props (9 models)
Description
Why
The e15 puzzle prefabs must look like they belong to the priory and the Knot, and each ending changes the world with a few telling props (bread at the mine gate, an epilogue lantern).
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Area reskins of e15 prefabs — mirrors, statues, scales plates, braziers, chimes, water basin — keeping their collider/socket contracts; ending props: bread basket at the mine gate, epilogue lantern, rebuilt mine headframe.
Kit brief ids (2):
prop-puzzle-reskins-01prop-ending-variants-01
Target model ids (9):
model-prop-puzzle-mirror-01model-prop-puzzle-statue-01model-prop-puzzle-scales-plate-01model-prop-puzzle-brazier-01model-prop-puzzle-chime-01model-prop-puzzle-water-basin-01model-prop-bread-basket-01model-prop-lantern-epilogue-01model-prop-mine-headframe-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-puzzle-endings-kit-kit-recolour)
Notes
Reskins must not change prefab collider sizes or socket names (
e15-prefab-piecescontract); the puzzle solver replays are the regression check.Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 2 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 9 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.253 Owner approval: Deepworks and Bellwater Priory concepts open
- mw-e37.257 Owner approval: Knot environment concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.312Recolour puzzle reskin and ending props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-puzzle-endings-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 9 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.311 Select CC0 kit pieces for puzzle reskin and ending props (9 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.313Owner approval: puzzle reskin and ending props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 9 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-puzzle-endings-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each reskin keeps the silhouette and legibility cue of its grey-box prefab (a player who solved it in grey-box recognises it) and the ending props read at a glance in epilogue framing. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each reskin keeps the silhouette and legibility cue of its grey-box prefab (a player who solved it in grey-box recognises it) and the ending props read at a glance in epilogue framing.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-puzzle-endings-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.312 Recolour puzzle reskin and ending props from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.314Integrate approved puzzle reskin and ending props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e26-dungeon-puzzles,e26-ending-states,e15-prefab-pieces. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-mine-priory,e26-art-dress-knot.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 9 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the Frozen Channel, Sunbeam, Scales and Dark Crossing puzzles, when their solution replays run with final reskins, then every replay passes and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e15.4 Ship reusable puzzle prefabs built only from world components done
- mw-e26.15 Place the Frozen Channel, Sunbeam, Scales and Dark Crossing puzzles open
- mw-e26.23 Build the four endings' level states across town and dungeon open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.313 Owner approval: puzzle reskin and ending props kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.315Select CC0 kit pieces for world-placed story item models (14 models)
Description
Why
Story items lie in the world before they reach the inventory; their world model must match their icon so players recognise what they found.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.World pickups matching the story item icons: the Vesperine Hours, horn-coin (tiny ox asleep on the back), Horn's ring, Deepworks ledger, rusted watch key, Tansy's ribbon, Anselm's psalter, Mortimer's tooth, a "friend below" note, the gallery key, the Fold horn-key, a Hornfolk fragment, Vane's mine key and the Horn of Horn. The bell tongue lives in
e37-asset-props-set-piece-hero-*; the goblin kettle helm reuses the goblin helm attachment frome37-asset-creatures-goblins-*.Kit brief ids (1):
prop-story-items-set-01
Target model ids (14):
model-prop-vesperine-hours-01model-prop-horn-coin-01model-prop-horn-ring-01model-prop-deepworks-ledger-01model-prop-rusted-watch-key-01model-prop-tansy-ribbon-01model-prop-psalter-anselm-01model-prop-mortimer-tooth-01model-prop-friend-below-note-01model-prop-gallery-key-01model-prop-fold-horn-key-01model-prop-hornfolk-fragment-01model-prop-mine-key-vane-01model-prop-horn-of-horn-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-story-items-kit-kit-recolour)
Notes
Many pieces are simple (book, coin, key, ring) and match CC0 kit meshes; unique ones (Horn of Horn, horn-key) go to the gap-fill chain.
prop-story-items-set-01is a new brief id grouping these models.Budget: small props 50–400 tris, prop atlas, one shared material (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 1 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 14 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, prop atlas, one shared material (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.293 Owner approval: story and loot item icons open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.316Recolour world-placed story item models from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-story-items-kit-kit-approve)
Notes
Budget: small props 50–400 tris, prop atlas, one shared material (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 14 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, prop atlas, one shared material (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.315 Select CC0 kit pieces for world-placed story item models (14 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.317Owner approval: world-placed story item models kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 14 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-story-items-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each world model is recognisable as its approved icon from 3 m under the wayfinder highlight, and seals/marks follow style bible §5.3 (Lantern Court seal, Vane mark, Hornfolk runes) without legible text. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each world model is recognisable as its approved icon from 3 m under the wayfinder highlight, and seals/marks follow style bible §5.3 (Lantern Court seal, Vane mark, Hornfolk runes) without legible text.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-story-items-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.316 Recolour world-placed story item models from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.318Integrate approved world-placed story item models
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e17-world-pickup-drop,e18-placed-treasure,e26-vane-gallery-breach,e26-horns-hall-fold.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 14 models are underpublic/assets/model/within small props 50–400 tris, prop atlas, one shared material (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the debug console, when each story item is dropped into the world and picked up, then its final model renders with the wayfinder highlight and the matching icon appears in the inventory, with no console errors.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e17.7 Pick up, drop and throw items as physical world objects done
- mw-e18.5 Support designer-placed and hidden treasure with discovery rules open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e26.4 Build Vane's sealed gallery, the ledger and the sealable breach open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.317 Owner approval: world-placed story item models kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.323Write Briar Glen story props concept prompts (2 images)
Description
Why
The town's story is told through a few specific objects — the bounty board, the missing-persons list, the bell mould, Vane's strongbox, the red shelf — that no kit provides.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Notice board with bounty poster (500 → 800 crowns shown by a swappable decal; numbers rendered by the game font) as its own sheet; a story-prop sheet for the Vesper Bell mould, missing-persons board, jail drain stone and evidence chest, Vane's strongbox and ledger desk, the Stacks' red shelf, restricted-shelf curtain and Mortimer's rare shelf, the biscuit tin, the inn cellar kettle and lending shelf.
Concept image ids (2):
prop-town-notice-board-01prop-town-story-set-01
Target model ids (13):
model-prop-notice-board-01model-prop-bell-mould-01model-prop-missing-persons-board-01model-prop-drain-stone-01model-prop-evidence-chest-01model-prop-strongbox-vane-01model-prop-ledger-desk-01model-prop-red-shelf-01model-prop-restricted-curtain-01model-prop-rare-shelf-mortimer-01model-prop-biscuit-tin-01model-prop-cellar-kettle-01model-prop-lending-shelf-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-props-town-story-3d-generate)
Notes
prop-town-story-set-01is a new concept id grouping story pieces from the inn, bookshop, forge, Watch House and Vane House sets of the needs list.Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: each story prop is readable as itself at 5 m, the drain stone visibly differs from its neighbours (loose), the strongbox reads lockpickable, and no prop carries legible text.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.324Generate Briar Glen story props concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-props-town-story-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.323 Write Briar Glen story props concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.325Owner approval: Briar Glen story props flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-town-story-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each story prop is readable as itself at 5 m, the drain stone visibly differs from its neighbours (loose), the strongbox reads lockpickable, and no prop carries legible text. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each story prop is readable as itself at 5 m, the drain stone visibly differs from its neighbours (loose), the strongbox reads lockpickable, and no prop carries legible text.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-town-story-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.324 Generate Briar Glen story props concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.326Generate Briar Glen story props 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 13 target models: first try a CC0 kit match recoloured to the palette atlas; otherwise call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-props-town-story-turntable-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 13 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.325 Owner approval: Briar Glen story props flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.327Owner approval: Briar Glen story props turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 13 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-town-story-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each story prop is readable as itself at 5 m, the drain stone visibly differs from its neighbours (loose), the strongbox reads lockpickable, and no prop carries legible text. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 13 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each story prop is readable as itself at 5 m, the drain stone visibly differs from its neighbours (loose), the strongbox reads lockpickable, and no prop carries legible text.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-town-story-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.326 Generate Briar Glen story props 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.328Integrate approved Briar Glen story props
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e24-town-greybox,e24-trade-row,e24-watch-house-jail,e24-vane-house,e24-lamplit-stacks,e24-sleeping-ox,e24-stacks-back-room. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-art-dress. Lower-priority consumers (e24-stacks-back-room) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 13 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given Briar Glen, when the thief loosens the drain stone, picks the strongbox and reads the notice board, then the final models load with their cues and the bounty decal swaps from 500 to 800 on the world fact, with no console errors.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e24.4 Build the Sleeping Ox inn: common room, rooms, cellar and rest open
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
- mw-e24.7 Build the Forge, Fletchery & Simples and Bakery with merchant stock open
- mw-e24.8 Build the Watch House and a jail cell whose drain leads out open
- mw-e24.9 Build Vane House as an infiltration target with strongbox and ledger open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.327 Owner approval: Briar Glen story props turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.329Write Rootcellar goblin camp props concept prompts (1 images)
Description
Why
The goblins are people with a camp, not vermin (style bible §5.2); their furniture — a kettle throne, hammocks, alarm pots — is where that reads.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Mother Kettleback's kettle throne, hammocks, alarm pots (loud when knocked), Duggan's crate (a person-sized crate with air holes), junk piles, planted "goblin trinkets" stamped with the Vane Deepworks mark (pick crossed with a key), and a cookfire.
Concept image ids (1):
prop-goblin-camp-set-01
Target model ids (7):
model-prop-kettle-throne-01model-prop-goblin-hammock-01model-prop-alarm-pots-01model-prop-duggan-crate-01model-prop-goblin-junk-pile-01model-prop-goblin-trinket-vane-01model-prop-goblin-cookfire-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-props-goblin-camp-3d-generate)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 1 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: the camp reads lived-in and patched (warm greens, copper kettles), alarm pots read as noisy traps, and the Vane mark on the trinkets is visible on close inspection.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.253 Owner approval: Deepworks and Bellwater Priory concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.330Generate Rootcellar goblin camp props concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 1 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-props-goblin-camp-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 1 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.329 Write Rootcellar goblin camp props concept prompts (1 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.331Owner approval: Rootcellar goblin camp props flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 1 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-goblin-camp-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the camp reads lived-in and patched (warm greens, copper kettles), alarm pots read as noisy traps, and the Vane mark on the trinkets is visible on close inspection. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 1 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the camp reads lived-in and patched (warm greens, copper kettles), alarm pots read as noisy traps, and the Vane mark on the trinkets is visible on close inspection.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-goblin-camp-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.330 Generate Rootcellar goblin camp props concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.332Generate Rootcellar goblin camp props 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 7 target models: first try a CC0 kit match recoloured to the palette atlas; otherwise call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-props-goblin-camp-turntable-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 7 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.331 Owner approval: Rootcellar goblin camp props flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.333Owner approval: Rootcellar goblin camp props turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 7 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-goblin-camp-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the camp reads lived-in and patched (warm greens, copper kettles), alarm pots read as noisy traps, and the Vane mark on the trinkets is visible on close inspection. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the camp reads lived-in and patched (warm greens, copper kettles), alarm pots read as noisy traps, and the Vane mark on the trinkets is visible on close inspection.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-goblin-camp-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.332 Generate Rootcellar goblin camp props 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.334Integrate approved Rootcellar goblin camp props
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e26-deepworks-encounters,e14-mother-kettleback,e14-rootcellar-society. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-mine-priory.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 7 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the goblin hall, when the thief knocks an alarm pot and opens Duggan's crate, then the final models load, the alarm noise event fires, and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e14.2 Make the Rootcellar clan talkable, bribable and forgiving of capture open
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e26.3 Populate the Deepworks: goblin camp, miner Forgotten, mine hazards open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.333 Owner approval: Rootcellar goblin camp props turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.335Write Hornfolk and Knot props concept prompts (4 images)
Description
Why
Hornfolk stonework (always covering its ears) is the Knot's visual voice, and the Fold/Cradle props carry the reveal and the finale.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.The Standing Horn menhir (hero landmark); Knot statuary sheet: Hornfolk ear-covering statues, carved landmark faces (two, turning inward near the Cradle), rune pillars, counterweighted portcullis, chapel bells; Fold sheet: moss beds, goblin bread, chalk tally wall (thick strokes by a big hand), the horn-key door; Cradle sheet: the abbot's four anchor lanterns (shootable, the only warm light) and dream-leak floating debris.
Concept image ids (4):
prop-standing-horn-01prop-knot-set-01prop-fold-set-01prop-cradle-set-01
Target model ids (13):
model-prop-standing-horn-01model-prop-statue-hornfolk-ears-01model-prop-carved-face-landmark-01model-prop-carved-face-landmark-02model-prop-rune-pillar-01model-prop-portcullis-counterweight-01model-prop-chapel-bell-set-01model-prop-moss-bed-01model-prop-goblin-bread-01model-prop-chalk-tally-wall-01model-prop-fold-horn-door-01model-prop-anchor-lantern-01model-prop-dream-debris-set-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-props-hornfolk-knot-3d-generate)
Notes
The Standing Horn is placed by
e25-standing-horn(P2) but also anchors the Hornfolk statue style, so it is produced here at P1.Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2); large statues 2–5 k. Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 4 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2); large statues 2–5 k) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: every Hornfolk statue covers its ears, the Fold props read warm and cared-for, anchor lanterns read as targets against the cold Cradle, and carved faces are readable as landmarks from 20 m.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.249 Owner approval: Briarwood and Mooring's Watch concepts open
- mw-e37.257 Owner approval: Knot environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.336Generate Hornfolk and Knot props concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 4 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-props-hornfolk-knot-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 4 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.335 Write Hornfolk and Knot props concept prompts (4 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.337Owner approval: Hornfolk and Knot props flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 4 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-hornfolk-knot-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every Hornfolk statue covers its ears, the Fold props read warm and cared-for, anchor lanterns read as targets against the cold Cradle, and carved faces are readable as landmarks from 20 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every Hornfolk statue covers its ears, the Fold props read warm and cared-for, anchor lanterns read as targets against the cold Cradle, and carved faces are readable as landmarks from 20 m.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-hornfolk-knot-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.336 Generate Hornfolk and Knot props concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.338Generate Hornfolk and Knot props 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 13 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-props-hornfolk-knot-turntable-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2); large statues 2–5 k. API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 13 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2); large statues 2–5 k, or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.337 Owner approval: Hornfolk and Knot props flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.339Owner approval: Hornfolk and Knot props turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 13 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-hornfolk-knot-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: every Hornfolk statue covers its ears, the Fold props read warm and cared-for, anchor lanterns read as targets against the cold Cradle, and carved faces are readable as landmarks from 20 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 13 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then every Hornfolk statue covers its ears, the Fold props read warm and cared-for, anchor lanterns read as targets against the cold Cradle, and carved faces are readable as landmarks from 20 m.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-hornfolk-knot-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.338 Generate Hornfolk and Knot props 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.340Integrate approved Hornfolk and Knot props
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e26-knot-outer-greybox,e26-chapel-of-echoes,e26-horns-hall-fold,e26-cradle-arena,e25-standing-horn. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-knot. Lower-priority consumers (e25-standing-horn) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 13 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2); large statues 2–5 k and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the Chapel, the Fold and the Cradle, when the portcullis trap fires, the horn-key door opens and an anchor lantern is shot, then the final models animate/break correctly and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e26.14 Build the Chapel of Echoes with the Chimes puzzle and portcullis trap open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e26.17 Stage the Cradle arena for the abbot confrontation open
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.339 Owner approval: Hornfolk and Knot props turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.341Write set-piece hero props concept prompts (3 images)
Description
Why
Three hero objects carry puzzles and the finale: the cracked Vesper Bell (and its tongue), the priory's rotatable singing-monk "Watchers", and Mooring's rotatable signal mirror.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Vesper Bell in three states — cracked and tongueless (as found), tongue rehung, mended (Frostglass/Alteration) — plus the bronze tongue as a world item with its warm rim; three singing-monk statue poses (mouths open, rotatable/topple-able); the signal mirror on its iron pivot.
Concept image ids (3):
prop-vesper-bell-01prop-priory-watcher-statues-01prop-signal-mirror-01
Target model ids (8):
model-prop-vesper-bell-cracked-01model-prop-vesper-bell-tongued-01model-prop-vesper-bell-mended-01model-prop-vesper-bell-tongue-01model-prop-statue-singing-monk-01model-prop-statue-singing-monk-02model-prop-statue-singing-monk-03model-prop-signal-mirror-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-props-set-piece-hero-3d-generate)
Notes
prop-priory-watcher-statues-01andprop-signal-mirror-01are new concept ids split out of the priory and watchtower sets. The bell tongue icon is ine37-asset-icons-world-items-*.Budget: hero props 2–5 k tris (bell up to 6 k with LOD1 50 %), T2 1024² unique texture, ≤ 1 material each (style bible §13.2, §6.1 hero assets). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 3 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (hero props 2–5 k tris (bell up to 6 k with LOD1 50 %), T2 1024² unique texture, ≤ 1 material each (style bible §13.2, §6.1 hero assets)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: the bell's crack and missing tongue read from the foot of the Sounding Shaft as a dark bell-mouth, the mended bell is visibly whole, the Watchers sing with open mouths (the opposite of Hornfolk statues), and the mirror's facing direction is readable at 10 m.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.249 Owner approval: Briarwood and Mooring's Watch concepts open
- mw-e37.253 Owner approval: Deepworks and Bellwater Priory concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.342Generate set-piece hero props concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 3 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-props-set-piece-hero-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 3 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.341 Write set-piece hero props concept prompts (3 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.343Owner approval: set-piece hero props flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 3 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-set-piece-hero-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the bell's crack and missing tongue read from the foot of the Sounding Shaft as a dark bell-mouth, the mended bell is visibly whole, the Watchers sing with open mouths (the opposite of Hornfolk statues), and the mirror's facing direction is readable at 10 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the bell's crack and missing tongue read from the foot of the Sounding Shaft as a dark bell-mouth, the mended bell is visibly whole, the Watchers sing with open mouths (the opposite of Hornfolk statues), and the mirror's facing direction is readable at 10 m.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-set-piece-hero-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.342 Generate set-piece hero props concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.344Generate set-piece hero props 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 8 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-props-set-piece-hero-turntable-approve)
Notes
Budget: hero props 2–5 k tris (bell up to 6 k with LOD1 50 %), T2 1024² unique texture, ≤ 1 material each (style bible §13.2, §6.1 hero assets). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 8 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets hero props 2–5 k tris (bell up to 6 k with LOD1 50 %), T2 1024² unique texture, ≤ 1 material each (style bible §13.2, §6.1 hero assets), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.343 Owner approval: set-piece hero props flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.345Owner approval: set-piece hero props turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 8 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-set-piece-hero-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the bell's crack and missing tongue read from the foot of the Sounding Shaft as a dark bell-mouth, the mended bell is visibly whole, the Watchers sing with open mouths (the opposite of Hornfolk statues), and the mirror's facing direction is readable at 10 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the bell's crack and missing tongue read from the foot of the Sounding Shaft as a dark bell-mouth, the mended bell is visibly whole, the Watchers sing with open mouths (the opposite of Hornfolk statues), and the mirror's facing direction is readable at 10 m.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-set-piece-hero-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.344 Generate set-piece hero props 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.346Integrate approved set-piece hero props
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e26-priory-belfry,e26-vesper-bell-finale,e26-priory-choir,e25-signal-mirror. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-mine-priory.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 8 models are underpublic/assets/model/within hero props 2–5 k tris (bell up to 6 k with LOD1 50 %), T2 1024² unique texture, ≤ 1 material each (style bible §13.2, §6.1 hero assets) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the belfry, choir and Mooring's Watch roof, when the bell is struck tongueless, the tongue is rehung, a Watcher is rotated and the mirror is turned, then the final models swap state correctly and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e25.8 Turn the signal mirror to light the priory's hidden choir door open
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e26.6 Place the Watchers statue puzzle in the priory choir open
- mw-e26.8 Build the belfry and the sour-bell consequence of early ringing open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.345 Owner approval: set-piece hero props turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.347Write world prop gap-fill concept prompts (up to 3 images)
Description
Why
CC0 kits will not cover every world prop; pieces the kit-select beads list as gaps still need approved, consistent models.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: prop) fromprop-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Prop sheets for every piece listed in the gap lists of the P1 kit-select beads, grouped by region, then Claude-run image-to-3D. Ids below are reserved slots; the final ids are copied from the gap lists (one model id per gap piece, already named in the kit briefs).
Concept image ids (up to 3):
prop-world-gapfill-sheet-01prop-world-gapfill-sheet-02prop-world-gapfill-sheet-03
Target model ids (up to 15):
model-prop-<gap-piece>-01 … (up to 15 models, ids fixed by the kit briefs)
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-props-world-gapfill-3d-generate)
Notes
If the gap lists are empty this chain closes with a note. More than 15 gap pieces means a kit choice should be revisited before generating. The P2 cemetery/school kit does not feed this chain.
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the up to 3 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: each gap piece matches its kit-set neighbours in palette, bevel and texel density so it cannot be picked out as "the generated one".
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.295 Select CC0 kit pieces for Briar Glen exterior props (10 models) open
- mw-e37.299 Select CC0 kit pieces for Briar Glen interior props (20 models) open
- mw-e37.303 Select CC0 kit pieces for wilderness props (15 models) open
- mw-e37.307 Select CC0 kit pieces for dungeon props (19 models) open
- mw-e37.311 Select CC0 kit pieces for puzzle reskin and ending props (9 models) open
- mw-e37.315 Select CC0 kit pieces for world-placed story item models (14 models) open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.348Generate world prop gap-fill concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all up to 3 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-props-world-gapfill-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the up to 3 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.347 Write world prop gap-fill concept prompts (up to 3 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.349Owner approval: world prop gap-fill flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the up to 3 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-world-gapfill-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each gap piece matches its kit-set neighbours in palette, bevel and texel density so it cannot be picked out as "the generated one". Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each gap piece matches its kit-set neighbours in palette, bevel and texel density so it cannot be picked out as "the generated one".
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-world-gapfill-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.348 Generate world prop gap-fill concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.350Generate world prop gap-fill 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the up to 15 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-props-world-gapfill-turntable-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the up to 15 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.349 Owner approval: world prop gap-fill flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.351Owner approval: world prop gap-fill turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the up to 15 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-world-gapfill-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each gap piece matches its kit-set neighbours in palette, bevel and texel density so it cannot be picked out as "the generated one". Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 15 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each gap piece matches its kit-set neighbours in palette, bevel and texel density so it cannot be picked out as "the generated one".
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-world-gapfill-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.350 Generate world prop gap-fill 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.352Integrate approved world prop gap-fill
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e24-town-greybox,e25-briarwood-greybox,e26-deepworks-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-art-dress,e25-wilds-art-dress,e26-art-dress-mine-priory.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all up to 15 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the areas that use gap pieces, when Playwright loads them, then every gap model renders from its final glb with no placeholder models and no console errors.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.351 Owner approval: world prop gap-fill turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.353Write named Briar Glen NPC characters concept prompts (9 images)
Description
Why
The nine named townsfolk are met, followed, robbed and saved; they must be recognisable in the world as the faces in their portraits.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: character) fromcharacter-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Turnaround sheets matching the approved portraits for Mirela, Captain Hale, Harrow Vane, Dot, Hollis Pell, Tansy (child proportions, plus a nightclothes/barefoot sleepwalk variant texture), Juniper, Oswin and Duggan; Hale also gets a nightclothes variant (he sleepwalks at night per style bible §5.2). Bespoke heads on kit-compatible bodies where
e37-3d-pipeline-decisionrecommends it.Concept image ids (9):
char-npc-mirela-01char-npc-hale-01char-npc-vane-01char-npc-dot-01char-npc-hollis-01char-npc-tansy-01char-npc-juniper-01char-npc-oswin-01char-npc-duggan-01
Target model ids (9):
model-npc-mirela-01model-npc-hale-01model-npc-vane-01model-npc-dot-01model-npc-hollis-01model-npc-tansy-01model-npc-juniper-01model-npc-oswin-01model-npc-duggan-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-npcs-named-3d-generate)
Notes
Duggan is found in a goblin crate (
e14-mother-kettleback). Sleepwalk and schedule clips are ine37-asset-anim-npcs-*.Budget: key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 9 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: each NPC is recognisable against their approved portrait at 10 m, silhouettes are distinct from each other and from the player classes, and Tansy reads as a 12-year-old child without caricature.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.354Generate named Briar Glen NPC characters concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 9 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-npcs-named-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 9 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.353 Write named Briar Glen NPC characters concept prompts (9 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.355Owner approval: named Briar Glen NPC characters flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 9 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-npcs-named-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each NPC is recognisable against their approved portrait at 10 m, silhouettes are distinct from each other and from the player classes, and Tansy reads as a 12-year-old child without caricature. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each NPC is recognisable against their approved portrait at 10 m, silhouettes are distinct from each other and from the player classes, and Tansy reads as a 12-year-old child without caricature.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-npcs-named-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.354 Generate named Briar Glen NPC characters concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.356Generate named Briar Glen NPC characters 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 9 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig on the canonical humanoid skeleton viae37-animation-pipelineso shared clips retarget, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-npcs-named-turntable-approve)
Notes
Budget: key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 9 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.355 Owner approval: named Briar Glen NPC characters flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.357Owner approval: named Briar Glen NPC characters turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 9 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-npcs-named-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each NPC is recognisable against their approved portrait at 10 m, silhouettes are distinct from each other and from the player classes, and Tansy reads as a 12-year-old child without caricature. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each NPC is recognisable against their approved portrait at 10 m, silhouettes are distinct from each other and from the player classes, and Tansy reads as a 12-year-old child without caricature.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-npcs-named-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.356 Generate named Briar Glen NPC characters 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.358Integrate approved named Briar Glen NPC characters
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e24-npc-schedules,e24-first-night,e24-lamplit-stacks,e14-mother-kettleback.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 9 models are underpublic/assets/model/within key NPC 4–7 k tris, LOD1 50 %, T1–T2 atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given Briar Glen with NPC schedules running, when the world clock passes a full day in the debug console, then every named NPC renders with its final model, plays its schedule idles, and no console errors occur.
- AC-4 perf Given the Market Green at its busiest schedule slot, when
e32-perf-budgetsruns, then skinned meshes on screen stay ≤ 10 and draw calls ≤ 150 on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e24.13 Place Briar Glen's NPCs on day/night schedules in progress
- mw-e24.14 Stage the first-night sleepwalk from the Ox to the mine gate open
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.357 Owner approval: named Briar Glen NPC characters turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.359Select CC0 kit pieces for crowd NPC models (8 models)
Description
Why
Briar Glen needs a believable population and the six missing must be recognisable in the Fold — a CC0 modular character kit with recolours and hair/face swaps gives variety cheaply.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/character/<brief-id>.mdfromcharacter-reference-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Generic townsfolk set (8–12 recolour/outfit variants), two watch recruits (the teenagers), and the six missing — Moss Greaves, Ettie Lark, Rowan Tate, Harlan Burr, Mina Coles, Fitch — as kit variants in nightclothes, barefoot, matched to their approved portraits for sleeping poses.
Kit brief ids (1):
char-npc-crowd-set-01
Target model ids (8):
model-npc-townsfolk-set-01model-npc-watch-recruit-set-01model-npc-sleeper-moss-01model-npc-sleeper-ettie-01model-npc-sleeper-rowan-01model-npc-sleeper-harlan-01model-npc-sleeper-mina-01model-npc-sleeper-fitch-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-npc-crowd-kit-kit-recolour)
Notes
char-npc-crowd-set-01is a new kit-brief id. Sleeping poses and wake clips come frome37-asset-anim-npcs-*.Budget: background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 1 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 8 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.355 Owner approval: named Briar Glen NPC characters flat concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.360Recolour crowd NPC models from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-npc-crowd-kit-kit-approve)
Notes
Budget: background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 8 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.359 Select CC0 kit pieces for crowd NPC models (8 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.361Owner approval: crowd NPC models kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 8 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-npc-crowd-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: crowd variants are distinguishable at 15 m, each sleeper is recognisable against their portrait (hair, build, nightclothes colour), sleepers read as ordinary warm-skinned people distinct from Hushlings, and the recruits read young and nervous. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then crowd variants are distinguishable at 15 m, each sleeper is recognisable against their portrait (hair, build, nightclothes colour), sleepers read as ordinary warm-skinned people distinct from Hushlings, and the recruits read young and nervous.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-npc-crowd-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.360 Recolour crowd NPC models from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.362Integrate approved crowd NPC models
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e24-npc-schedules,e26-horns-hall-fold.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 8 models are underpublic/assets/model/within background NPC 2–4 k tris, LOD1 50 %, T1 shared atlas, 1 material, ≤ 65 bones on the canonical humanoid rig (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given Briar Glen by day and the Fold, when Playwright captures the market crowd and the six sleepers, then final crowd models render with distinct variants, sleepers lie on their moss beds, and no console errors occur.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e24.13 Place Briar Glen's NPCs on day/night schedules in progress
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.361 Owner approval: crowd NPC models kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.363Write Forgotten and Clatter creatures concept prompts (3 images)
Description
Why
The Forgotten are the most common enemy and the m1/m2 combat test subject; posed mid-habit they say "a skeleton might remember", and Clatter is the one who does.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Miner-Forgotten (pick, lamp-hook), monk-Forgotten (rotted Vesperine habit, staff and censer attachments) and Clatter (Brother Anselm Hobb: rotted habit, gardener's stoop, hunched apologetic silhouette). Cold eye-glow #9FD8E0, never red; negative space between ribs is the read.
Concept image ids (3):
creature-forgotten-miner-01creature-forgotten-monk-01creature-clatter-01
Target model ids (3):
model-creature-forgotten-miner-01model-creature-forgotten-monk-01model-creature-clatter-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-creatures-forgotten-3d-generate)
Notes
Asset ids use canon short names (style bible §5.2): the world needs list said
creature-skeleton-miner-01etc. Part A's Animate Bones spell reusesmodel-creature-forgotten-miner-01.Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 3 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: miner and monk variants read apart at 20 m by tool and habit, Clatter's stoop reads as a different (friendly) character in silhouette, and eye-glow is cold, not red.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e13.2 Write the MVP bestiary design sheet for all ten creatures open
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.364Generate Forgotten and Clatter creatures concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 3 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-creatures-forgotten-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 3 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.363 Write Forgotten and Clatter creatures concept prompts (3 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.365Owner approval: Forgotten and Clatter creatures flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 3 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-forgotten-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: miner and monk variants read apart at 20 m by tool and habit, Clatter's stoop reads as a different (friendly) character in silhouette, and eye-glow is cold, not red. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then miner and monk variants read apart at 20 m by tool and habit, Clatter's stoop reads as a different (friendly) character in silhouette, and eye-glow is cold, not red.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-forgotten-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.364 Generate Forgotten and Clatter creatures concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.366Generate Forgotten and Clatter creatures 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 3 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig on the canonical humanoid skeleton so Forgotten clips retarget; bones as separable collapse pieces for reassembly, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-creatures-forgotten-turntable-approve)
Notes
Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 3 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.365 Owner approval: Forgotten and Clatter creatures flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.367Owner approval: Forgotten and Clatter creatures turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 3 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-forgotten-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: miner and monk variants read apart at 20 m by tool and habit, Clatter's stoop reads as a different (friendly) character in silhouette, and eye-glow is cold, not red. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then miner and monk variants read apart at 20 m by tool and habit, Clatter's stoop reads as a different (friendly) character in silhouette, and eye-glow is cold, not red.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-forgotten-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.366 Generate Forgotten and Clatter creatures 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.368Integrate approved Forgotten and Clatter creatures
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e13-forgotten-skeleton,e13-forgotten-habits,e14-clatter.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 3 models are underpublic/assets/model/within standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the creature arena, when a miner, a monk and Clatter are spawned via the debug console, then final models load, collapse into separable bones on poise break, and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e13.1 Add the Forgotten skeleton with the e04 three-move set done
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.367 Owner approval: Forgotten and Clatter creatures turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.369Write Rootcellar goblin creatures concept prompts (3 images)
Description
Why
The Rootcellar clan is fully social — talk, trade, bribe, hire — so goblins must read as people with rank and personality, not vermin.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Scavenger (nets, junk pack) and slinger (sling, pouch) with round heads, triangle ears and pear bodies; a set of rank kettle-helm attachments (also the world model for the goblin kettle helm item); Mother Kettleback, big for a goblin, with a copper kettle as armour and another as a hat.
Concept image ids (3):
creature-goblin-scavenger-01creature-goblin-slinger-01creature-goblin-kettleback-01
Target model ids (4):
model-creature-goblin-scavenger-01model-creature-goblin-slinger-01model-creature-goblin-kettle-helm-01model-creature-goblin-kettleback-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-creatures-goblins-3d-generate)
Notes
Snig (P2) is in
e37-asset-creatures-social-secondary-*and reuses the goblin rig.Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 3 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: scavenger and slinger read apart by silhouette, kettle helms signal rank at 15 m, clothing is patched and lived-in, and Mother Kettleback reads as the matriarch.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e13.2 Write the MVP bestiary design sheet for all ten creatures open
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.370Generate Rootcellar goblin creatures concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 3 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-creatures-goblins-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 3 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.369 Write Rootcellar goblin creatures concept prompts (3 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.371Owner approval: Rootcellar goblin creatures flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 3 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-goblins-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: scavenger and slinger read apart by silhouette, kettle helms signal rank at 15 m, clothing is patched and lived-in, and Mother Kettleback reads as the matriarch. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then scavenger and slinger read apart by silhouette, kettle helms signal rank at 15 m, clothing is patched and lived-in, and Mother Kettleback reads as the matriarch.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-goblins-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.370 Generate Rootcellar goblin creatures concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.372Generate Rootcellar goblin creatures 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 4 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-creatures-goblins-turntable-approve)
Notes
Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 4 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.371 Owner approval: Rootcellar goblin creatures flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.373Owner approval: Rootcellar goblin creatures turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 4 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-goblins-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: scavenger and slinger read apart by silhouette, kettle helms signal rank at 15 m, clothing is patched and lived-in, and Mother Kettleback reads as the matriarch. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then scavenger and slinger read apart by silhouette, kettle helms signal rank at 15 m, clothing is patched and lived-in, and Mother Kettleback reads as the matriarch.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-goblins-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.372 Generate Rootcellar goblin creatures 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.374Integrate approved Rootcellar goblin creatures
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e13-rootcellar-goblins,e14-mother-kettleback,e14-rootcellar-society.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 4 models are underpublic/assets/model/within standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the goblin hall, when scavengers, slingers and Mother Kettleback are spawned, then final models load with helm variants per rank and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e14.2 Make the Rootcellar clan talkable, bribable and forgiving of capture open
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.373 Owner approval: Rootcellar goblin creatures turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.375Write Brother Horn hero creature concept prompts (1 images)
Description
Why
Brother Horn is the central encounter: terrifying at first read, sad and gentle on the second — the model must hold both (style bible §5.2).
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Nine feet (≈ 2.75 m), grey-muzzled and old, horns chipped and bound in bronze wire, moss on his shoulders, a cloth bundle and a pouch of bread for the sleepers; soft face shapes with big expressive eyes and a heavy brow that can read sad; plus his stone-headed axe carved with his name in Hornfolk runes (separate held-weapon model).
Concept image ids (1):
creature-horn-base-01
Target model ids (2):
model-creature-horn-01model-prop-horn-axe-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-creature-horn-3d-generate)
Notes
Asset id
creature-horn-base-01/model-creature-horn-01follows the style bible §15.1 examples (the world needs list saidcreature-minotaur-base-01).Budget: hero creature ≤ 20 k tris, LOD1 50 %, LOD2 25 %, T3 2048² High / T2 Low, 1–2 materials, ≤ 80 bones (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 1 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (hero creature ≤ 20 k tris, LOD1 50 %, LOD2 25 %, T3 2048² High / T2 Low, 1–2 materials, ≤ 80 bones (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: Horn is enormous and intimidating in silhouette yet his face can read sad, the bronze-wired chipped horns and grey muzzle are visible at 10 m, and there are no red eyes, drool or gore.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e13.2 Write the MVP bestiary design sheet for all ten creatures open
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.376Generate Brother Horn hero creature concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 1 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-creature-horn-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 1 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.375 Write Brother Horn hero creature concept prompts (1 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.377Owner approval: Brother Horn hero creature flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 1 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creature-horn-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Horn is enormous and intimidating in silhouette yet his face can read sad, the bronze-wired chipped horns and grey muzzle are visible at 10 m, and there are no red eyes, drool or gore. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 1 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Horn is enormous and intimidating in silhouette yet his face can read sad, the bronze-wired chipped horns and grey muzzle are visible at 10 m, and there are no red eyes, drool or gore.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creature-horn-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.376 Generate Brother Horn hero creature concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.378Generate Brother Horn hero creature 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 2 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-creature-horn-turntable-approve)
Notes
Budget: hero creature ≤ 20 k tris, LOD1 50 %, LOD2 25 %, T3 2048² High / T2 Low, 1–2 materials, ≤ 80 bones (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 2 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets hero creature ≤ 20 k tris, LOD1 50 %, LOD2 25 %, T3 2048² High / T2 Low, 1–2 materials, ≤ 80 bones (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.377 Owner approval: Brother Horn hero creature flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.379Owner approval: Brother Horn hero creature turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 2 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creature-horn-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Horn is enormous and intimidating in silhouette yet his face can read sad, the bronze-wired chipped horns and grey muzzle are visible at 10 m, and there are no red eyes, drool or gore. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Horn is enormous and intimidating in silhouette yet his face can read sad, the bronze-wired chipped horns and grey muzzle are visible at 10 m, and there are no red eyes, drool or gore.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creature-horn-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.378 Generate Brother Horn hero creature 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.380Integrate approved Brother Horn hero creature
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e13-horn-moveset,e14-horn-befriend-help.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 2 models are underpublic/assets/model/within hero creature ≤ 20 k tris, LOD1 50 %, LOD2 25 %, T3 2048² High / T2 Low, 1–2 materials, ≤ 80 bones (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given Horn's Hall, when Horn is spawned and cycles idle, charge and yield via the debug console, then the final model and axe load with correct attach points and no console errors.
- AC-4 perf Given Horn's Hall with Horn and the six sleepers visible, when
e32-perf-budgetsruns, then visible triangles stay ≤ 500 k and skinned meshes ≤ 10 on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e13.13 Add Brother Horn's creature data: charges, grabs and throws to stop open
- mw-e14.6 Befriend and help Brother Horn without fighting open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.379 Owner approval: Brother Horn hero creature turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.381Write bestiary creatures concept prompts (7 images)
Description
Why
Five creature types make up the systemic bestiary: each has a property the world can exploit (fire, light, noise, feeding) that its silhouette must advertise.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Briar Wolf (lean grey horizontal wedge, silent, ears toward the mine); Hollow Sentinel (Mooring's garrison armour with glowing seams and barony tabard remnants); Loom Spider (pale, dog-sized, radial leg spikes, one ember warning colour); Tallow Ooze (wax blob with wicks, candle stubs and a snuffer visible inside) plus small, puddle and frozen variants; book, lectern and chest mimics, each an almost-perfect object with one tell.
Concept image ids (7):
creature-briar-wolf-01creature-hollow-sentinel-01creature-loom-spider-01creature-tallow-ooze-01creature-mimic-book-01creature-mimic-lectern-01creature-mimic-chest-01
Target model ids (10):
model-creature-briar-wolf-01model-creature-hollow-sentinel-01model-creature-loom-spider-01model-creature-tallow-ooze-01model-creature-tallow-ooze-small-01model-creature-tallow-ooze-puddle-01model-creature-tallow-ooze-frozen-01model-creature-mimic-book-01model-creature-mimic-lectern-01model-creature-mimic-chest-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-creatures-bestiary-3d-generate)
Notes
Ids use canon short names (world needs list said
creature-wolf-briar-01,creature-spider-loom-01,creature-sentinel-armour-01,creature-ooze-tallow-01). Ooze puddle is a mesh decal state; the frozen variant is matte and cracked.Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); ooze variants and mimics may be lighter (≤ 2 k). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 7 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); ooze variants and mimics may be lighter (≤ 2 k)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: each creature's family shape (wedge, hollow rectangles, radial spikes, soft blob, box/slab) reads at 20 m, disposition cues read (wolf head up/down), the ooze reads as wax not element, and each mimic's tell is findable by an attentive player.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e13.2 Write the MVP bestiary design sheet for all ten creatures open
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.382Generate bestiary creatures concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 7 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-creatures-bestiary-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 7 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.381 Write bestiary creatures concept prompts (7 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.383Owner approval: bestiary creatures flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 7 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-bestiary-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each creature's family shape (wedge, hollow rectangles, radial spikes, soft blob, box/slab) reads at 20 m, disposition cues read (wolf head up/down), the ooze reads as wax not element, and each mimic's tell is findable by an attentive player. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each creature's family shape (wedge, hollow rectangles, radial spikes, soft blob, box/slab) reads at 20 m, disposition cues read (wolf head up/down), the ooze reads as wax not element, and each mimic's tell is findable by an attentive player.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-bestiary-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.382 Generate bestiary creatures concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.384Generate bestiary creatures 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 10 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-creatures-bestiary-turntable-approve)
Notes
Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); ooze variants and mimics may be lighter (≤ 2 k). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 10 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); ooze variants and mimics may be lighter (≤ 2 k), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.383 Owner approval: bestiary creatures flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.385Owner approval: bestiary creatures turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 10 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-bestiary-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each creature's family shape (wedge, hollow rectangles, radial spikes, soft blob, box/slab) reads at 20 m, disposition cues read (wolf head up/down), the ooze reads as wax not element, and each mimic's tell is findable by an attentive player. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each creature's family shape (wedge, hollow rectangles, radial spikes, soft blob, box/slab) reads at 20 m, disposition cues read (wolf head up/down), the ooze reads as wax not element, and each mimic's tell is findable by an attentive player.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-bestiary-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.384 Generate bestiary creatures 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.386Integrate approved bestiary creatures
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e13-briar-wolves,e13-hollow-sentinels,e13-loom-spiders,e13-tallow-ooze,e13-mimics.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 10 models are underpublic/assets/model/within standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); ooze variants and mimics may be lighter (≤ 2 k) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the creature arena, when each creature and ooze variant is spawned via the debug console, then final models load, the ooze swaps variants on melt/freeze, and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e13.10 Add Tallow Ooze that melts into fire puddles and shatters when frozen open
- mw-e13.11 Add book, lectern and chest mimics with tells and feeding open
- mw-e13.12 Add Hollow Sentinels bound to hold the tower until relieved open
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e13.8 Add silent Briar Wolf packs that can be fed, scared or remembered open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.385 Owner approval: bestiary creatures turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.387Write dream creatures concept prompts (2 images)
Description
Why
Hushlings and the abbot are made of the Nightjar's dream: soft, fog-bodied and unsettling, and they must never be confused with real sleepwalkers.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Hushling: child-sized grey fog figure, smooth eyeless face, head cocked to listen, fingers slightly too long, fraying into wisps below the knees (never reads as a real child); the Sleepless Abbot: tall tapering robe of lantern-lit dream-fog, hands folded, head inclined, the Vesper Bell's bronze tongue on his belt, mouth open like a choir statue when solid.
Concept image ids (2):
creature-hushling-01creature-abbot-01
Target model ids (2):
model-creature-hushling-01model-creature-abbot-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-creatures-dream-3d-generate)
Notes
Ids use canon short names (world needs list said
creature-wraith-abbot-01). Fog bodies come frome37-asset-vfx-world-*(vfx-hushling-fog-body-01,vfx-abbot-fog-body-01).Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); fog bodies use the world VFX fog shader over a low-poly core. Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); fog bodies use the world VFX fog shader over a low-poly core) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: Hushlings read as dream creatures (eyeless, fraying, no clothing detail) and are distinct from sleepwalkers and from the abbot, and the abbot is courteous in posture, never skeletal or snarling.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e13.2 Write the MVP bestiary design sheet for all ten creatures open
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.388Generate dream creatures concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-creatures-dream-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.387 Write dream creatures concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.389Owner approval: dream creatures flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-dream-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Hushlings read as dream creatures (eyeless, fraying, no clothing detail) and are distinct from sleepwalkers and from the abbot, and the abbot is courteous in posture, never skeletal or snarling. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Hushlings read as dream creatures (eyeless, fraying, no clothing detail) and are distinct from sleepwalkers and from the abbot, and the abbot is courteous in posture, never skeletal or snarling.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-dream-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.388 Generate dream creatures concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.390Generate dream creatures 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 2 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-creatures-dream-turntable-approve)
Notes
Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); fog bodies use the world VFX fog shader over a low-poly core. API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 2 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); fog bodies use the world VFX fog shader over a low-poly core, or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.389 Owner approval: dream creatures flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.391Owner approval: dream creatures turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 2 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-dream-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Hushlings read as dream creatures (eyeless, fraying, no clothing detail) and are distinct from sleepwalkers and from the abbot, and the abbot is courteous in posture, never skeletal or snarling. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Hushlings read as dream creatures (eyeless, fraying, no clothing detail) and are distinct from sleepwalkers and from the abbot, and the abbot is courteous in posture, never skeletal or snarling.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-dream-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.390 Generate dream creatures 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.392Integrate approved dream creatures
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e13-hushlings,e13-abbot-wraith.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 2 models are underpublic/assets/model/within standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2); fog bodies use the world VFX fog shader over a low-poly core and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the drowned lane and the Cradle, when a Hushling and the abbot are spawned, then final core meshes load with their fog-body shaders and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e13.15 Add the Sleepless Abbot wraith: solid only while singing open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.391 Owner approval: dream creatures turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.399Write Forgotten and Clatter animations motion briefs (13 clips)
Description
Why
The Forgotten's three-move set, crumble-and-reassemble and habit idles are how players learn to read and avoid them; Clatter's fidgets make him a person.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Miner three-move set (chop, 2-hit slash, lunge), collapse and reassemble, hauling idle; monk staff sweep, censer swing, sweeping and praying idles, a calm "settle" when the Vesper hymn is hummed; Clatter tending his mushrooms and an apologetic fidget.
Clip ids (13):
anim-forgotten-chop-01anim-forgotten-slash-double-01anim-forgotten-lunge-01anim-forgotten-collapse-01anim-forgotten-reassemble-01anim-forgotten-idle-haul-01anim-forgotten-staff-sweep-01anim-forgotten-censer-swing-01anim-forgotten-idle-sweep-01anim-forgotten-idle-pray-01anim-forgotten-calm-settle-01anim-clatter-idle-tend-01anim-clatter-fidget-apologise-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Attack windows must match the e04 three-move set timings used by
e13-forgotten-skeleton.Rig: canonical humanoid skeleton (Forgotten models are rigged to it); collapse/reassemble use per-bone physics hand-off markers. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 13 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.400Source and retarget Forgotten and Clatter animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-forgotten-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 13 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.367 Owner approval: Forgotten and Clatter creatures turntables open
- mw-e37.399 Write Forgotten and Clatter animations motion briefs (13 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.401Owner approval: Forgotten and Clatter animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 13 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-forgotten-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: attack telegraphs read from the default camera, habit idles read as habits (sweeping, praying, hauling) at 15 m, collapse/reassemble has no pops, and Clatter's idles read apologetic rather than threatening. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 13 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then attack telegraphs read from the default camera, habit idles read as habits (sweeping, praying, hauling) at 15 m, collapse/reassemble has no pops, and Clatter's idles read apologetic rather than threatening.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-forgotten-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.400 Source and retarget Forgotten and Clatter animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.402Integrate approved Forgotten and Clatter animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e13-forgotten-skeleton,e13-forgotten-reassembly,e13-forgotten-habits,e14-clatter.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 13 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the creature arena, when a miner attacks, collapses and reassembles, a monk sweeps and prays, and Clatter tends his garden, then final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e13.1 Add the Forgotten skeleton with the e04 three-move set done
- mw-e13.3 Forgotten crumble and reassemble unless bones are scattered or burned open
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
- mw-e37.368 Integrate approved Forgotten and Clatter creatures open
- mw-e37.401 Owner approval: Forgotten and Clatter animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.403Write goblin animations motion briefs (8 clips)
Description
Why
Goblins fight with slings and nets, raise very loud alarms and flee when outnumbered; their bodies must also say "we can talk" in the camp.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Sling throw, net throw, flee, alarm-pot banging, idle chatter, cower, Mother Kettleback enthroned idle and a haggling gesture.
Clip ids (8):
anim-goblin-sling-01anim-goblin-net-throw-01anim-goblin-flee-01anim-goblin-alarm-bang-01anim-goblin-idle-chatter-01anim-goblin-cower-01anim-kettleback-idle-throne-01anim-kettleback-haggle-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Rig: goblin creature rig from the approved goblin models (auto-rig), shared by scavenger, slinger, Kettleback and Snig. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 8 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.404Source and retarget goblin animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-goblins-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 8 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.373 Owner approval: Rootcellar goblin creatures turntables open
- mw-e37.403 Write goblin animations motion briefs (8 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.405Owner approval: goblin animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 8 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-goblins-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: sling and net wind-ups are readable telegraphs, flee and cower read as fear not attack, and camp idles read social. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then sling and net wind-ups are readable telegraphs, flee and cower read as fear not attack, and camp idles read social.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-goblins-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.404 Source and retarget goblin animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.406Integrate approved goblin animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e13-rootcellar-goblins,e14-mother-kettleback,e14-rootcellar-society.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 8 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the goblin hall, when goblins are alerted, attack, raise the alarm and flee, and Kettleback trades, then final clips play with markers driving sling, net and alarm events, and no console errors occur.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e14.2 Make the Rootcellar clan talkable, bribable and forgiving of capture open
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e37.374 Integrate approved Rootcellar goblin creatures open
- mw-e37.405 Owner approval: goblin animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.407Write Brother Horn animations motion briefs (8 clips)
Description
Why
Horn fights to stop, not to kill; every clip must feel heavy, effortful and reluctant, and the yield and carry clips carry the reveal.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Axe sweep, charge, grab-and-throw, stomp, yield kneel (axe laid flat, head bowed), circle-walk, resting with one eye open, carrying a sleeper gently.
Clip ids (8):
anim-horn-axe-sweep-01anim-horn-charge-01anim-horn-grab-throw-01anim-horn-stomp-01anim-horn-yield-kneel-01anim-horn-circle-walk-01anim-horn-rest-one-eye-01anim-horn-carry-sleeper-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Grab-throw uses grab/release markers bound to the player; carry-sleeper attaches a sleeper model from
e37-asset-npc-crowd-kit-*.Rig: Horn hero rig (≤ 80 bones) from
model-creature-horn-01, axe attach bone. Timings must match the consuming beads' attack/interaction data.Acceptance criteria
- AC-1 content Given the 8 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.408Source and retarget Brother Horn animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-horn-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 8 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.379 Owner approval: Brother Horn hero creature turntables open
- mw-e37.407 Write Brother Horn animations motion briefs (8 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.409Owner approval: Brother Horn animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 8 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-horn-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Horn reads nine feet tall and heavy, attacks read as stopping not killing, the yield reads as choice not defeat, and the carry is gentle. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Horn reads nine feet tall and heavy, attacks read as stopping not killing, the yield reads as choice not defeat, and the carry is gentle.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-horn-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.408 Source and retarget Brother Horn animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.410Integrate approved Brother Horn animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e13-horn-moveset,e13-horn-encounter,e14-horn-befriend-help.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 8 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given Horn's Hall, when the Warden encounter runs its charge, grab-throw, stomp and yield beats and the befriend path plays rest and carry, then final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e13.13 Add Brother Horn's creature data: charges, grabs and throws to stop open
- mw-e13.14 Build the Warden boss encounter with yield, spare and Trial of Horns open
- mw-e14.6 Befriend and help Brother Horn without fighting open
- mw-e37.380 Integrate approved Brother Horn hero creature open
- mw-e37.409 Owner approval: Brother Horn animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.411Write Briar Wolf and Loom Spider animations motion briefs (9 clips)
Description
Why
Wolves that can be fed or scared and spiders that crawl walls and flee from light are read entirely through motion.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Wolf pack trot, lunge, eating (fed), backing away from fire (no howl until the ending); spider wall crawl, ceiling crawl, bite, web spit, flee from light.
Clip ids (9):
anim-briar-wolf-trot-pack-01anim-briar-wolf-lunge-01anim-briar-wolf-eat-01anim-briar-wolf-back-away-fire-01anim-loom-spider-crawl-wall-01anim-loom-spider-crawl-ceiling-01anim-loom-spider-bite-01anim-loom-spider-web-spit-01anim-loom-spider-flee-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Rig: quadruped wolf rig and multi-leg spider rig from the approved bestiary models (auto-rig). Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 9 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.412Source and retarget Briar Wolf and Loom Spider animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-beasts-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 9 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.385 Owner approval: bestiary creatures turntables open
- mw-e37.411 Write Briar Wolf and Loom Spider animations motion briefs (9 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.413Owner approval: Briar Wolf and Loom Spider animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 9 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-beasts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: wolf head height reads disposition (lowered threat, raised curious), backing from fire reads as fear, and spider wall/ceiling crawls have no foot sliding. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then wolf head height reads disposition (lowered threat, raised curious), backing from fire reads as fear, and spider wall/ceiling crawls have no foot sliding.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-beasts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.412 Source and retarget Briar Wolf and Loom Spider animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.414Integrate approved Briar Wolf and Loom Spider animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e13-briar-wolves,e13-loom-spiders.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 9 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the wolf den and the Outer Ring, when wolves are fed and scared with fire and a spider crawls, bites and flees a light, then final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e13.8 Add silent Briar Wolf packs that can be fed, scared or remembered open
- mw-e37.386 Integrate approved bestiary creatures open
- mw-e37.413 Owner approval: Briar Wolf and Loom Spider animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.415Write Hushling and abbot animations motion briefs (7 clips)
Description
Why
Hushlings drift and listen, the abbot conducts and sings; their motion sells that they are made of dream, not flesh.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Hushling drift, grasp, dissipate (torn apart by loud sound); abbot singing/conducting (solid), drifting (fog), stagger when a lantern breaks, fury.
Clip ids (7):
anim-hushling-drift-01anim-hushling-grasp-01anim-hushling-dissipate-01anim-abbot-sing-conduct-01anim-abbot-drift-01anim-abbot-stagger-01anim-abbot-fury-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Rig: Hushling and abbot core rigs from the approved dream models; fog-body VFX follow bone sockets. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 7 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.416Source and retarget Hushling and abbot animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-dream-wraith-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 7 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.391 Owner approval: dream creatures turntables open
- mw-e37.415 Write Hushling and abbot animations motion briefs (7 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.417Owner approval: Hushling and abbot animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 7 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-dream-wraith-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Hushlings never show footfalls and their head-snap-to-sound reads as hunting, and the abbot's singing pose reads solid while drift reads insubstantial. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Hushlings never show footfalls and their head-snap-to-sound reads as hunting, and the abbot's singing pose reads solid while drift reads insubstantial.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-dream-wraith-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.416 Source and retarget Hushling and abbot animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.418Integrate approved Hushling and abbot animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e13-hushlings,e13-abbot-wraith,e13-abbot-encounter.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 7 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the drowned lane and the Cradle, when a Hushling hunts and is torn apart by a loud noise and the abbot sings, drifts, staggers and rages, then final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e13.15 Add the Sleepless Abbot wraith: solid only while singing open
- mw-e13.16 Build the Cradle encounter with fight, trick, persuade and join paths open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e37.392 Integrate approved dream creatures open
- mw-e37.417 Owner approval: Hushling and abbot animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.419Write ooze, mimic and sentinel animations motion briefs (14 clips)
Description
Why
The ooze, mimics and Hollow Sentinels are systemic toys whose tells and states are animation: a breathing mimic, a splitting ooze, a sentinel saluting and falling still.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Tallow Ooze crawl, engulf, split; per-mimic disguised breathing tell and bite (book, lectern, chest), the book mimic eating paper, a shared flee hop; Hollow Sentinel halberd sweep, shield charge, and salute-then-fall-still when relieved.
Clip ids (14):
anim-tallow-ooze-crawl-01anim-tallow-ooze-engulf-01anim-tallow-ooze-split-01anim-mimic-book-tell-01anim-mimic-book-bite-01anim-mimic-book-eat-paper-01anim-mimic-lectern-tell-01anim-mimic-lectern-bite-01anim-mimic-chest-tell-01anim-mimic-chest-bite-01anim-mimic-flee-01anim-hollow-sentinel-halberd-sweep-01anim-hollow-sentinel-shield-charge-01anim-hollow-sentinel-salute-still-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
Mortimer (P2) reuses the book mimic clips.
Rig: ooze blend-shape/joint rig, one rig per mimic body (book, lectern, chest) and the humanoid-derived Hollow Sentinel rig. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 14 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.420Source and retarget ooze, mimic and sentinel animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-constructs-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 14 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.385 Owner approval: bestiary creatures turntables open
- mw-e37.419 Write ooze, mimic and sentinel animations motion briefs (14 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.421Owner approval: ooze, mimic and sentinel animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 14 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-constructs-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each mimic's breathing tell is subtle but findable within 10 s of watching, the ooze split reads clearly, and the sentinel salute reads as honour discharged. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each mimic's breathing tell is subtle but findable within 10 s of watching, the ooze split reads clearly, and the sentinel salute reads as honour discharged.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-constructs-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.420 Source and retarget ooze, mimic and sentinel animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.422Integrate approved ooze, mimic and sentinel animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e13-tallow-ooze,e13-mimics,e13-hollow-sentinels.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 14 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the scriptorium, the Knot and Mooring's Watch, when an ooze engulfs and splits, each mimic tells and bites, and a sentinel attacks then is relieved, then final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e13.10 Add Tallow Ooze that melts into fire puddles and shatters when frozen open
- mw-e13.11 Add book, lectern and chest mimics with tells and feeding open
- mw-e13.12 Add Hollow Sentinels bound to hold the tower until relieved open
- mw-e37.386 Integrate approved bestiary creatures open
- mw-e37.421 Owner approval: ooze, mimic and sentinel animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.423Write townsfolk and sleeper animations motion briefs (11 clips)
Description
Why
Following Tansy's sleepwalk on the first night and finding the six sleepers breathing in the Fold are the MVP's key emotional beats; town schedules need a few work idles to feel alive.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Sleepwalk (serene, eyes open, barefoot), three sleeping poses with breathing (on back, on side, curled), waking and sitting up, plus schedule idles: talking, sitting, smith hammering, baker kneading, sweeping, lamplighting.
Clip ids (11):
anim-humanoid-sleepwalk-01anim-humanoid-sleep-pose-back-01anim-humanoid-sleep-pose-side-01anim-humanoid-sleep-pose-curled-01anim-humanoid-wake-sit-up-01anim-humanoid-npc-idle-talk-01anim-humanoid-npc-sit-01anim-humanoid-npc-work-hammer-01anim-humanoid-npc-work-knead-01anim-humanoid-npc-sweep-01anim-humanoid-npc-lamplight-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
The wake clip also serves
e26-vesper-bell-finale(sleepers wake). Tansy's hum issfx-tansy-hum-sleepwalk-loop-01.Rig: canonical humanoid skeleton (named NPCs and crowd kit share it). Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 11 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.424Source and retarget townsfolk and sleeper animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-npcs-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 11 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.357 Owner approval: named Briar Glen NPC characters turntables open
- mw-e37.361 Owner approval: crowd NPC models kit turntables open
- mw-e37.423 Write townsfolk and sleeper animations motion briefs (11 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.425Owner approval: townsfolk and sleeper animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 11 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-npcs-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the sleepwalk reads serene and uncanny (never zombie-like), sleepers visibly breathe, and schedule idles read as the trade they depict at 15 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 11 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the sleepwalk reads serene and uncanny (never zombie-like), sleepers visibly breathe, and schedule idles read as the trade they depict at 15 m.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-npcs-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.424 Source and retarget townsfolk and sleeper animations open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.426Integrate approved townsfolk and sleeper animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e24-npc-schedules,e24-first-night,e26-horns-hall-fold.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 11 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the first-night sequence and the Fold, when Tansy sleepwalks from the Ox to the mine gate and the six sleepers lie then wake, then final clips play with footstep markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e24.13 Place Briar Glen's NPCs on day/night schedules in progress
- mw-e24.14 Stage the first-night sleepwalk from the Ox to the mine gate open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e37.358 Integrate approved named Briar Glen NPC characters open
- mw-e37.362 Integrate approved crowd NPC models open
- mw-e37.425 Owner approval: townsfolk and sleeper animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.427Write world and dream VFX prompts (10 assets)
Description
Why
The dream leaking into the world, the fog-bodied creatures, the song's motes and a few set-piece effects are location-specific and not covered by the gameplay VFX sets.
What
Write one prompt file per asset in
assets/prompts/vfx/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Cradle dream-leak loop, Hushling fog body, abbot fog body, abbot lantern anchor link (the tether between lantern and abbot), song-intensity motes, signal-mirror beam, follow-rune glow (light-reactive rune cue), sentinel binding dispel, Vesper Bell soundwave, and the priory pear-tree bloom.
Asset ids (10):
vfx-dream-leak-loop-01vfx-hushling-fog-body-01vfx-abbot-fog-body-01vfx-abbot-lantern-anchor-link-01vfx-song-intensity-motes-01vfx-signal-mirror-beam-01vfx-rune-glow-follow-01vfx-sentinel-binding-dispel-01vfx-bell-soundwave-01vfx-pear-tree-bloom-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-world-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
Generic ambient VFX (dust, embers, fireflies, leaves, fog volume) are part A's
e37-asset-vfx-environment-ambient-*and are not duplicated.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 10 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (512² or 1024² flipbook sheets (4×4 or 8×8), alpha or additive on black, loopable where marked; shader masks greyscale 512²) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: dream effects use the Cradle palette (dream-fog #9C98C2, shimmer #CFC6F2) and read as unsettling beauty, the lantern link and rune glow read as gameplay cues, and nothing flashes faster than 3 Hz.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.389 Owner approval: dream creatures flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxepic:e37milestone:m3mw-e37.428Generate world and dream VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 10 prompt files ofe37-asset-vfx-world-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-world-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 512² or 1024² flipbook sheets (4×4 or 8×8), alpha or additive on black, loopable where marked; shader masks greyscale 512². Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (512² or 1024² flipbook sheets (4×4 or 8×8), alpha or additive on black, loopable where marked; shader masks greyscale 512²), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.427 Write world and dream VFX prompts (10 assets) open
area:artasset:vfxepic:e37milestone:m3mw-e37.429Owner approval: world and dream VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 10 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-world-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: dream effects use the Cradle palette (dream-fog #9C98C2, shimmer #CFC6F2) and read as unsettling beauty, the lantern link and rune glow read as gameplay cues, and nothing flashes faster than 3 Hz. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then dream effects use the Cradle palette (dream-fog #9C98C2, shimmer #CFC6F2) and read as unsettling beauty, the lantern link and rune glow read as gameplay cues, and nothing flashes faster than 3 Hz.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-world-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.428 Generate world and dream VFX via Codex CLI open
area:artasset:vfxepic:e37milestone:m3needs-humanmw-e37.430Integrate approved world and dream VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e29-vfx-framework,e26-cradle-arena,e13-hushlings,e13-abbot-wraith,e13-song-intensity,e25-signal-mirror,e26-knot-rune-navigation,e13-hollow-sentinels,e26-vesper-bell-finale,e26-priory-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-art-dress-knot.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 10 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Cradle, the drowned lane, Mooring's Watch roof and the belfry, when each effect is triggered via the debug console, then final textures render and VFX GPU time stays within its 2 ms share.
- AC-5 perf Given the Cradle arena with dream-leak, fog bodies, lantern links and motes active, when
e32-perf-budgetsruns, then live particles stay ≤ 800 and full-screen transparent layers ≤ 2 on Low. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e13.12 Add Hollow Sentinels bound to hold the tower until relieved open
- mw-e13.15 Add the Sleepless Abbot wraith: solid only while singing open
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e25.8 Turn the signal mirror to light the priory's hidden choir door open
- mw-e26.11 Make the Knot loop unless you follow its runes open
- mw-e26.17 Stage the Cradle arena for the abbot confrontation open
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.429 Owner approval: world and dream VFX open
area:artasset:vfxepic:e37milestone:m3mw-e37.78Write P2 spell VFX prompts (7 assets)
Description
Why
Fire Wall, Arcane Sight, Snuff and Stasis are MVP-desirable spells; if they ship, they deserve final visuals.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Phase flipbooks for fire-wall, arcane-sight, snuff, stasis.
Asset ids (7):
vfx-spell-fire-fire-wall-spawn-01vfx-spell-fire-fire-wall-loop-01vfx-spell-fire-fire-wall-end-01vfx-spell-arcane-arcane-sight-overlay-01vfx-spell-shadow-snuff-pulse-01vfx-spell-time-stasis-lock-01vfx-spell-time-stasis-release-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-spells-secondary-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
P2 because every spell here is P2; cut together with its spell. Stasis uses the time palette (desaturated area, clock-ring).
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 7 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each spell's phases read as one effect and match its school motion signature (§2.3).
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.79Generate P2 spell VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 7 prompt files ofe37-asset-vfx-spells-secondary-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-spells-secondary-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 7 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² sheet, 4×4 or 8×8 exact grid, black background for additive cores, 12–24 fps), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.78 Write P2 spell VFX prompts (7 assets) open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.80Owner approval: P2 spell VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 7 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-spells-secondary-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each spell's phases read as one effect and match its school motion signature (§2.3). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each spell's phases read as one effect and match its school motion signature (§2.3).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-spells-secondary-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.79 Generate P2 spell VFX via Codex CLI open
area:artasset:vfxclass:sorcererepic:e37milestone:m3needs-humanmw-e37.81Integrate approved P2 spell VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-spell-vfx-hooks,e08-spell-fire-wall,e08-spell-arcane-sight,e08-spell-snuff,e08-spell-stasis.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 7 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when each secondary spell is cast, then every phase plays from final textures with no console errors.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e08.16 Add Arcane Sight: reveal hidden mechanisms and magical properties open
- mw-e08.19 Add Snuff: extinguish all light sources in a radius open
- mw-e08.23 Add Stasis: freeze one object or creature in time open
- mw-e08.4 Add Fire Wall: a persistent line of flame that blocks and burns open
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.10 Spell VFX slots and templates for the spell pipeline done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.80 Owner approval: P2 spell VFX open
area:artasset:vfxclass:sorcererepic:e37milestone:m3mw-e37.98Write ambient environment VFX prompts (5 assets)
Description
Why
Ambient motion makes the hand-painted world feel alive and warm without costing gameplay budget.
What
Write one prompt file per asset in
assets/prompts/<category>/<asset-id>.md(category: vfx) from templatevfx-flipbook-texture.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Dust motes in light shafts, torch embers, fireflies, falling leaves and a painted fog-volume noise texture.
Asset ids (5):
vfx-env-dust-motes-01vfx-env-torch-embers-01vfx-env-fireflies-01vfx-env-falling-leaves-01vfx-env-fog-volume-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-vfx-environment-ambient-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
P2 to match
e29-environment-fx. Location-specific ambient VFX from the world list are out of scope here.Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs.Acceptance criteria
- AC-1 content Given the 5 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (512² sheets or single sprites, alpha background, loopable) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: ambient effects stay subtle (never mistaken for gameplay cues).
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:vfxepic:e37milestone:m3mw-e37.99Generate ambient environment VFX via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 5 prompt files ofe37-asset-vfx-environment-ambient-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-vfx-environment-ambient-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 512² sheets or single sprites, alpha background, loopable. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 5 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (512² sheets or single sprites, alpha background, loopable), then dimensions, transparency/background and grid layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
- mw-e37.98 Write ambient environment VFX prompts (5 assets) open
area:artasset:vfxepic:e37milestone:m3mw-e37.100Owner approval: ambient environment VFX
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 5 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-vfx-environment-ambient-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: ambient effects stay subtle (never mistaken for gameplay cues). Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 5 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then ambient effects stay subtle (never mistaken for gameplay cues).
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-vfx-environment-ambient-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.99 Generate ambient environment VFX via Codex CLI open
area:artasset:vfxepic:e37milestone:m3needs-humanmw-e37.101Integrate approved ambient environment VFX
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/UI logic (owned by the consuming beads)
Notes
Consumers:
e29-environment-fx.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 5 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the style-target diorama, when ambient VFX are enabled, then final textures render and VFX GPU time stays within its 2 ms share.
- AC-5 perf Given the rebuilt atlases/textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the UI/fonts download share stays ≤ 4 MB where applicable. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.14 Ambient environment VFX: dust motes, embers, fireflies, fog open
- mw-e37.100 Owner approval: ambient environment VFX open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:vfxepic:e37milestone:m3mw-e37.221Write harbour gull and rat models concept prompts (2 images)
Description
Why
Gulls that steal shinies and rats that give you away are ambient life, not creature types (story bible §8), but they make the harbour feel criminal and alive.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Harbour gull (white-grey, carries a stolen shiny with the pickup glint) and wharf rat (small, dark, scatters from light).
Concept image ids (2):
creature-ambient-gull-01creature-ambient-rat-01
Target model ids (2):
model-ambient-gull-01model-ambient-rat-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-ambient-life-gull-rat-3d-generate)
Notes
P2 to match
e40-harbor-ambient-life. The Gull Shoot uses wooden gull targets (waterfront kit), so the P1 contest does not wait on this set.Budget: ambient critter ≤ 1.5 k tris, LOD1 50 %, T0–T1 shared atlas, ≤ 30 bones, instanced. Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (ambient critter ≤ 1.5 k tris, LOD1 50 %, T0–T1 shared atlas, ≤ 30 bones, instanced) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: the gull reads cheeky rather than menacing with its stolen shiny glinting, and the rat reads small and dark without being grotesque.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.149 Owner approval: Wendmouth environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.222Generate harbour gull and rat models concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-ambient-life-gull-rat-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.221 Write harbour gull and rat models concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.223Owner approval: harbour gull and rat models flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ambient-life-gull-rat-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the gull reads cheeky rather than menacing with its stolen shiny glinting, and the rat reads small and dark without being grotesque. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the gull reads cheeky rather than menacing with its stolen shiny glinting, and the rat reads small and dark without being grotesque.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ambient-life-gull-rat-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.222 Generate harbour gull and rat models concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.224Generate harbour gull and rat models 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 2 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-ambient-life-gull-rat-turntable-approve)
Notes
Budget: ambient critter ≤ 1.5 k tris, LOD1 50 %, T0–T1 shared atlas, ≤ 30 bones, instanced. API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 2 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets ambient critter ≤ 1.5 k tris, LOD1 50 %, T0–T1 shared atlas, ≤ 30 bones, instanced, or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.223 Owner approval: harbour gull and rat models flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.225Owner approval: harbour gull and rat models turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 2 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-ambient-life-gull-rat-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the gull reads cheeky rather than menacing with its stolen shiny glinting, and the rat reads small and dark without being grotesque. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the gull reads cheeky rather than menacing with its stolen shiny glinting, and the rat reads small and dark without being grotesque.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-ambient-life-gull-rat-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.224 Generate harbour gull and rat models 3D models via image-to-3D API open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.226Integrate approved harbour gull and rat models
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e40-harbor-ambient-life.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 2 models are underpublic/assets/model/within ambient critter ≤ 1.5 k tris, LOD1 50 %, T0–T1 shared atlas, ≤ 30 bones, instanced and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the rooftops, when gulls and rats are spawned via the debug console, then final instanced models load and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.225 Owner approval: harbour gull and rat models turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.19 Add harbour gulls that steal shiny things and rats that give you away open
area:artasset:modelepic:e37milestone:m3mw-e37.231Write Tereza's sabre duel animations motion briefs (5 clips)
Description
Why
Tereza's first-blood duel is the knight's elegant set piece: a retired duellist who enjoyed it too much.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Guard stance, lunge, parry, riposte, and a yield salute when first blood is drawn.
Clip ids (5):
anim-tereza-guard-01anim-tereza-lunge-01anim-tereza-parry-01anim-tereza-riposte-01anim-tereza-yield-salute-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
P2 to match
e40-tereza-duel.Rig: canonical humanoid skeleton with the Saltmarch sabre socket. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 5 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.232Source and retarget Tereza's sabre duel animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-tereza-duel-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 5 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.191 Owner approval: Saltmarch weapon and arrow turntables open
- mw-e37.215 Owner approval: named harbour NPC characters turntables open
- mw-e37.231 Write Tereza's sabre duel animations motion briefs (5 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.233Owner approval: Tereza's sabre duel animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 5 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-tereza-duel-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the duel reads precise and elegant, lunge telegraphs match the e04 parry window, and the salute reads gracious. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 5 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the duel reads precise and elegant, lunge telegraphs match the e04 parry window, and the salute reads gracious.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-tereza-duel-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.232 Source and retarget Tereza's sabre duel animations open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.234Integrate approved Tereza's sabre duel animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e40-tereza-duel.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 5 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the Tidemarket duel, when the knight duels Tereza to first blood, then final clips play with hit markers matching the parry window and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e37.216 Integrate approved named harbour NPC characters open
- mw-e37.233 Owner approval: Tereza's sabre duel animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.11 Stage Tereza Maelo's first-blood duel open
area:artasset:modelepic:e37milestone:m3mw-e37.235Write gull and rat animations motion briefs (6 clips)
Description
Why
Gulls snatching shinies and rats scattering from light are only readable through motion.
What
Write one motion brief per clip in
assets/prompts/anim/<asset-id>.mdfromassets/prompts/_templates/animation-brief.md: clip id, target model and rig, source (CC0 library clip, or Meshy/Tripo auto-anim prompt), loop/one-shot, duration, root motion policy, sim marker events, consistency checklist and approval gate.Gull idle, fly, snatch and hoard; rat scurry and flee-from-light.
Clip ids (6):
anim-ambient-gull-idle-01anim-ambient-gull-fly-01anim-ambient-gull-snatch-01anim-ambient-gull-hoard-01anim-ambient-rat-scurry-01anim-ambient-rat-flee-01
Scope
- In: One brief per clip id
- In: Sim marker list per clip (footstep, hit-open/close, grab/release, sfx, vfx, interrupt-ok)
- Out: Retargeting tooling (
e37-animation-pipeline) - Out: Base locomotion (idle/walk/run) — comes with the model's auto-rig step or the pipeline base library
Notes
P2 to match
e40-harbor-ambient-life.Rig: gull and rat critter rigs from the approved ambient-life models. Timings must match the consuming beads' attack/interaction data.
Acceptance criteria
- AC-1 content Given the 6 clip ids, when the asset validator runs, then each has one brief whose id equals its filename stem and follows the
anim-<rig-or-creature>-<action>-<nn>pattern. - AC-2 content Given each brief, when inspected, then it states target model, rig, source, frame count, loop flag, root-motion policy and the required sim marker events per
animation-brief.md. - AC-3 content Given each brief sourcing from a library, when inspected, then the library licence is CC0 (Mixamo raw files never enter the repo); API-sourced briefs record provider and tier.
- AC-4 content Edge: given a brief with no marker for a clip that emits noise or contact (footfall, hit, grab), when validated, then the validator fails naming the clip.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
area:artasset:modelepic:e37milestone:m3mw-e37.236Source and retarget gull and rat animations
Description
Why
Claude produces every clip; the owner only approves.
What
Source or generate each clip per its brief (CC0 library, or the provider auto-anim API on the approved rigged model), retarget with
e37-animation-pipeline, add markers, and render preview GIFs on the target model withe37-turntable-preview.Scope
- In: Sourcing/generation, retarget, markers
- In: Preview GIF per clip
- Out: Owner approval (
e37-asset-anim-ambient-life-approve)
Notes
glb clip on the target rig, 30 fps, in-place with root velocity curve unless the brief says otherwise, loop flag and event markers per the clip manifest. API keys only from env vars.
Acceptance criteria
- AC-1 integration Given the 6 briefs, when the batch completes, then each clip exists in
assets/_incoming/<asset-id>/vN.glbwith a sidecar (source, licence or provider/tier, date). - AC-2 content Given the clip manifest entries, when validated, then loop flags, durations (±1 frame) and event markers match each brief, and looping clips have first/last pose delta below the pipeline threshold.
- AC-3 integration Given each clip, when previewed, then a GIF on the target model exists on the review page.
- AC-4 integration Edge: given the animation API or library download hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.11 Character animation pipeline with retargeting to a shared humanoid rig open
- mw-e37.225 Owner approval: harbour gull and rat models turntables open
- mw-e37.235 Write gull and rat animations motion briefs (6 clips) open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.237Owner approval: gull and rat animations
Description
Why
Every shipped asset needs an owner decision at the animation gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 6 preview GIFs and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: animation, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-anim-ambient-life-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the snatch reads as theft (shiny in beak), fly loops are seamless, and the rat flee reads as startled. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 6 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "animation". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the snatch reads as theft (shiny in beak), fly loops are seamless, and the rat flee reads as startled.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-anim-ambient-life-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.236 Source and retarget gull and rat animations open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.238Integrate approved gull and rat animations
Description
Why
Final clips replace placeholder poses without touching gameplay code.
What
Pack approved clips into the per-character clip sets via the clip manifest, add CREDITS + approval records, clear placeholder flags, and verify in-game.
Scope
- In: Clip manifest entries, CREDITS, approval records
- Out: Animation state machine logic (consuming beads,
e02-animation-system)
Notes
Consumers:
e40-harbor-ambient-life.Acceptance criteria
- AC-1 integration Given the approved clips, when the pipeline and validator run, then all 6 clips are in the runtime clip sets and the validator is green.
- AC-2 content Given
assets/CREDITS.md, when audited, then every clip has a CREDITS row with source and licence, and an animation approval record. - AC-3 e2e Given the rooftops, when a gull snatches a dropped coin and a rat is lit by a lantern, then final clips play with correct markers and no console errors.
- AC-4 integration Edge: given a clip missing its approval record, when the pipeline runs, then it is not packed and the placeholder pose remains.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e37.226 Integrate approved harbour gull and rat models open
- mw-e37.237 Owner approval: gull and rat animations open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.19 Add harbour gulls that steal shiny things and rats that give you away open
area:artasset:modelepic:e37milestone:m3mw-e37.259Write optional Briar Glen and slice concepts prompts (4 assets)
Description
Why
The restricted back room, the lychgate cemetery and the empty schoolhouse are MVP-desirable story spaces (P2 beads); the slice mood reference helps the m1 grey-box read as the same game.
What
Write one prompt file per asset in
assets/prompts/environment/<asset-id>.md(category: environment) from templateenvironment-concept.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Mirela's back room with *Nine Sleeps* on a locked lectern; the lychgate cemetery with Vesperine bell-marked graves and the Mooring family plot; the empty schoolroom with the rhyme on the slate (as brush-mark shapes, no legible text) and Tansy's reading corner; an optional mood reference for the m1 slice level.
Asset ids (4):
env-lamplit-stacks-back-room-01env-lychgate-cemetery-01env-schoolhouse-empty-01env-grey-box-slice-01
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-env-optional-concepts-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
P2 to match
e24-stacks-back-roomande24-cemetery-schoolhouse.env-grey-box-slice-01is optional and informse01-slice-greybox-levelonly as a mood reference (the slice ships on grey-box art).Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 4 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: each painting matches the approved Briar Glen concepts in palette and brushwork, keeps text as unreadable shapes, and the schoolhouse reads as absence (missing teacher) rather than threat.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.245 Owner approval: Briar Glen environment concepts open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:conceptepic:e37milestone:m3mw-e37.260Generate optional Briar Glen and slice concepts via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 4 prompt files ofe37-asset-env-optional-concepts-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-env-optional-concepts-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 4 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (16:9, 1536×864 generate, PNG, painterly concept — reference/mood target for grey-box and art-dress, not a runtime texture), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.259 Write optional Briar Glen and slice concepts prompts (4 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:conceptepic:e37milestone:m3mw-e37.261Owner approval: optional Briar Glen and slice concepts
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 4 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-env-optional-concepts-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each painting matches the approved Briar Glen concepts in palette and brushwork, keeps text as unreadable shapes, and the schoolhouse reads as absence (missing teacher) rather than threat. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each painting matches the approved Briar Glen concepts in palette and brushwork, keeps text as unreadable shapes, and the schoolhouse reads as absence (missing teacher) rather than threat.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-env-optional-concepts-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.260 Generate optional Briar Glen and slice concepts via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3needs-humanmw-e37.262Integrate approved optional Briar Glen and slice concepts
Description
Why
Approved environment concepts are the art-dress target: the art-dress, lighting and style-consistency beads compare in-engine screenshots against them.
What
Promote approved versions to
assets/source/environment/<asset-id>/, addassets/CREDITS.mdrows and approval records, register each concept as theartReferenceof its area in the area content data, and add it to the art-dress checklist ande37-style-consistency-reviewcomparison sheet.Scope
- In: Source promotion, CREDITS rows + approval records in the same commit
- In: Area
artReferenceentries and the art-dress comparison sheet - Out: Art-dressing the areas (art-dress beads); concepts are not runtime assets
Notes
Consumers:
e24-town-greybox,e24-stacks-back-room,e24-cemetery-schoolhouse. Lower-priority consumers (e24-stacks-back-room,e24-cemetery-schoolhouse) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved sources, when
e37-asset-validatorruns, then every id has a source folder, CREDITS row and a matching approval hash, and the validator is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 content Given the area content data for the back room, cemetery, schoolhouse and slice level, when schema-validated, then each area in this set names its concept id in
artReferenceand the id resolves to an approved asset. - AC-4 integration Given the art-dress comparison sheet generator, when it runs, then each concept is paired with the latest Playwright screenshot of its area from the same camera preset.
- AC-5 integration Edge: given an area whose
artReferencepoints at an id without an approval record, when CI runs, then the content check fails naming the area and id.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.261 Owner approval: optional Briar Glen and slice concepts open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:conceptepic:e37milestone:m3mw-e37.287Write secondary and historical portraits prompts (4 assets)
Description
Why
Snig and Mortimer are P2 social creatures, and the historical painted portraits (Sergeant Aldric Mooring, Ysolde Thorne) are optional in-world props that reward curiosity.
What
Write one prompt file per asset in
assets/prompts/portrait/<asset-id>.md(category: portrait) from templatenpc-portrait.md, with the GPT STYLE PREAMBLE (style bible §14) plus its §14.1 category add-on pasted verbatim, the output spec, the bible consistency checklist, consuming bead(s) and an empty iteration log.Snig (small, quick, a stolen primer tucked away), Mortimer (a plump, smug book with a red ribbon tongue), and two in-world painted portraits in period frames: Sergeant Aldric Mooring (for Mooring's Watch) and Ysolde Thorne as a Vesperine novice (for the Lamplit Stacks).
Asset ids (4):
portrait-npc-snig-goblinportrait-npc-mortimer-mimicportrait-npc-mooring-sergeantportrait-npc-ysolde-novice
Scope
- In: One prompt file per asset id above
- In: Consuming bead references and anchor/reference inputs (our own approved assets only)
- In: Output spec per template
- Out: Running the generator (see
e37-asset-portraits-secondary-generate) - Out: Changing a preamble (bible bead + version bump only)
Notes
P2: optional portraits. The historical portraits are hung as framed picture props (texture on a frame model from the town kits).
Prompts cite the locked style anchors from
e37-asset-style-lockas reference inputs. Canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 4 asset ids listed in this bead, when the asset validator runs over
assets/prompts/, then every id has exactly one prompt file whose asset id equals its filename stem and passes naming checks (<category>-<subject>-<variant>, ≤ 64 chars). - AC-2 content Given each prompt file, when the validator compares its preamble block with docs/art/style-bible.md §14, then it matches byte-for-byte and the correct §14.1 category add-on follows it.
- AC-3 content Given each prompt file, when inspected, then it names its consuming bead(s), output spec (1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background) and the filled consistency checklist, with no unfilled
<…>placeholders. - AC-4 content Given the set, when reviewed against the bible, then every prompt writes out the palette hexes, shape language and silhouette rules needed to meet the approval criterion: Snig and Mortimer match the approved cast portraits in style, and the two historical portraits read as old oil paintings in the world (varnish, frame) without legible text.
- AC-5 content Edge: given a prompt that asks for text/letters, names an artist, studio or game, uses a non-canon name for a story-bible subject, or contains an API key pattern, when the validator runs, then it fails and names the file and line.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.285 Owner approval: named cast portraits open
- mw-e37.5 Review and finalize image prompt templates open
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
area:artasset:portraitepic:e37milestone:m3mw-e37.288Generate secondary and historical portraits via Codex CLI
Description
Why
Claude produces every image candidate so the owner only has to approve (contract §6).
What
Run
e37-image-gen-runner(codex exec→ gpt-image; fallback OpenAI Images API) for all 4 prompt files ofe37-asset-portraits-secondary-prompts. Outputs land inassets/_incoming/<asset-id>/vN.pngwith avN.jsonsidecar; Claude self-checks each candidate against its checklist, iterates, logs every iteration, then builds a batch review page for the owner.Scope
- In: Batch generation in planned batches respecting subscription limits
- In: Self-check + iteration log per asset
- In: Batch review page (contact sheet) for the approval bead
- Out: Owner approval (
e37-asset-portraits-secondary-approve) - Out: Committing final assets (integrate bead)
Notes
Output spec: 1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background. Record account tier in every sidecar — restrictive-tier outputs are placeholders only.
Acceptance criteria
- AC-1 integration Given the 4 prompt files, when the runner batch completes, then
assets/_incoming/<asset-id>/vN.pngand avN.jsonsidecar (prompt path, tool, model, account tier, date) exist for every id. - AC-2 content Given each candidate, when checked against its output spec (1024² generate → 512²/256² PNG → KTX2 UASTC, bust framing with head-room, softly blurred location-coloured background), then dimensions, transparency/background and layout match or the iteration log records why it was regenerated.
- AC-3 content Given the batch review page, when opened, then it shows every id with its latest version, the prompt path and the checklist result.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
- AC-5 integration Given all sidecars and iteration logs, when the secret scanner runs, then no API key or token is found.
Waits on
- mw-e37.287 Write secondary and historical portraits prompts (4 assets) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:portraitepic:e37milestone:m3mw-e37.289Owner approval: secondary and historical portraits
Description
Why
Every shipped asset needs an owner decision at the image gate; CI refuses final assets without one (contract §6).
What
The owner reviews the batch review page of 4 candidates and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: image, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-portraits-secondary-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Snig and Mortimer match the approved cast portraits in style, and the two historical portraits read as old oil paintings in the world (varnish, frame) without legible text. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "image". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Snig and Mortimer match the approved cast portraits in style, and the two historical portraits read as old oil paintings in the world (varnish, frame) without legible text.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-portraits-secondary-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.288 Generate secondary and historical portraits via Codex CLI open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:portraitepic:e37milestone:m3needs-humanmw-e37.290Integrate approved secondary and historical portraits
Description
Why
Final art replaces placeholders without touching gameplay code (contract §5: integration is a file swap).
What
Promote approved versions to
assets/source/<category>/<asset-id>/, run the pipeline (atlas/KTX2 build as applicable), addassets/CREDITS.mdrows and approval records, replace placeholder entries, and verify in-game.Scope
- In: Source promotion, pipeline run, atlas rebuild
- In: CREDITS rows + approval records in the same commit
- In: Clearing placeholder flags for this set
- Out: Gameplay/level logic (owned by the consuming beads)
Notes
Consumers:
e22-dialogue-ui,e25-mooring-watch-encounters,e24-lamplit-stacks,e14-snig,e14-mortimer. Lower-priority consumers (e14-snig,e14-mortimer) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e37-asset-validatorrun, then runtime files exist underpublic/assets/for all 4 ids and the validator (naming, budgets, licence, approval hash) is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool, model, account tier, date, prompt path and terms URL. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Snig's and Mortimer's dialogues and the two framed portraits in the world, when rendered, then final portraits show with no placeholder flags and no console errors.
- AC-5 perf Given the rebuilt textures, when
e32-perf-budgetsruns, then resident texture memory stays ≤ 256 MB on Low and the owning location package stays ≤ 25 MB. - AC-6 integration Edge: given an id in this set without an approval record, when the pipeline runs, then it refuses to promote that id, the placeholder stays in place, and the remaining-placeholder report lists it.
Waits on
- mw-e22.10 Build accessible dialogue UI with choices, tags and history done
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
- mw-e25.7 Place the Hollow Sentinels and goblin lookout with class resolutions open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.289 Owner approval: secondary and historical portraits open
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
area:artasset:portraitepic:e37milestone:m3mw-e37.319Select CC0 kit pieces for cemetery and schoolhouse props (7 models)
Description
Why
The lychgate cemetery (Vesperine graves, the Mooring plot) and the empty schoolhouse are quiet lore spaces for the P2 story beads.
What
Bulk props come from CC0 kits recoloured to the palette, not from generation (contract §1, style bible §6.1 and §17.2). Write one kit brief per brief id in
assets/prompts/prop/<brief-id>.mdfromprop-sheet.md(asset table, brief, target model list, output spec, consistency checklist, iteration log) — no image is generated. Instead Claude selects matching pieces from CC0-only kits, records source URL + licence file per piece, maps each piece to a target model id, plans the palette-atlas recolour (source colour → palette token hex from style bible §2.1/§2.2) and lists any piece no kit supplies as a gap fore37-asset-props-world-gapfill-*.Lychgate, Vesperine headstones with a small bell mark (two variants), the Mooring family plot, school desks, the slate board (rhyme rendered by the game font) and a stack of children's books.
Kit brief ids (2):
prop-cemetery-set-01prop-school-set-01
Target model ids (7):
model-prop-lychgate-01model-prop-headstone-vesperine-01model-prop-headstone-vesperine-02model-prop-mooring-plot-01model-prop-school-desk-01model-prop-slate-board-01model-prop-books-children-01
Scope
- In: Kit brief per brief id; piece → model id mapping with licence
- In: Recolour map and interaction sockets needed by consumers (breakable, liftable, hide)
- In: Gap list handed to the gap-fill chain
- Out: Download/recolour/import (
e37-asset-props-cemetery-school-kit-kit-recolour)
Notes
P2 to match
e24-cemetery-schoolhouse. Gaps here are sourced CC0-only or cut; the P1 gap-fill chain does not wait on this set.Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). Allowed sources: CC0 only (Kenney, Quaternius, KayKit-style packs verified CC0, OpenGameArt CC0) per style bible §16; no CC-BY-SA/NC, no "royalty-free, no redistribution".
Acceptance criteria
- AC-1 content Given the 2 brief ids, when the asset validator runs, then each has exactly one kit brief whose id equals its filename stem and passes naming checks.
- AC-2 content Given the 7 target model ids, when the briefs are audited, then every id maps to exactly one CC0 source piece (kit, piece name, source URL, licence file path) or appears in the gap list.
- AC-3 content Given each recolour map, when validated, then every target colour is a palette hex from style bible §2.1 or the location row in §2.2.
- AC-4 content Given each brief, when inspected, then it records the §13.2 budget (small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2)) and which legibility cues from style bible §7 each interactive piece must carry.
- AC-5 content Edge: given a piece whose licence is not CC0 (CC-BY-SA, NC, ND, unknown provenance), when the validator runs, then it fails naming the piece and brief.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.261 Owner approval: optional Briar Glen and slice concepts open
- mw-e37.6 Spike: 3D sourcing — CC0 kits plus Meshy vs Tripo API bake-off open
- mw-e37.7 Placeholder asset kit: CC0 stand-ins at correct scale and naming open
area:artasset:modelepic:e37milestone:m3mw-e37.320Recolour cemetery and schoolhouse props from CC0 kits
Description
Why
Kits only look like one game once they share the palette atlas and the stylized material (style bible §6).
What
Download the selected kits unmodified to
assets/kits/<kit>/with their licence files, derive each target model intoassets/_incoming/<asset-id>/vN.glb(palette-atlas UV remap, merge to budget, pivots/colliders, interaction sockets and breakable fracture pieces where the brief asks), run automated validation, and render turntables/contact sheets withe37-turntable-preview.Scope
- In: Kit download with licence, recolour, merge, sockets
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model, one contact sheet per brief
- Out: Owner approval (
e37-asset-props-cemetery-school-kit-kit-approve)
Notes
Budget: small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2). The palette remap is scripted in the model pipeline;
e37-texture-atlas-build(P2) may later replace the script without changing ids.Acceptance criteria
- AC-1 integration Given the approved briefs, when the recolour script runs, then each of the 7 model ids has a
vN.glb+ sidecar (kit, piece, source URL, licence, date) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and uses only the shared prop atlas material.
- AC-3 integration Given
assets/kits/, when audited, then every kit folder is byte-identical to its download and contains its licence file. - AC-4 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist, plus one contact sheet per brief on the review page. - AC-5 integration Edge: given a model whose sampled albedo colours fall outside the palette (ΔE > 3 from every palette hex), when validated, then it is flagged on the review page and not offered for approval.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.319 Select CC0 kit pieces for cemetery and schoolhouse props (7 models) open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.321Owner approval: cemetery and schoolhouse props kit turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the recoloured kit turntables and contact sheets of the 7 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-props-cemetery-school-kit-kit-recolourwith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Vesperine headstones carry the bell mark (not text), the Mooring plot is findable as a landmark, and the schoolroom reads as abandoned mid-lesson. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Vesperine headstones carry the bell mark (not text), the Mooring plot is findable as a landmark, and the schoolroom reads as abandoned mid-lesson.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-props-cemetery-school-kit-kit-recolourwith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.320 Recolour cemetery and schoolhouse props from CC0 kits open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.322Integrate approved cemetery and schoolhouse props
Description
Why
Final props replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs/impostors), bind to the stylized material, wire model ids into the content data of the consuming areas, add CREDITS rows (kit, licence, source URL) and approval records, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + turntable approval records
- In: Placeholder swap
- Out: Placement and level logic (consuming beads)
- Out: Gap pieces (
e37-asset-props-world-gapfill-integrate)
Notes
Consumers:
e24-cemetery-schoolhouse.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 7 models are underpublic/assets/model/within small props 50–400 tris, medium 400–2 k, large 2–5 k (LOD1 50 %), trees ≤ 3 k + impostor, prop atlas, one shared material per set (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (kit, piece, licence, source URL, date) and a turntable approval record. - AC-3 e2e Given the cemetery and schoolhouse, when Playwright loads them, then final props render with no placeholder models and no console errors.
- AC-4 perf Given the owning area with these props placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low, with this set using one material. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e24.11 Build the lychgate cemetery and the empty schoolhouse open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.321 Owner approval: cemetery and schoolhouse props kit turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3mw-e37.393Write Snig and Mortimer creatures concept prompts (2 images)
Description
Why
Snig (the pickpocketing goblin who wants to read) and Mortimer (the tame book mimic) are P2 social characters with a lot of charm per polygon.
What
Write one prompt file per concept image in
assets/prompts/<category>/<asset-id>.md(category: creature) fromcreature-reference-sheet.md, flat front-facing on plain parchment so the image-to-3D API gets clean input, with the GPT STYLE PREAMBLE (style bible §14) verbatim, output spec and consistency checklist.Snig: small, quick, sticky-fingered, a stolen primer tucked somewhere, on the goblin rig; Mortimer: a plump, well-oiled, slightly smug book with a red ribbon tongue.
Concept image ids (2):
creature-goblin-snig-01creature-mimic-mortimer-01
Target model ids (2):
model-creature-goblin-snig-01model-creature-mimic-mortimer-01
Scope
- In: Concept/reference prompt per id above
- In: Model output spec (tri budget, texture, bones) recorded in each prompt
- Out: 3D generation (
e37-asset-creatures-social-secondary-3d-generate)
Notes
P2 to match
e14-snigande14-mortimer. Clips reuse the goblin and mimic sets (e37-asset-anim-goblins-*,e37-asset-anim-constructs-*).Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2). Prompts cite the locked style anchors from
e37-asset-style-lock; canon names and details followdocs/narrative/story-bible.md.Acceptance criteria
- AC-1 content Given the 2 concept ids, when the asset validator runs, then each has exactly one prompt file whose id equals its filename stem and passes naming checks.
- AC-2 content Given each prompt file, when compared with docs/art/style-bible.md §14 and §14.1, then the preamble and category add-on match byte-for-byte.
- AC-3 content Given each prompt file, when inspected, then it records the §13.2 model budget (standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2)) and the silhouette/palette rules from §5 relevant to the asset.
- AC-4 content Given the set, when reviewed against the bible, then every prompt spells out what the approval criterion needs: Snig reads smaller and quicker than other goblins with the primer visible, and Mortimer reads as a friendly version of the book mimic.
- AC-5 content Edge: given a prompt asking for perspective views, cast shadows or busy backgrounds that break image-to-3D, or containing text requests, a non-canon name or an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
- mw-e37.142 Lock approved style anchor images for all later image prompts open
- mw-e37.289 Owner approval: secondary and historical portraits open
- mw-e37.371 Owner approval: Rootcellar goblin creatures flat concepts open
- mw-e37.383 Owner approval: bestiary creatures flat concepts open
- mw-e37.5 Review and finalize image prompt templates open
area:artasset:modelepic:e37milestone:m3mw-e37.394Generate Snig and Mortimer creatures concepts via Codex CLI
Description
Why
Image-to-3D quality depends on a clean, approved flat concept (style bible §17.2).
What
Run
e37-image-gen-runnerfor all 2 concept prompts intoassets/_incoming/<asset-id>/vN.png+ sidecar; self-check against the checklist (silhouette at 64 px, palette, flat lighting) and iterate; build the review page.Scope
- In: Concept generation and iteration log
- In: Silhouette test renders (solid ink at 64 px) on the review page
- Out: Owner approval (
e37-asset-creatures-social-secondary-concept-approve)
Notes
Record account tier in every sidecar.
Acceptance criteria
- AC-1 integration Given the 2 prompts, when the batch completes, then every id has
vN.png+vN.json(prompt path, tool, model, tier, date) inassets/_incoming/. - AC-2 content Given each candidate, when auto-checked, then it is front-facing on a plain parchment background with no cast shadows, or the iteration log records a regeneration.
- AC-3 content Given the review page, when opened, then each candidate shows alongside its solid-ink 64 px silhouette.
- AC-4 integration Edge: given the image runner hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e37.393 Write Snig and Mortimer creatures concept prompts (2 images) open
- mw-e37.4 Image generation runner wrapping codex exec with Images API fallback open
area:artasset:modelepic:e37milestone:m3mw-e37.395Owner approval: Snig and Mortimer creatures flat concepts
Description
Why
Every shipped asset needs an owner decision at the flat concept gate; CI refuses final assets without one (contract §6).
What
The owner reviews the 2 flat concept images and their silhouette tests and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: flat concept, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-social-secondary-concept-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Snig reads smaller and quicker than other goblins with the primer visible, and Mortimer reads as a friendly version of the book mimic. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "flat concept". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Snig reads smaller and quicker than other goblins with the primer visible, and Mortimer reads as a friendly version of the book mimic.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-social-secondary-concept-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.394 Generate Snig and Mortimer creatures concepts via Codex CLI open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.396Generate Snig and Mortimer creatures 3D models via image-to-3D API
Description
Why
The owner never does 3D work; Claude turns approved flats into game-ready models (contract §1).
What
For each of the 2 target models: call
e37-3d-gen-api-client(Meshy or Tripo pere37-3d-pipeline-decision) with the approved flat, remesh/retopo to budget, rig/animate via API where applicable, download toassets/_incoming/<asset-id>/vN.glb, run automated validation, and render turntables withe37-turntable-preview.Scope
- In: API jobs, retopo, rig
- In: Automated budget/naming/licence validation
- In: Turntable PNG strip + GIF per model
- Out: Turntable approval (
e37-asset-creatures-social-secondary-turntable-approve)
Notes
Budget: standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2). API keys only from
MESHY_API_KEY/TRIPO_API_KEYenv vars.Acceptance criteria
- AC-1 integration Given the approved flats, when the API client runs, then each of the 2 model ids has a
vN.glb+ sidecar (provider, model version, tier, job id, date, source image hash) inassets/_incoming/. - AC-2 integration Given each glb, when validated, then it meets standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2), or the job is re-run with adjusted retopo settings and the iteration log says why.
- AC-3 integration Given each glb, when
e37-turntable-previewruns, then a PNG strip (8 angles) and a GIF exist for the review page. - AC-4 integration Edge: given an API job that fails or times out, when the client retries with back-off, then at most 3 attempts are made, no duplicate paid job is started for an id with a completed job, and the failure is reported per id.
- AC-5 integration Given sidecars and logs, when the secret scanner runs, then no API key appears.
Waits on
- mw-e37.10 Headless turntable preview of a glb as PNG strip and GIF open
- mw-e37.395 Owner approval: Snig and Mortimer creatures flat concepts open
- mw-e37.9 Image-to-3D API client and CLI for the chosen provider open
area:artasset:modelepic:e37milestone:m3mw-e37.397Owner approval: Snig and Mortimer creatures turntables
Description
Why
Every shipped asset needs an owner decision at the turntable gate; CI refuses final assets without one (contract §6).
What
The owner reviews the turntable strips/GIFs of the 2 models and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: turntable, version, SHA-256, approver, date, notes). Rejected items loop back toe37-asset-creatures-social-secondary-3d-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Snig reads smaller and quicker than other goblins with the primer visible, and Mortimer reads as a friendly version of the book mimic. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 2 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "turntable". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Snig reads smaller and quicker than other goblins with the primer visible, and Mortimer reads as a friendly version of the book mimic.
- AC-3 manual Given each turntable, when compared with its approved flat concept, then proportions, palette and signature shapes match and there are no holes, floating geometry or smeared textures.
- AC-4 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e37-asset-creatures-social-secondary-3d-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-5 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e37.396 Generate Snig and Mortimer creatures 3D models via image-to-3D API open
area:artasset:modelepic:e37milestone:m3needs-humanmw-e37.398Integrate approved Snig and Mortimer creatures
Description
Why
Final models replace greybox stand-ins without touching gameplay code (contract §5).
What
Promote approved glbs through
e37-model-import-pipeline(meshopt + KTX2, LODs), bind to the stylized material, wire into content data, add CREDITS rows and approval records for both gates, and swap out the placeholder models.Scope
- In: Import pipeline, LODs, material binding
- In: CREDITS + approval records (flat concept + turntable)
- In: Placeholder swap
- Out: Gameplay logic (consuming beads)
Notes
Consumers:
e14-snig,e14-mortimer.Acceptance criteria
- AC-1 integration Given the approved glbs, when the import pipeline and
e37-asset-validatorrun, then all 2 models are underpublic/assets/model/within standard creature 2–6 k tris, LOD1 50 %, LOD2 20 %, T1 512², 1 material, ≤ 65 bones (style bible §13.2) and the validator is green. - AC-2 content Given
assets/CREDITS.mdandassets/approvals.json, when audited, then every model has a CREDITS row (provider, tier, date, prompt path, terms URL) and approval records for both the flat-concept and turntable gates. - AC-3 e2e Given the Deepworks and the Lamplit Stacks, when Snig and Mortimer are spawned, then final models load on the shared rigs and no console errors occur.
- AC-4 perf Given the owning area with these models placed, when
e32-perf-budgetsruns, then draw calls stay ≤ 150 and visible triangles ≤ 500 k on Low. - AC-5 integration Edge: given a model with a turntable approval but a changed file hash, when CI runs, then integration is refused and the placeholder remains.
Waits on
- mw-e14.4 Bring Snig to life: pocket-picking, reading lessons and vouching open
- mw-e14.7 Add Mortimer, the tame book mimic who guards the rare shelf open
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
- mw-e37.397 Owner approval: Snig and Mortimer creatures turntables open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
area:artasset:modelepic:e37milestone:m3
E38Audio Direction & Asset PipelineThe Vesper Bell sounds like one world — acoustic dark-folk music (hurdy-gurdy, nyckelharpa, low strings, wordless choir) that adapts to exploration, stealth, tension and combat, and tactile SFX whose loudness and character match the stealth/sound simulation — produced with Suno and a decided SFX source through a validated, licence-tracked pipeline.4/181 done · 2%
M1 Grey-box slice 5
mw-e38.2Spike: SFX sourcing — ElevenLabs Sound Effects API vs CC0
Description
Why
We need hundreds of SFX (footsteps × surfaces × gaits, 13 spell schools, creature states, UI) with clean licences for a public repo and consistent character.
What
Time-box 1 day. Question: which source (or hybrid) gives the best quality/consistency/licence-safety per hour for our SFX list? Claude produces the same 12-sound test set via (a) the ElevenLabs Sound Effects API with the SFX PREAMBLE (end-to-end, no owner generation work) and (b) CC0 libraries (Kenney, freesound CC0-only filter, OpenGameArt CC0), scores them, the owner listens at a recorded SFX-batch gate, and an ADR records the decision.
Scope
- In: 12-sound test set: stone walk ×3 round-robin, gravel run, wood door creak, sword parry metal, arrow release, fire cast, frost impact, skeleton collapse, goblin chatter, minotaur bellow (angry + calm), UI page turn
- In: Scoring: fit to audio bible palette, dryness/cleanliness, round-robin variety, time per usable sound, licence clarity for a public repo, cost
- In: Licence/terms review per source and tier
- Out: Producing the full SFX set (later prompt/generate beads)
- Out: Import pipeline (see
e38-audio-import-pipeline)
Notes
Recommendation going in: ElevenLabs Sound Effects API as primary — official API lets Claude generate end-to-end from the template (text prompt, duration, prompt influence) and the owner only approves by listening; strongest for signature sounds (13 schools of magic, the minotaur's dual-character vocals, goblin gibberish, creature state sets). CC0 libraries as fallback/bulk (Kenney, freesound CC0-only, OpenGameArt CC0) for footsteps, UI, doors and ambience where they are better or cheaper. Use a paid ElevenLabs tier for shipping sounds (free tiers typically require attribution and/or restrict commercial use — verify at spike time). Avoid "royalty-free, no redistribution" libraries for files committed to a public repo. Key from
ELEVENLABS_API_KEYenv only. Feedse38-sfx-gen-client.Acceptance criteria
- AC-1 manual Given the 12-sound test set produced from each source, when scored with the rubric, then an ADR in
docs/adr/records per-sound scores, time spent, cost and the chosen approach per SFX category. - AC-2 manual Given each candidate source and tier, when licence terms are reviewed, then the ADR quotes the relevant clauses (with URL and date) on redistribution in a public repository and attribution.
- AC-3 content Given the test sounds committed as placeholders, when validated, then each has a CREDITS row with source URL/tool, tier, date and licence.
- AC-4 manual Given the minotaur angry and calm bellows, when played back-to-back, then the ADR records whether the chosen source can produce the "same creature, two emotions" requirement from audio bible §7.5.
- AC-5 manual Edge: given a CC0 library sound whose licence metadata is ambiguous or later changed, when found, then it is excluded and the ADR notes the rule "CC0 confirmed on the file page at download time, screenshot or archived URL kept".
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
area:sfxasset:sfxepic:e38milestone:m1mw-e38.3Review and finalize music, stinger and SFX prompt templates
Description
Why
Per-asset music and SFX prompt beads copy these templates; they must encode key/BPM family locking, loudness and loop rules so every asset arrives pipeline-ready.
What
Review
assets/prompts/_templates/music-track.md,music-stinger.mdandsfx.mdagainst the signed-off audio bible and the SFX tool ADR; add one worked example each (music-briar-glen-day-explore,music-stinger-combat-start-d-dorian,sfx-foot-stone-walk-01..06).Scope
- In: Template edits
- In: Three worked examples
- In: Suno UI field names updated to the current product
- Out: Per-asset music/SFX prompt beads (later wave)
Notes
The SFX template must support both library sourcing (search brief + per-file provenance) and generation (SFX PREAMBLE).
Acceptance criteria
- AC-1 content Given the three audio templates, when inspected, then each contains asset id, consuming bead(s), filled prompt with preamble placeholder, output spec (key/BPM/length/loop/format/LUFS), consistency checklist, iteration log and provenance table.
- AC-2 manual Given the worked music example, when run in Suno, then the result matches the requested key and BPM (verified with an analyser) and contains no vocals.
- AC-3 manual Given the worked SFX example, when sourced per the ADR, then 6 round-robin footsteps pass the audio validator and the loudness ordering sneak < walk < run holds.
- AC-4 manual Edge: given a Suno UI change that renames or removes a field referenced by a template (e.g. Exclude styles), when discovered, then the template documents the fallback (append negatives to Style).
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.2 Spike: SFX sourcing — ElevenLabs Sound Effects API vs CC0 open
area:musicarea:sfxepic:e38milestone:m1mw-e38.4Audio validator: loudness, true peak, loop seams, naming and licence
Description
Why
Loudness and loop quality are invisible in code review and easy to get wrong across many files and tools; a clicking loop or a 6 dB-hot SFX ruins feel.
What
As a developer, I want
pnpm assets:validateto check audio against the audio bible so that CI rejects files outside loudness targets, with bad loop seams, wrong channel counts or missing credits. Checks: integrated LUFS and true peak per category (BS.1770 K-weighting, gating); leading silence for one-shots; mono for positional SFX; sidecar JSON schema (bpm, beatsPerBar, loop points); loop length = whole bars (± 1 ms); seam discontinuity (sample delta + short-window RMS jump); naming + CREDITS (reusinge37-asset-validator).Scope
- In: Pure TS loudness meter (BS.1770-4) and loop analysis over decoded PCM
- In: Category targets as data (shared with the bible)
- In: Sidecar zod schema
- In: CI integration
- Out: Encoding/normalising (see
e38-audio-import-pipeline) - Out: Runtime mixing (see
e28-audio-engine)
Notes
Owner requirement: 100 % tested. Test fixtures: synthetic sines/noise with known LUFS (e.g. 1 kHz sine at −20 dBFS ≈ −20.0 LUFS mono per spec reference) and deliberately clicking loops.
Acceptance criteria
- AC-1 unit Given the EBU reference 1 kHz stereo sine at −23 dBFS, when measured, then integrated loudness is −23.0 ± 0.1 LUFS.
- AC-2 unit Given a music file measured at −14 LUFS in the
music-explorecategory (target −18 ± 1), when validated, then it fails with actual vs target. - AC-3 unit Given a loop whose end sample jumps by > 0.1 full-scale versus its start, or whose length is not a whole number of bars at its sidecar BPM, when validated, then it fails naming the seam or length error.
- AC-4 unit Given a positional SFX file with 2 channels or > 10 ms leading silence, when validated, then it fails with the specific rule.
- AC-5 unit Edge: given a silent file, a truncated/corrupt file or a sidecar with loopEnd ≤ loopStart, when validated, then each produces a clear error (no NaN loudness, no crash) and validation continues with other files.
- AC-6 unit Given the validator source, when coverage runs, then lines, branches, functions and statements are all 100 %.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e37.2 Asset validator: naming, budgets and licence entries in CI open
area:sfxarea:toolsepic:e38milestone:m1mw-e38.5Audio import pipeline: normalise, encode Opus/AAC, loop metadata
Description
Why
Suno, SFX tools and libraries deliver inconsistent sample rates, levels and formats; the browser needs small Opus files with AAC fallback and sample-accurate loops.
What
As a developer, I want
pnpm assets:buildto ingest the owner's Suno takes fromassets/_incoming/music/<asset-id>.wav(and generated/CC0 SFX fromassets/_incoming/<asset-id>/), promote approved files toassets/source/, and process them intopublic/assets/audio/so that every file is 48 kHz, loudness-normalised to its category target, trimmed, encoded (Opus .ogg + AAC .m4a) and accompanied by a sidecar JSON with loop points compensated for encoder pre-skip/priming, then validated.Scope
- In: ffmpeg-based (pinned) node script: resample, trim, normalise (two-pass loudnorm), encode
- In: Pre-skip/priming compensation for loop points
- In: Audio manifest (asset id → URLs, duration, bytes, loop info)
- In: Incremental cache
- Out: Loudness/loop checking logic (see
e38-audio-validator, invoked as final step) - Out: Playback (see
e28-audio-engine)
Notes
Opus pre-skip is typically 312 samples at 48 kHz; read the actual value from the Ogg header. Bitrates per audio bible §5.2. Initial-load audio ≤ 8 MB.
Acceptance criteria
- AC-1 integration Given a 44.1 kHz WAV music source at −10 LUFS, when built, then outputs are 48 kHz Opus .ogg and AAC .m4a measuring within the category target ± 1 LU and ≤ its true-peak ceiling.
- AC-2 integration Given a source with sidecar loop points, when built and decoded, then the decoded Opus loop region matches the source loop region sample-for-sample within ± 1 sample after pre-skip compensation.
- AC-3 e2e Given a built loop played in the browser via the audio engine test page for 3 iterations, when recorded, then no seam discontinuity is detected by the validator's seam check.
- AC-4 integration Given the built audio manifest, when summed for files tagged
initial, then the total is ≤ 8 MB, otherwise the build fails. - AC-5 integration Edge: given a source that is clipped (true peak > 0 dBTP) or mostly silent, when built, then the build reports it and does not normalise silence upward beyond +20 dB gain.
Waits on
- mw-e38.4 Audio validator: loudness, true peak, loop seams, naming and licence open
area:musicarea:toolsepic:e38milestone:m1mw-e38.6Placeholder audio kit: CC0 stand-ins with final asset ids
Description
Why
The m1 vertical slice needs feedback sounds (footsteps, hits, UI, a loop) to judge feel; final audio must not block gameplay.
What
Assemble CC0 placeholders named with their final asset ids: footsteps for stone/wood/dirt/grass (walk/run/sneak, 4 round-robins), melee hit/block/parry, bow draw/release, one spell cast/impact, door/lever/chest, skeleton/goblin vocals, UI set, one ambience bed and one placeholder music loop — processed by the import pipeline.
Scope
- In: CC0 selection (Kenney, freesound CC0-only)
- In: Final-format ids + CREDITS rows flagged placeholder
- In: Catalogue listing id → placeholder
- Out: Final audio (per-asset beads)
- Out: Audio engine wiring (Related: e28-audio-engine consumers)
Notes
Keep placeholder loudness on-target so mix decisions made in m1/m2 still hold when finals arrive.
Acceptance criteria
- AC-1 content Given the catalogue, when validated, then every placeholder has a final-format id, passes loudness/format checks and has a CC0 CREDITS row with
placeholder: yes. - AC-2 integration Given the footstep set, when measured, then mean loudness orders sneak < walk < run for every surface.
- AC-3 e2e Given the greybox testbed with the audio engine, when the player walks across each placeholder surface, then the matching surface sound plays (verified via the audio debug log).
- AC-4 integration Edge: given an asset id referenced by content data with no placeholder or final file, when content validation runs, then it fails naming the missing id.
Waits on
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
area:musicarea:sfxepic:e38milestone:m1
M2 Class fantasies 17
mw-e38.7ElevenLabs Sound Effects API client for Claude-run SFX generation
Description
Why
With an official API, Claude can generate SFX end-to-end from the template while the owner just listens and approves — SFX "generate" tasks become Claude-run.
What
As a developer, I want
pnpm assets:gen-sfx <prompt-file>to call the ElevenLabs Sound Effects API with the SFX PREAMBLE + template prompt (duration, prompt influence, round-robin count), save each take toassets/_incoming/<asset-id>/vN.wav(or the API's format converted to WAV) withvN.jsonprovenance, run the audio import pipeline and validator, and build a batch listening page/playlist for the owner's SFX-batch approval gate.Scope
- In: Client in
src/tools/assets/sfx-gen/with HTTP behind an injectable interface - In: Round-robin generation (n takes per sound)
- In: Retry/backoff on 429/5xx, character/credit usage recorded
- In: Batch preview HTML listing takes with approve/reject commands
- Out: Approval recording (see
e37-asset-approval-log) - Out: CC0 library sourcing (manual/Claude-scripted per ADR)
Notes
Only if
e38-sfx-tool-decisionconfirms ElevenLabs. Key only fromELEVENLABS_API_KEYenv — never logged, committed or written to vN.json. 100 % coverage with HTTP mocked (no live calls in CI). Refuse to run if the prompt file preamble differs from audio bible §8.Acceptance criteria
- AC-1 unit Given a filled SFX prompt file with round-robin count 4 and a mocked API returning audio, when
gen-sfxruns, then 4 takes are saved as v1..v4 with vN.json provenance (tool, model, duration, date) and the validator is invoked on each. - AC-2 unit Given a prompt file whose SFX PREAMBLE differs from the audio bible, when run, then it refuses before any HTTP call.
- AC-3 unit Given 429 then success, when called, then the client backs off (injected clock) and succeeds, recording attempts.
- AC-4 integration Given a completed batch, when the preview page is generated, then it lists every take with its asset id, loudness and an approve/reject command line.
- AC-5 unit Edge: given a missing
ELEVENLABS_API_KEYor an error response body echoing headers, when logs are captured, then the key value never appears and the missing-key case exits non-zero with setup instructions. - AC-6 unit Given the client module, when coverage runs, then all four metrics are 100 %.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.2 Spike: SFX sourcing — ElevenLabs Sound Effects API vs CC0 open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
area:sfxarea:toolsasset:sfxepic:e38milestone:m2- In: Client in
mw-e38.8Spike: prove a key/BPM-locked Suno family with bar-synced cuts
Description
Why
Adaptive music (explore/stealth/tension/combat) is core to stealth feel, but Suno outputs full mixes of unpredictable key/length; we must prove a workable method before producing ~12 location families.
What
Time-box 1.5 days. Using the labyrinth family (E Phrygian, ~58/116 BPM), generate an explore anchor and derive stealth/tension/combat variants via Extend/Cover/audio-reference; test stem export/separation if available on the tier; cut loop points; prototype bar-synced crossfades + stingers in the audio engine test page. ADR records the method, success rate and Suno tier needed.
Scope
- In: One full family + 2 stingers
- In: Key/BPM verification method
- In: Stem evaluation
- In: Transition prototype
- In: Suno licence tier confirmation for shipping music
- Out: Producing other location families
- Out: Final music state mapping (see
e38-music-state-data)
Notes
Fallbacks if derivation fails: (1) accept separate generations in the same key/BPM and mask transitions with stingers; (2) vertical-only approach using separated stems of one track; (3) use drone-only states. Generate shipping takes on a paid tier (audio bible §4).
Acceptance criteria
- AC-1 manual Given the labyrinth family, when analysed, then all four state tracks share the same key/mode and a BPM relationship of 1:1 or 1:2, and the ADR records how many generations were needed per usable track.
- AC-2 e2e Given the transition prototype, when state switches explore→tension→combat→resolve are triggered at random times, then each transition begins on the next bar boundary (± 10 ms) and the validator seam check reports no clicks.
- AC-3 manual Given stem export/separation on the current tier, when evaluated on the anchor track, then the ADR states whether stems are clean enough for vertical layering (yes/no with listening notes).
- AC-4 manual Given the ADR, when read, then it states the required Suno tier, the terms URL and date consulted, and the per-family production recipe.
- AC-5 manual Edge: given a derived variant that drifts in key or tempo, when detected, then the ADR documents the detection method and the chosen fallback.
Waits on
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
area:musicasset:musicepic:e38milestone:m2needs-humanmw-e38.9Data-driven music state mapping from alert states with hysteresis
Description
Why
Music is a stealth cue: tension must start exactly when a creature investigates and never lie (audio bible §2.1). The rules belong in data so designers can tune them per location.
What
As a thief, I want the music to shift to tension only when a creature is actually investigating me so that the score is a trustworthy cue. Define a zod-validated music-state config per location (tracks per state, BPM/beatsPerBar, hysteresis delays, stingers, silence budget) and a pure sim-side selector: highest-priority creature alert state → music state, with enter-immediate / exit-after-delay hysteresis.
Scope
- In: Schema + data files in
src/content/music/ - In: Pure selector in
src/sim(deterministic, injected clock) - In: Game-layer binding to the audio engine's transition API
- Out: Audio engine transitions/crossfades (Related: e28-audio-engine)
- Out: Alert state logic (see
e11-alert-states)
Notes
Defaults from audio bible §2.1: combat enter next beat, exit after 4 s; tension exit after 6 s; silence 60–180 s after 1–2 explore loops.
Acceptance criteria
- AC-1 unit Given one creature transitioning Unaware → Investigating, when the selector runs, then the music state becomes
tensionon that tick. - AC-2 unit Given the last combat creature leaving Combat, when 3.9 s elapse the state is still
combat, and when 4.0 s elapse it becomesresolvethentension/exploreper the remaining creatures. - AC-3 unit Given rapid Searching ↔ Alerted flapping every 1 s, when the selector runs for 20 s, then the music state changes at most twice.
- AC-4 content Given every location music config, when validated, then each referenced track id exists in the audio manifest and all tracks in a family share BPM and beatsPerBar.
- AC-5 unit Edge: given a creature that loses track of the player (Searching → Unaware) while another is Suspicious, when evaluated, then music stays at the highest remaining state (
stealth) rather than dropping toexplore.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:contentarea:musicclass:thiefepic:e38milestone:m2- In: Schema + data files in
mw-e38.10Audio style target: full soundscape for the style-target diorama
Description
Why
Like the visual diorama, one finished sonic slice sets the benchmark for mix, reverb and palette before full production.
What
Give the style-target diorama its final-quality soundscape: golden-hour ambience bed + randomised one-shots, footsteps on each diorama surface, weak-wall draught, flammable rope bridge creak and burn, one spell (cast/loop/impact), one creature vocal set, the Briar Glen explore→tension→combat family and stingers, location reverb.
Scope
- In: Assets via the chosen SFX source and Suno per templates
- In: Mix pass on the audio engine buses
- In: Reference recording (video capture) committed for comparison
- Out: Other locations
- Out: Engine features (Related: e28-audio-engine)
Notes
Its reference capture becomes the mix benchmark for
e38-mix-review.Acceptance criteria
- AC-1 integration Given a 3-minute scripted diorama walkthrough capture, when measured, then master integrated loudness is −16 ± 2 LUFS and true peak ≤ −1 dBTP.
- AC-2 manual Given the weak wall and the hidden well tunnel, when approached with eyes closed, then a listener can locate them by the draught/air cue within 5 m.
- AC-3 integration Given all diorama audio assets, when validated, then each passes the audio validator and has a non-placeholder CREDITS row.
- AC-4 manual Edge: given 6 simultaneous impact sounds plus combat music, when played, then no clipping occurs and the parry sound remains audible (voice-limiting/priority works).
Waits on
- mw-e37.15 Build the style-target diorama as the visual and performance benchmark open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicarea:sfxepic:e38milestone:m2mw-e38.12Write knight combat SFX prompts (14 assets)
Description
Why
Knight combat is proven by feel; a parry that rings and a heavy swing that whooms are half of that feel (souls-like weight).
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Light and heavy sword swings, wood/metal shield blocks, parry, guard break, riposte, shield bash, kick, dodge rolls (cloth, armour), charge-ready cue, stamina-exhausted cue and wall-recoil clang; ≥ 3 round-robin variants for swings/blocks.
Asset ids (14):
sfx-knight-sword-swing-light-01sfx-knight-sword-swing-heavy-01sfx-knight-block-shield-wood-01sfx-knight-block-shield-metal-01sfx-knight-parry-metal-01sfx-knight-guard-break-01sfx-knight-riposte-01sfx-knight-shield-bash-01sfx-knight-kick-01sfx-knight-dodge-roll-cloth-01sfx-knight-dodge-roll-armor-01sfx-knight-charge-ready-01sfx-knight-stamina-exhausted-01sfx-knight-wall-recoil-clang-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-knight-combat-generate)
Notes
m2 because the Forgotten (skeleton) fun validation and class-fantasy playtests judge combat feel; CC0 placeholders (
e28-placeholder-sounds) keep gameplay unblocked. One of the sonic-lock anchor sets. Charge-ready and parry must be distinguishable for players relying on audio cues (captions viae28-sound-captions).Acceptance criteria
- AC-1 content Given the 14 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:sfxasset:sfxclass:knightepic:e38milestone:m2mw-e38.13Generate knight combat SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-knight-combat-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 14 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.12 Write knight combat SFX prompts (14 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:knightepic:e38milestone:m2mw-e38.14Owner approval: knight combat SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 14 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-knight-combat-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: parry is unmistakable from a block and heavy swings sound heavier than light ones. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then parry is unmistakable from a block and heavy swings sound heavier than light ones.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-knight-combat-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.13 Generate knight combat SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:knightepic:e38milestone:m2needs-humanmw-e38.15Integrate approved knight combat SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-combat-hit-audio,e04-melee-core,e04-parry-riposte,e04-shield-bash,e04-kick-verb,e04-heavy-charged,e04-weapon-reach,e04-dodge-iframes.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 14 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the combat sandbox, when the knight performs every move, then each cue plays the final sound (no placeholder flag) via the cue sheet with no console errors.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e04.12 Parry window and riposte critical attack done
- mw-e04.13 Heavy and charged attacks with hyperarmor done
- mw-e04.14 Shield bash interrupts, shoves and breaks bashable objects done
- mw-e04.15 Weapon reach classes and swing-type wall recoil done
- mw-e04.18 Kick verb shoves creatures and physics objects done
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e04.8 Dodge roll and backstep with invulnerability frames done
- mw-e28.4 Combat impact audio matrix: hits, blocks, parries, whiffs done
- mw-e38.14 Owner approval: knight combat SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:knightepic:e38milestone:m2mw-e38.16Write impact and Forgotten SFX prompts (15 assets)
Description
Why
Every systemic material must sound different the way the sim treats it (audio bible §7.1), and the Forgotten (animated skeletons) are the first enemy the combat loop is validated against.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Impact matrix (blade-flesh, blade-bone, metal-metal, blunt-wood, claw, stone), destruction (stone wall, wood crate) and the Forgotten set (idle rattle loop, alert, attack telegraph, unblockable telegraph, hurt, poise break, collapse).
Asset ids (15):
sfx-impact-blade-flesh-01sfx-impact-blade-bone-01sfx-impact-metal-metal-01sfx-impact-blunt-wood-01sfx-impact-claw-01sfx-impact-stone-01sfx-destruct-stone-wall-01sfx-destruct-wood-crate-01sfx-creature-forgotten-idle-rattle-loop-01sfx-creature-forgotten-alert-01sfx-creature-forgotten-attack-telegraph-01sfx-creature-forgotten-unblockable-telegraph-01sfx-creature-forgotten-hurt-bone-01sfx-creature-forgotten-poise-break-01sfx-creature-forgotten-collapse-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-impacts-forgotten-generate)
Notes
The Forgotten (canon name for skeletons, story bible) are listed in the gameplay needs because they are the m2 combat test enemy (
e04-skeleton-fun-validation); all other creature vocal sets come from the world asset planner. Unblockable telegraph must be audibly distinct from the normal telegraph. Flesh hits are muffled thump + cloth — never wet gore.Acceptance criteria
- AC-1 content Given the 15 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:sfxasset:sfxepic:e38milestone:m2mw-e38.17Generate impact and Forgotten SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-impacts-forgotten-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 15 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.16 Write impact and Forgotten SFX prompts (15 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m2mw-e38.18Owner approval: impact and Forgotten SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 15 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-impacts-forgotten-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each material impact is identifiable blind and the unblockable telegraph is distinct from the normal one. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 15 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each material impact is identifiable blind and the unblockable telegraph is distinct from the normal one.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-impacts-forgotten-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.17 Generate impact and Forgotten SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m2needs-humanmw-e38.19Integrate approved impact and Forgotten SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-combat-hit-audio,e04-smash-breakables,e28-creature-audio-hooks,e04-creature-attack-hooks.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 15 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the combat sandbox with a Forgotten, when every impact material, destruction and Forgotten state is triggered, then final sounds play via cue sheets with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e04.17 Smash breakable walls and objects by impact force open
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e28.4 Combat impact audio matrix: hits, blocks, parries, whiffs done
- mw-e28.8 Creature audio slots and audible attack telegraphs done
- mw-e38.18 Owner approval: impact and Forgotten SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m2mw-e38.20Write core footsteps prompts (20 assets)
Description
Why
Footsteps are both feedback and a stealth rule: players learn that gravel is loud because it sounds loud (loudness must match the sim noise radius).
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Round-robin footstep sets (4–6 variants each) for stone, wood, dirt, grass, gravel and shallow water in sneak/walk/run gaits, plus light and heavy landings.
Asset ids (20):
sfx-foot-stone-sneaksfx-foot-stone-walksfx-foot-stone-runsfx-foot-wood-sneaksfx-foot-wood-walksfx-foot-wood-runsfx-foot-dirt-sneaksfx-foot-dirt-walksfx-foot-dirt-runsfx-foot-grass-sneaksfx-foot-grass-walksfx-foot-grass-runsfx-foot-gravel-sneaksfx-foot-gravel-walksfx-foot-gravel-runsfx-foot-water-shallow-sneaksfx-foot-water-shallow-walksfx-foot-water-shallow-runsfx-foot-land-lightsfx-foot-land-heavy
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-footsteps-core-generate)
Notes
Each id is a round-robin set (
-01…-04minimum, per sfx template). Surface ids must equale09-sound-propagationsurface ids. m2 because the thief class gauntlet reads footstep loudness. CC0 sourcing (Kenney/freesound CC0) is acceptable and often better for footsteps. Sonic-lock anchor set.Acceptance criteria
- AC-1 content Given the 20 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:sfxasset:sfxepic:e38milestone:m2mw-e38.21Generate core footsteps via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-footsteps-core-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 20 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.20 Write core footsteps prompts (20 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m2mw-e38.22Owner approval: core footsteps
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 20 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-footsteps-core-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: loudness ordering sneak < walk < run < land holds, and surfaces order carpet < grass < dirt < stone < wood-hollow < gravel/water when auditioned in sequence. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 20 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then loudness ordering sneak < walk < run < land holds, and surfaces order carpet < grass < dirt < stone < wood-hollow < gravel/water when auditioned in sequence.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-footsteps-core-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.21 Generate core footsteps via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m2needs-humanmw-e38.23Integrate approved core footsteps
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-footsteps,e09-surface-acoustics,e02-footstep-events.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 20 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the stealth grey-box course, when the player walks every surface in every gait, then the final round-robin plays without immediate repeats and no placeholder flags remain.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e02.11 Emit footstep events tagged with surface material and gait done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e28.6 Surface-aware footsteps modulated by stance and armor done
- mw-e38.22 Owner approval: core footsteps open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m2mw-e38.64Lock approved reference sounds for all later SFX prompts
Description
Why
SFX generated in many batches drift in loudness, room tone and weight; locked reference sounds keep the palette coherent (audio bible §7).
What
After the knight combat, impact/Forgotten and core footstep sets are approved, Claude proposes reference sounds per SFX category (impact, weapon, footstep, creature, magic-to-follow, UI) with measured loudness; the owner confirms. Recorded in
assets/prompts/_sonic-anchors.json; later SFX prompt files cite them and the generate step loudness-matches against them.Scope
- In: Reference proposal + owner confirmation
- In: Sonic anchor registry
- In: Validator rule for later SFX prompt files
- Out: Music (Suno) — stingers follow the audio bible directly
Notes
Anchor sets:
e38-asset-sfx-knight-combat-*,e38-asset-sfx-impacts-forgotten-*,e38-asset-sfx-footsteps-core-*.Acceptance criteria
- AC-1 manual Given the approved anchor sets, when the owner reviews the proposal, then references for impact, weapon, footstep and creature categories are confirmed and recorded in
assets/approvals.jsonwith gate "sonic-lock". - AC-2 content Given
assets/prompts/_sonic-anchors.json, when validated, then every reference id resolves to an approved sound whose SHA-256 matches its approval record. - AC-3 integration Given a later SFX prompt file without its category references, when the asset validator runs, then it fails naming the file.
- AC-4 integration Edge: given a new candidate whose integrated loudness deviates > 3 LU from its category reference, when the generate step runs, then it is flagged on the listening page rather than silently accepted.
Waits on
- mw-e38.14 Owner approval: knight combat SFX open
- mw-e38.18 Owner approval: impact and Forgotten SFX open
- mw-e38.22 Owner approval: core footsteps open
- mw-e38.4 Audio validator: loudness, true peak, loop seams, naming and licence open
area:sfxasset:sfxepic:e38milestone:m2needs-human
M3 MVP content 155
mw-e38.11Audio consistency and mix review gate before MVP content lock
Description
Why
Suno sessions and mixed SFX sources drift in timbre and level; a gate catches it before external playtests (m4).
What
Structured listening review per location against the audio bible and the audio style target: music family transitions, leitmotif presence (Glen, Labyrinth, Minotaur and its reveal variant), footstep loudness ordering vs sim, spell school distinctness (blind identification test), creature state readability, loudness report across all assets.
Scope
- In: Loudness/format report across all audio
- In: Blind school-identification test
- In: Findings file
docs/audio/reviews/with fix beads - Out: Fixing assets (filed as beads)
Notes
Run informally at m2 exit, formally before m3 content lock.
Acceptance criteria
- AC-1 integration Given all runtime audio, when the report script runs, then it lists per-file LUFS/true peak/category and flags any out-of-target file (expected zero at gate pass).
- AC-2 manual Given 13 spell-cast sounds played in random order to 3 listeners with the school list, when identified, then ≥ 70 % of identifications are correct.
- AC-3 manual Given the minotaur encounter and reveal, when reviewed, then the minotaur motif and its reveal variant are both present and recognisable by the reviewer.
- AC-4 manual Edge: given a failing asset on the MVP critical path, when triaged, then it is fixed before m3 or explicitly waived by the owner with a reason in the review file.
Waits on
- mw-e38.10 Audio style target: full soundscape for the style-target diorama open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicarea:sfxepic:e38milestone:m3needs-humanmw-e38.24Write extra footsteps and armour prompts (18 assets)
Description
Why
Stealth-relevant surfaces (creaky boards, grates, carpet, leaves, broken glass) and armour weight make noise a readable, plannable rule.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Round-robin footsteps for creaky/hollow wood, metal grate, carpet, leaves and glass shards in three gaits, plus plate/chain/leather armour movement layers played on top of footsteps.
Asset ids (18):
sfx-foot-wood-hollow-sneaksfx-foot-wood-hollow-walksfx-foot-wood-hollow-runsfx-foot-metal-grate-sneaksfx-foot-metal-grate-walksfx-foot-metal-grate-runsfx-foot-carpet-sneaksfx-foot-carpet-walksfx-foot-carpet-runsfx-foot-leaves-sneaksfx-foot-leaves-walksfx-foot-leaves-runsfx-foot-glass-shards-sneaksfx-foot-glass-shards-walksfx-foot-glass-shards-runsfx-armor-plate-layersfx-armor-chain-layersfx-armor-leather-layer
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-footsteps-extra-armor-generate)
Notes
glass-shardsis not in the audio bible §7.3 surface list — add it toe09-surface-acousticsor drop these ids. Armour layers: input said chain/plate; audio bible says plate/leather/cloth — this set uses plate, chain, leather; reconcile at prompt time. Remaining bible surfaces (mud, snow-ice, straw, bone-litter) are location-driven and left to the world asset planner.Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 18 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.25Generate extra footsteps and armour via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-footsteps-extra-armor-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 18 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.24 Write extra footsteps and armour prompts (18 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.26Owner approval: extra footsteps and armour
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 18 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-footsteps-extra-armor-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the extra surfaces respect the sim loudness ordering and the knight's plate layer is clearly the loudest. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 18 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the extra surfaces respect the sim loudness ordering and the knight's plate layer is clearly the loudest.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-footsteps-extra-armor-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.25 Generate extra footsteps and armour via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.27Integrate approved extra footsteps and armour
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-footsteps,e09-surface-acoustics,e04-armor-weight.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 18 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the stealth grey-box course, when the knight and thief walk the extra surfaces, then final footsteps and armour layers play with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e04.16 Armor weight sets roll class, stamina regen, poise and noise done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e28.6 Surface-aware footsteps modulated by stance and armor done
- mw-e38.26 Owner approval: extra footsteps and armour open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.28Write bow and arrow SFX prompts (15 assets)
Description
Why
The archer's rhythm lives in the draw creak and the focused-release chime; trick-arrow payloads must be recognisable by ear.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Draw creak loop, release twang, focused-window chime, flyby, impacts (wood, stone, flesh, metal), metal ricochet, pickup, and payload sounds for fire, water, rope, noise and blunt arrows.
Asset ids (15):
sfx-bow-draw-creak-loop-01sfx-bow-release-twang-01sfx-bow-focused-chime-01sfx-arrow-flyby-01sfx-arrow-impact-wood-01sfx-arrow-impact-stone-01sfx-arrow-impact-flesh-01sfx-arrow-impact-metal-01sfx-arrow-ricochet-metal-01sfx-arrow-pickup-01sfx-arrow-fire-ignite-01sfx-arrow-water-splash-01sfx-arrow-rope-unfurl-01sfx-arrow-noise-rattle-01sfx-arrow-blunt-thud-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-bow-arrows-generate)
Notes
Draw creak loop needs a seamless loop point (sidecar). Focused chime must be audible over combat music at the SFX bus level.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 15 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxclass:archerepic:e38milestone:m3mw-e38.29Generate bow and arrow SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-bow-arrows-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 15 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.28 Write bow and arrow SFX prompts (15 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:archerepic:e38milestone:m3mw-e38.30Owner approval: bow and arrow SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 15 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-bow-arrows-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the focused chime is clearly audible in a busy mix and each payload is identifiable blind. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 15 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the focused chime is clearly audible in a busy mix and each payload is identifiable blind.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-bow-arrows-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.29 Generate bow and arrow SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:archerepic:e38milestone:m3needs-humanmw-e38.31Integrate approved bow and arrow SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-bow-audio,e05-draw-timing,e05-arrow-retrieval,e05-fire-arrow,e05-water-arrow,e05-rope-arrow,e05-noise-arrow,e05-blunt-arrow.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 15 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the archery range, when every arrow type is drawn, released and lands on each material, then final sounds play via cue sheets with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e05.10 Rope arrows anchor into wood and create climbable ropes open
- mw-e05.11 Noise arrows create sound events that lure creatures open
- mw-e05.12 Blunt arrows knock objects, press switches and stun open
- mw-e05.4 Focused release window and rhythm bonus for well-timed shots open
- mw-e05.7 Stuck arrows persist and can be retrieved or looted done
- mw-e05.8 Fire arrows ignite flammable things; dip arrows in open flame open
- mw-e05.9 Water arrows douse fires, extinguish lights and wet surfaces open
- mw-e28.7 Bow audio: draw creak, release, flyby and payload cues open
- mw-e38.30 Owner approval: bow and arrow SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:archerepic:e38milestone:m3mw-e38.32Write stealth action SFX prompts (20 assets)
Description
Why
Stealth is a conversation through sound: a coin that clinks draws a guard, a dropped body is heard, an alarm bell changes the whole level.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Lockpick pin click/fail/break, pickpocket rustle, takedown choke, backstab, body drop, coin impact, bottle shatter, splash, candle snuff, torch douse, alarm bell, guard shout, cough in smoke, smoke-bomb pop and trap triggers (bear trap, tripwire, noisemaker, caltrops).
Asset ids (20):
sfx-thief-lockpick-pin-click-01sfx-thief-lockpick-fail-01sfx-thief-lockpick-break-01sfx-thief-pickpocket-rustle-01sfx-thief-takedown-choke-01sfx-thief-backstab-01sfx-body-drop-thud-01sfx-coin-impact-stone-01sfx-bottle-shatter-01sfx-splash-throwable-01sfx-light-candle-snuff-01sfx-light-torch-douse-01sfx-alarm-bell-01sfx-guard-shout-alert-01sfx-cough-smoke-01sfx-smoke-bomb-pop-01sfx-trap-bear-snap-01sfx-trap-tripwire-twang-01sfx-trap-noisemaker-rattle-01sfx-trap-caltrops-scatter-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-stealth-actions-generate)
Notes
No voice for MVP: guard shout and cough are non-verbal vocalisations (no words). Relative loudness must match each event's sim noise radius. Smoke-bomb pop added (implied by smoke bombs). Takedown choke is non-graphic.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 20 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxclass:thiefepic:e38milestone:m3mw-e38.33Generate stealth action SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-stealth-actions-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 20 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.32 Write stealth action SFX prompts (20 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:thiefepic:e38milestone:m3mw-e38.34Owner approval: stealth action SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 20 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-stealth-actions-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: no sound contains intelligible words, and loud world events (bell, shatter) are clearly louder than thief actions. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 20 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then no sound contains intelligible words, and loud world events (bell, shatter) are clearly louder than thief actions.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-stealth-actions-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.33 Generate stealth action SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:thiefepic:e38milestone:m3needs-humanmw-e38.35Integrate approved stealth action SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e10-lockpicking,e10-pickpocketing,e10-takedowns,e10-body-carry-hide,e10-thrown-distractions,e10-extinguish-lights,e10-smoke-escape,e10-trap-disarm,e11-communication.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 20 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the thief grey-box challenge, when every thief action and trap fires, then final sounds play via cue sheets with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e10.12 Detect and disarm world traps; failures trigger them open
- mw-e10.13 Smoke bombs and flash powder break line of sight for escapes open
- mw-e10.2 Throw objects to create noise distractions guards investigate open
- mw-e10.3 Extinguish and relight light sources to reshape the shadows open
- mw-e10.4 Backstabs and non-lethal takedowns on unaware targets open
- mw-e10.5 Carry and hide unconscious or dead bodies open
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e11.12 Allies share alerts by shouts, sight signals and alarm devices open
- mw-e38.34 Owner approval: stealth action SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:thiefepic:e38milestone:m3mw-e38.36Write UI SFX prompts (14 assets)
Description
Why
Menus should feel like handling a field journal — paper, leather, brass (audio bible §7.6) — and critical HUD states (lock-on, low health) need audio.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Menu open/close (leather flap), hover, confirm (brass click), cancel, page turn, coin clink, item pickup, equip, quest update (soft chime), journal quill scratch, error/deny (wooden knock), lock-on tick and low-health heartbeat loop.
Asset ids (14):
sfx-ui-menu-open-01sfx-ui-menu-close-01sfx-ui-hover-01sfx-ui-confirm-01sfx-ui-cancel-01sfx-ui-page-turn-01sfx-ui-coin-01sfx-ui-item-pickup-01sfx-ui-equip-01sfx-ui-quest-update-01sfx-ui-journal-quill-01sfx-ui-error-deny-01sfx-ui-lockon-01sfx-ui-low-health-heartbeat-loop-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-ui-core-generate)
Notes
All ≤ 400 ms except the heartbeat loop; UI loudness target −20 LUFS short-term ±2, never louder than combat. The verb-learned fanfare is a music stinger (
e38-asset-music-stingers-*).Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 14 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.37Generate UI SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-ui-core-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 14 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.36 Write UI SFX prompts (14 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.38Owner approval: UI SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 14 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-ui-core-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: UI sounds stay pleasant after 50 repetitions and never mask combat cues. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 14 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then UI sounds stay pleasant after 50 repetitions and never mask combat cues.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-ui-core-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.37 Generate UI SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.39Integrate approved UI SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e17-inventory-ui,e17-equipment-ui,e23-journal-ui,e20-shop-ui,e07-book-reader-ui,e02-lock-on,e28-cue-sheets.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 14 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the inventory, journal, shop and combat sandbox, when every UI action and lock-on fires, then final sounds play with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e02.16 Lock on to and cycle between targets with camera framing done
- mw-e07.9 Read books in a page-turning reader with a study action open
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e17.11 Add equipment paper-doll view with verb and stealth comparison open
- mw-e20.10 Build shop UI with merchant identity, buy/sell and price breakdown done
- mw-e23.6 Build in-character journal for quests, rumors and notes done
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e38.38 Owner approval: UI SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.40Write elemental spell SFX prompts (13 assets)
Description
Why
Elemental spells change the world; each must sound organic and match its school palette (audio bible §7.4).
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Phase sounds for ember, firebolt, flame-jet, frostglass, wellspring, gust, thunderclap, arc-lash.
Asset ids (13):
sfx-spell-fire-ember-cast-01sfx-spell-fire-ember-impact-01sfx-spell-fire-firebolt-cast-01sfx-spell-fire-firebolt-impact-01sfx-spell-fire-flame-jet-loop-01sfx-spell-frost-frostglass-cast-01sfx-spell-frost-frostglass-impact-01sfx-spell-frost-frostglass-shatter-01sfx-spell-conjuration-wellspring-cast-01sfx-spell-conjuration-wellspring-splash-01sfx-spell-storm-gust-whoosh-01sfx-spell-storm-thunderclap-boom-01sfx-spell-storm-arc-lash-crackle-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-spells-elemental-generate)
Notes
Storm harshness is capped (audio bible §7.4). Flame Jet loop needs a seamless loop point.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 13 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.41Generate elemental spell SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-spells-elemental-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 13 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.40 Write elemental spell SFX prompts (13 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.42Owner approval: elemental spell SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 13 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-spells-elemental-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each spell's phases sound like one spell and its school is identifiable blind. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 13 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each spell's phases sound like one spell and its school is identifiable blind.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-spells-elemental-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.41 Generate elemental spell SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3needs-humanmw-e38.43Integrate approved elemental spell SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e06-spell-cue-binding,e08-spell-ember,e08-spell-firebolt-flame-jet,e08-spell-frostglass,e08-spell-wellspring,e08-spell-gust,e08-spell-thunderclap,e08-spell-arc-lash.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 13 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when each elemental spell is cast, then every phase plays its final sound with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e06.17 Bind spell events to placeholder VFX and SFX cues open
- mw-e08.2 Add Ember: precise ignition of flammable things done
- mw-e08.3 Add Firebolt and its Flame Jet follow-on verb open
- mw-e08.5 Add Frostglass: freeze targets and create temporary ice surfaces open
- mw-e08.6 Add Wellspring: conjure a burst of water that soaks an area open
- mw-e08.7 Add Gust: blast creatures back, clear gas and propel objects open
- mw-e08.8 Add Thunderclap: a violent shockwave that breaks and deafens open
- mw-e08.9 Add Arc Lash: lightning that chains through conductive and wet open
- mw-e38.42 Owner approval: elemental spell SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.44Write arcane and mystic spell SFX prompts (22 assets)
Description
Why
The sorcerer's kinetic, illusion, light and life spells need distinct sonic identities so a player can hear what was cast.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Phase sounds for mage-hand, telekinetic-throw, blink, levitate, gravity-well, mirror-self, wisp-light, prism-ray, grasping-vines, animate-bones, polymorph.
Asset ids (22):
sfx-spell-arcane-mage-hand-grab-01sfx-spell-arcane-mage-hand-hold-loop-01sfx-spell-arcane-mage-hand-release-01sfx-spell-arcane-telekinetic-throw-launch-01sfx-spell-arcane-telekinetic-throw-catch-01sfx-spell-arcane-blink-depart-01sfx-spell-arcane-blink-arrive-01sfx-spell-gravity-levitate-loop-01sfx-spell-gravity-gravity-well-loop-01sfx-spell-gravity-gravity-well-collapse-01sfx-spell-illusion-mirror-self-spawn-01sfx-spell-illusion-mirror-self-dispel-01sfx-spell-light-wisp-light-spawn-01sfx-spell-light-wisp-light-hum-loop-01sfx-spell-light-prism-ray-loop-01sfx-spell-nature-grasping-vines-erupt-01sfx-spell-nature-grasping-vines-creak-01sfx-spell-necromancy-animate-bones-assemble-01sfx-spell-necromancy-animate-bones-rattle-01sfx-spell-necromancy-animate-bones-crumble-01sfx-spell-alteration-polymorph-poof-01sfx-spell-alteration-polymorph-critter-cluck-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-spells-arcane-mystic-generate)
Notes
Loops (hold, levitate, gravity well, wisp hum, prism) need seamless loop points. Polymorph critter cluck matches the critter model.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 22 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.45Generate arcane and mystic spell SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-spells-arcane-mystic-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 22 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.44 Write arcane and mystic spell SFX prompts (22 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.46Owner approval: arcane and mystic spell SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 22 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-spells-arcane-mystic-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each spell's phases sound like one spell and its school is identifiable blind. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 22 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each spell's phases sound like one spell and its school is identifiable blind.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-spells-arcane-mystic-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.45 Generate arcane and mystic spell SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3needs-humanmw-e38.47Integrate approved arcane and mystic spell SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e06-spell-cue-binding,e08-spell-mage-hand,e08-spell-telekinetic-throw,e08-spell-blink,e08-spell-levitate,e08-spell-gravity-well,e08-spell-mirror-self,e08-spell-wisp-light,e08-spell-prism-ray,e08-spell-grasping-vines,e08-spell-animate-bones,e08-spell-polymorph.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 22 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when each spell in the set is cast, then every phase plays its final sound with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e06.17 Bind spell events to placeholder VFX and SFX cues open
- mw-e08.10 Add Mage Hand: grab, move and use distant objects done
- mw-e08.11 Add Telekinetic Throw & Catch: hurl objects, creatures and arrows open
- mw-e08.12 Add Blink: teleport several metres through open space open
- mw-e08.13 Add Levitate: float the caster or an object upward slowly open
- mw-e08.14 Add Gravity Well: pull creatures and loose objects to a point open
- mw-e08.15 Add Mirror Self: an illusory double that draws attention open
- mw-e08.17 Add Wisp Light: a floating orb that lights darkness and repels open
- mw-e08.18 Add Prism Ray: a light beam that reflects off reflective surfaces open
- mw-e08.20 Add Grasping Vines: roots burst up and restrain creatures open
- mw-e08.21 Add Animate Bones: raise nearby remains as a temporary servant open
- mw-e08.22 Add Polymorph: temporarily turn a creature into a harmless animal open
- mw-e38.46 Owner approval: arcane and mystic spell SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.52Write school default SFX prompts (26 assets)
Description
Why
Spells without bespoke sound (NPC casts, data-only spells, miscasts) must still sound like their school.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.One default cast and one default impact per school following the audio bible §7.4 palette.
Asset ids (26):
sfx-spell-fire-default-cast-01sfx-spell-frost-default-cast-01sfx-spell-storm-default-cast-01sfx-spell-arcane-default-cast-01sfx-spell-illusion-default-cast-01sfx-spell-alteration-default-cast-01sfx-spell-conjuration-default-cast-01sfx-spell-necromancy-default-cast-01sfx-spell-nature-default-cast-01sfx-spell-light-default-cast-01sfx-spell-shadow-default-cast-01sfx-spell-gravity-default-cast-01sfx-spell-time-default-cast-01sfx-spell-fire-default-impact-01sfx-spell-frost-default-impact-01sfx-spell-storm-default-impact-01sfx-spell-arcane-default-impact-01sfx-spell-illusion-default-impact-01sfx-spell-alteration-default-impact-01sfx-spell-conjuration-default-impact-01sfx-spell-necromancy-default-impact-01sfx-spell-nature-default-impact-01sfx-spell-light-default-impact-01sfx-spell-shadow-default-impact-01sfx-spell-gravity-default-impact-01sfx-spell-time-default-impact-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-school-defaults-generate)
Notes
Miscasts reuse the default impact with pipeline pitch/filter variants rather than separate generations.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 26 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.53Generate school default SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-school-defaults-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 26 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.52 Write school default SFX prompts (26 assets) open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.54Owner approval: school default SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 26 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-school-defaults-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: all 13 schools are distinguishable blind in a shuffled playlist. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 26 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then all 13 schools are distinguishable blind in a shuffled playlist.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-school-defaults-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.53 Generate school default SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3needs-humanmw-e38.55Integrate approved school default SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e06-spell-cue-binding,e06-botched-casts.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 26 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when a data-only spell of each school is cast, then the school default sounds play with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e06.13 Resolve botched casts into data-driven miscast outcomes open
- mw-e06.17 Bind spell events to placeholder VFX and SFX cues open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.54 Owner approval: school default SFX open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.60Write gameplay music stingers Suno prompts (11)
Description
Why
Stingers mark the story beats of play — being spotted, escaping, discovering a secret, learning a verb — and tie gameplay to the musical identity (audio bible §2.3).
What
Write one prompt file per stinger in
assets/prompts/music/<asset-id>.mdfrom_templates/music-stinger.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key (or neutral), BPM, length, output spec.Key-neutral stealth stingers (suspicious, detected, combat start, lost them), discovery, four class-ident capability-unlock fanfares, quest complete and death.
Asset ids (11):
music-stinger-suspiciousmusic-stinger-detectedmusic-stinger-combat-startmusic-stinger-lost-themmusic-stinger-discoverymusic-stinger-capability-unlock-knightmusic-stinger-capability-unlock-archermusic-stinger-capability-unlock-sorcerermusic-stinger-capability-unlock-thiefmusic-stinger-quest-completemusic-stinger-death
Scope
- In: One Suno prompt per stinger
- In: Motif references per audio bible §1.4
- Out: Pasting into Suno (
e38-asset-music-stingers-generate, owner)
Notes
Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min/stinger) and drops the chosen take in
assets/_incoming/music/. Location-keyed combat-start/victory variants and the Brother Horn reveal (music-stinger-horn-reveal) belong to the location music set (world planner); these are key-neutral. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 11 ids, when the asset validator runs, then each has one prompt file matching
music-stinger-<event>-<key?>naming. - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte.
- AC-3 content Given each prompt, when inspected, then length is within audio bible §2.3 for its stinger type and key/BPM are stated (or "key-neutral").
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words or names a real artist, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.61Owner generates gameplay music stingers in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per stinger) — contract §1.
What
For each prompt of
e38-asset-music-stingers-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 11 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.60 Write gameplay music stingers Suno prompts (11) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.62Owner approval: gameplay music stingers
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loudness-normalised) stingers played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-stingers-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each stinger fits its moment and the four class idents are recognisably one family with different characters. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 11 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each stinger fits its moment and the four class idents are recognisably one family with different characters.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-stingers-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.61 Owner generates gameplay music stingers in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.63Integrate approved gameplay music stingers into adaptive music
Description
Why
Stingers only matter when they fire at the right moment on the music bus.
What
Trim, fade tails, normalise to stinger loudness, encode Opus + AAC with sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data / stinger table.
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e28-adaptive-music,e09-alert-barks,e19-capabilities-screen,e38-music-state-data.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then each stinger is −16 LUFS ±1 (short-term max), ≤ −1.0 dBTP, transient within 20 ms of start and tail ≤ 2 s, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each stinger has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the stealth course and capabilities screen, when a guard cycles suspicious→detected→lost and a verb is learned, then the final stingers play on the music bus without clipping.
- AC-4 integration Edge: given a stinger without an approval record, when the build runs, then its placeholder remains and the report lists it.
Waits on
- mw-e09.13 Text barks and audio stingers on alert state transitions open
- mw-e19.12 Build capabilities screen and "new verb learned" feedback open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.62 Owner approval: gameplay music stingers open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.65Write River Wend and barge music Suno prompts (7)
Description
Why
The river is connective tissue: open and flowing on the towpath, a gentle journey on the barge, and faintly wrong at the humming Weeping Adit.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.River Wend family (B Dorian 84 BPM 6/8: explore, stealth, tension, combat), the barge journey (D Dorian 76, main theme on harp and low whistle, linear), and the Weeping Adit (C♯ Aeolian 56/free-time, bridging to the Deepworks: drone explore with the Nightjar song faint on the water, stealth).
Asset ids (7):
music-river-wend-exploremusic-river-wend-stealthmusic-river-wend-tensionmusic-river-wend-combatmusic-barge-journeymusic-weeping-adit-exploremusic-weeping-adit-stealth
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-river-generate, owner)
Notes
Ids follow audio bible §3 (
music-barge-journey; the needs list saidmusic-barge-ride).music-weeping-aditis a bible track not in the needs list.Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 7 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.66Owner generates River Wend and barge music in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-river-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 7 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.65 Write River Wend and barge music Suno prompts (7) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.67Owner approval: River Wend and barge music
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-river-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the river family is key/tempo-locked and open-sounding, the barge journey quotes the main theme recognisably, and the Adit bridges into the Deepworks key with a faint Nightjar quote. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the river family is key/tempo-locked and open-sounding, the barge journey quotes the main theme recognisably, and the Adit bridges into the Deepworks key with a faint Nightjar quote.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-river-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.66 Owner generates River Wend and barge music in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.68Integrate approved River Wend and barge music into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e25-barge-travel,e25-weeping-adit. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-river-art-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the quay, towpath and the Weeping Adit, when the player explores, sneaks and fights, then the river family switches on bar boundaries, the Adit track cross-fades into the Deepworks family, and the barge card plays the journey track, with no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.67 Owner approval: River Wend and barge music open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.69Write Wendmouth music families Suno prompts (12)
Description
Why
Wendmouth's "Harbour colour" (shanty-inflected, no accordion, no pirate cliché) and "The Tide" motif make the harbour sound like a different mood of the same band (audio bible §1.1, §1.4).
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Wendmouth day (A Mixolydian 112 6/8: explore — establishes "The Tide" — tension, combat/brawl), Wendmouth night (A Dorian 90: explore, stealth = heist variant of "The Tide" on pizzicato cello and muted dulcimer, tension, combat), the Drowned Gull (diegetic, D Mixolydian 120), the Tidefair festive loop (A Mixolydian 120 6/8) and the Undertow (D Phrygian 70: explore, stealth, tension).
Asset ids (12):
music-wendmouth-day-exploremusic-wendmouth-day-tensionmusic-wendmouth-day-combatmusic-wendmouth-night-exploremusic-wendmouth-night-stealthmusic-wendmouth-night-tensionmusic-wendmouth-night-combatmusic-drowned-gull-exploremusic-tidefair-exploremusic-undertow-exploremusic-undertow-stealthmusic-undertow-tension
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-wendmouth-generate, owner)
Notes
music-wendmouth-day-exploreis the motif anchor for "The Tide"; later harbour cues cite it. The needs list'smusic-heist-bay-nineis covered by the bible's night stealth (heist) layer rather than a separate family; ids follow audio bible §3 (music-drowned-gull, not-tavern).Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 12 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.70Owner generates Wendmouth music families in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-wendmouth-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 12 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.69 Write Wendmouth music families Suno prompts (12) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.71Owner approval: Wendmouth music families
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-wendmouth-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: "The Tide" is recognisable in the day explore track and its heist variant, there is no accordion, concertina, lyrics or pirate cliché, and the Gull is rowdier than the Sleeping Ox. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then "The Tide" is recognisable in the day explore track and its heist variant, there is no accordion, concertina, lyrics or pirate cliché, and the Gull is rowdier than the Sleeping Ox.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-wendmouth-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.70 Owner generates Wendmouth music families in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.72Integrate approved Wendmouth music families into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e40-drowned-gull,e40-tidefair-strength,e40-undertow,e40-bonded-warehouse. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given Wendmouth by day and night, the Gull, the Tidefair and the Undertow, when states change and the warehouse heist starts, then the matching tracks switch on bar boundaries and the heist stealth layer plays, with no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.71 Owner approval: Wendmouth music families open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
area:musicasset:musicepic:e38milestone:m3mw-e38.73Write harbour and Tidefair stingers Suno prompts (12)
Description
Why
Tidefair contests, the customs confiscation, a perfect heist and the Tern sailing are beats that need a musical punctuation mark in the harbour key.
What
Write one prompt file per stinger/cue in
assets/prompts/music/<asset-id>.mdfrom_templates/music-stinger.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Tidefair start (1–2 s fiddle flourish), win (3–4 s "The Tide" fragment with stomp) and lose (2–3 s gracious deflating fiddle slide); customs-confiscate; heist-perfect ("The Tide" heist variant resolving); tern-sails (Act III departure); combat-start and combat-victory in the new keys (b-dorian = River Wend; a-mixolydian = Wendmouth day; a-dorian = Wendmouth night).
Asset ids (12):
music-stinger-tidefair-startmusic-stinger-tidefair-winmusic-stinger-tidefair-losemusic-stinger-customs-confiscatemusic-stinger-heist-perfectmusic-stinger-tern-sailsmusic-stinger-combat-start-b-dorianmusic-stinger-combat-start-a-mixolydianmusic-stinger-combat-start-a-dorianmusic-stinger-combat-victory-b-dorianmusic-stinger-combat-victory-a-mixolydianmusic-stinger-combat-victory-a-dorian
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-harbour-stingers-generate, owner)
Notes
music-stinger-tidefair-startis added from audio bible §2.3 (the needs list had only win/lose).Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 12 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.71 Owner approval: Wendmouth music families open
area:musicasset:musicepic:e38milestone:m3mw-e38.74Owner generates harbour and Tidefair stingers in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-harbour-stingers-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 12 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.73 Write harbour and Tidefair stingers Suno prompts (12) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.75Owner approval: harbour and Tidefair stingers
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-harbour-stingers-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each stinger sits in its family key, Tidefair lose is comic not sad, and heist-perfect and tidefair-win quote "The Tide" recognisably. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each stinger sits in its family key, Tidefair lose is comic not sad, and heist-perfect and tidefair-win quote "The Tide" recognisably.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-harbour-stingers-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.74 Owner generates harbour and Tidefair stingers in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.76Integrate approved harbour and Tidefair stingers into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Trim, fade tails, normalise to stinger loudness, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data / stinger table.
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e40-tidefair-strength,e40-tidefair-shoot-pockets,e40-customs-house,e40-crate-heist,e40-gilded-tern.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then each stinger is −16 LUFS ±1 (short-term max), ≤ −1.0 dBTP, transient within 20 ms of start and tail ≤ 2 s, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the Tidefair contests, Cray confiscating a book, a perfect heist and the Act III fact, when each fires, then its stinger plays once on the music bus in key, and river/harbour combat uses the keyed combat stingers, with no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.75 Owner approval: harbour and Tidefair stingers open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
- mw-e40.20 Place The Crate for Mr. S— heist with all six solution routes open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
- mw-e40.8 Stage the Tidefair Gull Shoot and the Pocket Race open
area:musicasset:musicepic:e38milestone:m3mw-e38.77Write River Wend ambience prompts (6 assets)
Description
Why
The river is heard before it is seen — reeds, herons, the weir's roar (which may mask noise), the lock's creaks and the Adit's night hum (rumour 16).
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Quay bed (barge creaks, the grind and knock of dressed blocks being landed), riverbank flow bed (audio only — no flow sim), towpath day (herons, reeds, hooves and tow-rope creak), Millweir spill roar, Kestrel Lock (gate creaks, sluice rush, windlass, quiet cottage), and the Weeping Adit drip with a faint hum at night.
Asset ids (6):
amb-river-wend-quay-01amb-river-wend-bank-01amb-river-wend-towpath-01amb-millweir-spill-01amb-kestrel-lock-01amb-weeping-adit-drip-hum-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-amb-river-generate)
Notes
Ids follow audio bible §7.7 (
amb-river-wend-*; the needs list saidamb-river-quay-01,amb-river-flow-bank-01,amb-towpath-day-01). If the weir is a noise-masking zone its radius must matche09-sound-propagation. The Adit hum uses the Nightjar motif anchor.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 6 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each bed, when inspected, then it states loop length ≥ 30 s, stereo, the randomised one-shots kept out of the bed, and the §7.7 gameplay role (draught, water, song) it carries.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (ambience −24 LUFS ± 2, ≤ −3.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.78Generate River Wend ambience via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout, loop-seam preview).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-amb-river-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds. Loop beds are stitched from multiple generations where the API limits clip length.
Acceptance criteria
- AC-1 integration Given the 6 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then beds are stereo, ≥ 30 s, loop seams pass the discontinuity check, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
- mw-e38.77 Write River Wend ambience prompts (6 assets) open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.79Owner approval: River Wend ambience
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 6 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-amb-river-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each bed identifies its place, the weir roar is continuous and seamless, and the Adit hum is faint enough to be a rumour rather than a cue. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 6 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each bed identifies its place, the weir roar is continuous and seamless, and the Adit hum is faint enough to be a rumour rather than a cue.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-amb-river-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.78 Generate River Wend ambience via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.80Integrate approved River Wend ambience
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets / ambience zone data, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-cue-sheets,e25-quay-stone-stair,e25-millweir-sluice-walk,e25-weeping-adit,e25-towpath-kestrel-lock. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e25-river-art-audio. Ambience zones aree28-ambience(P2); these beds bind through the cue sheets.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 6 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the river, when the player walks from the quay along the towpath to the lock and visits the weir and the Adit by night, then each final bed plays and cross-fades with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e25.12 Build the Millweir and its sluice walk to the Briarwood far bank open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.79 Owner approval: River Wend ambience open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.81Write Wendmouth ambience prompts (10 assets)
Description
Why
Wendmouth must be louder and busier than any Briar Glen bed (audio bible §7.7): gulls, halyards, crowds, cranes, the bell buoy.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Harbour day (wordless crowd, gulls, rigging creak, surf, crane winches), harbour night, the Drowned Gull murmur (rowdy walla, dice, tankards, waves under the floor), Tidemarket bustle, Undertow cellar, Bonded Warehouse at night, the Tern's hold, breakwater surf, a gull loop and wharf-rat scurry beds.
Asset ids (10):
amb-wendmouth-harbour-day-01amb-wendmouth-harbour-night-01amb-drowned-gull-murmur-01amb-wendmouth-tidemarket-01amb-wendmouth-undertow-01amb-wendmouth-bonded-warehouse-night-01amb-wendmouth-gilded-tern-hold-01amb-wendmouth-breakwater-surf-01amb-wendmouth-gulls-01amb-wendmouth-wharf-rats-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-amb-harbour-generate)
Notes
Ids use the
amb-wendmouth-*pattern from audio bible §7.7 (needs list saidamb-tidemarket-bustle-01,amb-undertow-cellar-01, etc.);amb-drowned-gull-*follows the bible tavern row.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 10 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each bed, when inspected, then it states loop length ≥ 30 s, stereo, the randomised one-shots kept out of the bed, and the §7.7 gameplay role (draught, water, song) it carries.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (ambience −24 LUFS ± 2, ≤ −3.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.82Generate Wendmouth ambience via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout, loop-seam preview).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-amb-harbour-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds. Loop beds are stitched from multiple generations where the API limits clip length.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then beds are stereo, ≥ 30 s, loop seams pass the discontinuity check, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
- mw-e38.81 Write Wendmouth ambience prompts (10 assets) open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.83Owner approval: Wendmouth ambience
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 10 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-amb-harbour-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: harbour beds are clearly louder and busier than Briar Glen beds, crowd walla has no intelligible words, the warehouse at night is quiet enough for a heist, and beds loop seamlessly. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then harbour beds are clearly louder and busier than Briar Glen beds, crowd walla has no intelligible words, the warehouse at night is quiet enough for a heist, and beds loop seamlessly.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-amb-harbour-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.82 Generate Wendmouth ambience via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.84Integrate approved Wendmouth ambience
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets / ambience zone data, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-cue-sheets,e40-harbor-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e40-harbor-lighting-audio. Ambience zones aree28-ambience(P2); these beds bind through the cue sheets.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 10 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Wendmouth, when the player walks from the Stone Wharf through the Tidemarket into the Undertow, the Gull and the Tern by day and night, then each final bed plays with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.83 Owner approval: Wendmouth ambience open
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:sfxasset:sfxepic:e38milestone:m3mw-e38.85Write river set-piece SFX prompts (10 assets)
Description
Why
The Stone Stair slide, weir sluice, lock cycle and the Adit current are mechanisms the player operates or survives; each must say what changed.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Stone Stair block-slide rumble, heavy sluice gate, lock paddle and windlass, lock gate strain, lock chamber fill loop, Adit current push loop, Adit barge winch, barge horn, barge pole plunge, towpath hooves and harness jingle.
Asset ids (10):
sfx-stone-stair-slide-01sfx-sluice-gate-heavy-01sfx-lock-paddle-01sfx-lock-gate-strain-01sfx-lock-chamber-fill-loop-01sfx-adit-current-push-loop-01sfx-adit-winch-barge-01sfx-barge-horn-01sfx-barge-pole-plunge-01sfx-towpath-hooves-harness-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-river-setpiece-generate)
Notes
Barge pole and hooves are added from audio bible §7.9 River row.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 10 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.86Generate river set-piece SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-river-setpiece-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
- mw-e38.85 Write river set-piece SFX prompts (10 assets) open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.87Owner approval: river set-piece SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 10 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-river-setpiece-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each mechanism sound reads its state change, the Adit current loop sounds forceful without implying a flow sim, and the barge horn is distinct from every bell. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each mechanism sound reads its state change, the Adit current loop sounds forceful without implying a flow sim, and the barge horn is distinct from every bell.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-river-setpiece-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.86 Generate river set-piece SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.88Integrate approved river set-piece SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e25-quay-stone-stair,e25-millweir-sluice-walk,e25-towpath-kestrel-lock,e25-weeping-adit,e25-barge-travel.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 10 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the river locations, when the Stone Stair is slid, the sluice opened, the lock cycled and the Adit swum, then final sounds play with the right noise events and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e25.11 Build Briar Glen Quay and the Stone Stair slipway down from the mine open
- mw-e25.12 Build the Millweir and its sluice walk to the Briarwood far bank open
- mw-e25.13 Build the Weeping Adit: the flooded sixth way into the lower Deepworks open
- mw-e25.14 Build the towpath and Kestrel Lock with the keeper's cottage open
- mw-e25.15 Ride Mags Oakum's barge between Briar Glen Quay and Wendmouth open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.87 Owner approval: river set-piece SFX open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.89Write harbour mechanism and ship SFX prompts (18 assets)
Description
Why
Cranes, nets, seals, strongrooms, sea-doors and the Tern are the heist's moving parts; bells and buoys give the harbour its low, slightly sad pulse.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Crane winch loop and jib swing, cargo net cut and drop-splash, customs stamp, seal break and re-stamp, strongroom door, skylight, hoist beam creak, Gull trapdoor, sea-door, Tern anchor chain, sails unfurl, ship's bell, tide change, bell buoy and breakwater fog bell.
Asset ids (18):
sfx-crane-winch-loop-01sfx-crane-jib-swing-01sfx-cargo-net-cut-01sfx-cargo-net-drop-splash-01sfx-customs-stamp-01sfx-customs-seal-break-01sfx-customs-seal-restamp-01sfx-strongroom-door-01sfx-skylight-open-01sfx-hoist-beam-creak-01sfx-trapdoor-gull-01sfx-sea-door-01sfx-tern-anchor-chain-01sfx-tern-sails-unfurl-01sfx-tern-ship-bell-01sfx-tide-change-01sfx-bell-buoy-01sfx-fog-bell-breakwater-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-harbour-mechanisms-generate)
Notes
Net cut, ship's bell, bell buoy and fog bell are added from audio bible §7.9.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 18 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.90Generate harbour mechanism and ship SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-harbour-mechanisms-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 18 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
- mw-e38.89 Write harbour mechanism and ship SFX prompts (18 assets) open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.91Owner approval: harbour mechanism and ship SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 18 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-harbour-mechanisms-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the net cut reads as fibrous snap plus falling load, seal break vs re-stamp are distinguishable, and harbour bells are thinner and shorter than the Vesper Bell with no minor-third bloom. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 18 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the net cut reads as fibrous snap plus falling load, seal break vs re-stamp are distinguishable, and harbour bells are thinner and shorter than the Vesper Bell with no minor-third bloom.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-harbour-mechanisms-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.90 Generate harbour mechanism and ship SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.92Integrate approved harbour mechanism and ship SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e40-stone-wharf,e40-customs-house,e40-bonded-warehouse,e40-drowned-gull,e40-breakwater-sea-door,e40-gilded-tern,e40-crate-heist.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 18 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Stone Wharf, warehouse, customs house and breakwater, when a crane swings, a net is cut, a seal is broken and re-stamped, the sea-door opens and the Tern sails, then final sounds play with correct noise events and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.91 Owner approval: harbour mechanism and ship SFX open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
- mw-e40.15 Build the breakwater, sunken figurehead, sea-door and harbour tides open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.20 Place The Crate for Mr. S— heist with all six solution routes open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.93Write harbour contest, alarm and gull SFX prompts (25 assets)
Description
Why
Contests, brawls and the watch's non-verbal alarms carry the harbour's hijinks; gulls and rats are its ambient mischief.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Arm-wrestle strain loop and table slam, four brawl punches, crowd cheer and groan, wooden gull target hit, regatta oars loop and starting clapper, dice roll, tankard slam; watch wooden rattle and handbell, smuggler whistle, bosun's pipe, thrown-off-pier splash and surfacing gasp; four gull cries, gull snatch "ha-ha", rat startle squeal.
Asset ids (25):
sfx-arm-wrestle-strain-loop-01sfx-arm-wrestle-slam-01sfx-brawl-punch-01sfx-brawl-punch-02sfx-brawl-punch-03sfx-brawl-punch-04sfx-crowd-cheer-01sfx-crowd-groan-01sfx-wooden-gull-hit-01sfx-regatta-oars-loop-01sfx-regatta-start-clapper-01sfx-dice-roll-01sfx-tankard-slam-01sfx-watch-rattle-01sfx-watch-handbell-01sfx-smuggler-whistle-01sfx-bosun-pipe-01sfx-thrown-off-pier-splash-01sfx-surface-gasp-01sfx-gull-cry-01sfx-gull-cry-02sfx-gull-cry-03sfx-gull-cry-04sfx-gull-snatch-01sfx-rat-squeal-startle-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-harbour-contests-life-generate)
Notes
Audio bible §7.9 replaces the needs list's
sfx-watch-whistle-01with the watch's wooden rattle and handbell (sfx-watch-rattle-01,sfx-watch-handbell-01) and the regatta start bell with a wooden clapper (sfx-regatta-start-clapper-01);sfx-bosun-pipe-01is added for the Tern crew.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 25 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.94Generate harbour contest, alarm and gull SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-harbour-contests-life-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 25 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
- mw-e38.93 Write harbour contest, alarm and gull SFX prompts (25 assets) open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.95Owner approval: harbour contest, alarm and gull SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 25 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-harbour-contests-life-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: crowd sounds carry emotion with no words, the watch, smuggler and crew alarms are instantly distinguishable, brawl hits read comic not brutal, and the rat startle is loud enough to matter as a noise event. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 25 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then crowd sounds carry emotion with no words, the watch, smuggler and crew alarms are instantly distinguishable, brawl hits read comic not brutal, and the rat startle is loud enough to matter as a noise event.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-harbour-contests-life-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.94 Generate harbour contest, alarm and gull SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.96Integrate approved harbour contest, alarm and gull SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e40-contest-framework,e40-tidefair-strength,e40-tidefair-shoot-pockets,e40-drowned-gull,e40-harbor-opposition,e40-harbor-crowds,e40-tidefair-regatta,e40-harbor-ambient-life. Lower-priority consumers (e40-tidefair-regatta,e40-harbor-ambient-life) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 25 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Tidefair and the Drowned Gull, when a bout, a brawl, the Gull Shoot and a watch alarm run, then final sounds play with correct noise events and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.95 Owner approval: harbour contest, alarm and gull SFX open
- mw-e40.16 Fill the harbour with pickpocketable crowds that give stealth cover open
- mw-e40.18 Add the harbour watch, Low Tide Company and Tern crew as humans open
- mw-e40.5 Run data-defined contests with entry, scoring, cheating and bets done
- mw-e40.6 Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor open
- mw-e40.7 Stage the Tidefair arm-wrestling bout and the Gull brawl open
- mw-e40.8 Stage the Tidefair Gull Shoot and the Pocket Race open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.97Write Saltmarch weapon, arrow and tool SFX prompts (12 assets)
Description
Why
Imported gear must sound foreign and its gameplay-relevant noise must match the sim: a whistling arrow is a lure, glass shatter tears Hushlings, glue-soled boots are quieter than any gait.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Saltmarch sabre ring (thinner, higher than local steel), harpoon-spear haft thrum, sharkskin brigandine rasp layer (quieter than leather), arrows: whistling shriek, netting thwump, tar slap, glass-headed shatter, grapple clink and pay-out; Undertow tools: smoke pearl pop-hiss, glue-soled boot peel, skeleton-key ring jingle, tide-silk rope whisper.
Asset ids (12):
sfx-saltmarch-sabre-ring-01sfx-harpoon-spear-thrum-01sfx-armor-sharkskin-layersfx-arrow-whistling-01sfx-arrow-netting-01sfx-arrow-tar-01sfx-arrow-glass-shatter-01sfx-arrow-grapple-01sfx-smoke-pearl-01sfx-glue-boots-peel-01sfx-key-ring-jingle-01sfx-tide-silk-rope-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-saltmarch-imports-generate)
Notes
Ids follow part A patterns (
sfx-armor-<material>-layer,sfx-arrow-*). No duplicates of part A arrow or armour sounds.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 12 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.98Generate Saltmarch weapon, arrow and tool SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-saltmarch-imports-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 12 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
- mw-e38.97 Write Saltmarch weapon, arrow and tool SFX prompts (12 assets) open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.99Owner approval: Saltmarch weapon, arrow and tool SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 12 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-saltmarch-imports-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the whistling arrow is gameplay-loud and glass shatter is loud enough for its Hushling rule, the brigandine layer sits between cloth and leather, and glue-soled boots are quieter than any sneak step. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the whistling arrow is gameplay-loud and glass shatter is loud enough for its Hushling rule, the brigandine layer sits between cloth and leather, and glue-soled boots are quieter than any sneak step.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-saltmarch-imports-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.98 Generate Saltmarch weapon, arrow and tool SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.100Integrate approved Saltmarch weapon, arrow and tool SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e40-tidemarket,e40-undertow,e28-bow-audio,e28-footsteps,e04-armor-weight.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 12 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the stealth grey-box course, when each import is equipped and used, then final sounds play with noise radii matching the sim and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e04.16 Armor weight sets roll class, stamina regen, poise and noise done
- mw-e28.6 Surface-aware footsteps modulated by stance and armor done
- mw-e28.7 Bow audio: draw creak, release, flyby and payload cues open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
- mw-e38.99 Owner approval: Saltmarch weapon, arrow and tool SFX open
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.102Owner generates main theme and motif anchor in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-anchors-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 3 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.101 Write main theme and motif anchor Suno prompts (3) done
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.103Owner approval: main theme and motif anchor
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-anchors-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each anchor states its motif clearly enough to be recognised when quoted in other tracks, the hymn is calm and plainchant-like, and the Nightjar's song is seductive rather than scary. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 3 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each anchor states its motif clearly enough to be recognised when quoted in other tracks, the hymn is calm and plainchant-like, and the Nightjar's song is seductive rather than scary.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-anchors-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.102 Owner generates main theme and motif anchor in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.104Integrate approved main theme and motif anchor into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e01-title-new-game-flow.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the title screen, when it loads, then
music-title-main-themeloops seamlessly on the music bus; the two motif anchors are present in the music registry for derived tracks. - AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e01.2 Title screen flows into class select, spawn and continue done
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.105Write Briar Glen music family Suno prompts (10)
Description
Why
Briar Glen day, night, the inn, the bookshop and the first-night sleepwalk are the most-heard music in the game and establish "warm but uneasy".
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Day family (D Dorian 88 BPM 6/8: explore, tension, combat), night family (D Aeolian 72 BPM: explore with faint Nightjar fragments on still nights, stealth, tension, combat), the Sleeping Ox (diegetic, G Mixolydian 108), the Lamplit Stacks (A Lydian 70, magic motif) and the first-night sleepwalk (the Nightjar's song hummed, following Tansy).
Asset ids (10):
music-briar-glen-day-exploremusic-briar-glen-day-tensionmusic-briar-glen-day-combatmusic-briar-glen-night-exploremusic-briar-glen-night-stealthmusic-briar-glen-night-tensionmusic-briar-glen-night-combatmusic-sleeping-ox-exploremusic-lamplit-stacks-exploremusic-first-night-sleepwalk
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-briar-glen-generate, owner)
Notes
Ids follow the audio bible §3 family roots (the world needs list said
music-briar-glen-tavern/-bookshop);music-first-night-sleepwalkis new.Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 10 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.106Owner generates Briar Glen music family in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-briar-glen-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 10 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.105 Write Briar Glen music family Suno prompts (10) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.107Owner approval: Briar Glen music family
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-briar-glen-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each family shares key and tempo so bar-synced cuts are seamless, day feels safe and warm, night uneasy, and the sleepwalk quotes the Nightjar's song recognisably. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each family shares key and tempo so bar-synced cuts are seamless, day feels safe and warm, night uneasy, and the sleepwalk quotes the Nightjar's song recognisably.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-briar-glen-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.106 Owner generates Briar Glen music family in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.108Integrate approved Briar Glen music family into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e24-sleeping-ox,e24-lamplit-stacks,e24-first-night. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given Briar Glen, when the world clock passes day→night and a guard escalates suspicious→combat, then the matching family tracks cross-fade on bar boundaries and the inn/bookshop tracks play indoors, with no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e24.14 Stage the first-night sleepwalk from the Ox to the mine gate open
- mw-e24.4 Build the Sleeping Ox inn: common room, rooms, cellar and rest open
- mw-e24.5 Author the Lamplit Stacks bookshop on the e21 authoring template open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.107 Owner approval: Briar Glen music family open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.109Write wilderness music families Suno prompts (7)
Description
Why
The wilderness music must feel open and old, cooler as you go deeper, with enough stealth support for the wolf den and the watchtower.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Briarwood family (A Aeolian 80 BPM: explore, stealth, tension, combat) and Mooring's Watch family (E Aeolian 76 BPM: explore, tension, combat).
Asset ids (7):
music-briarwood-exploremusic-briarwood-stealthmusic-briarwood-tensionmusic-briarwood-combatmusic-moorings-watch-exploremusic-moorings-watch-tensionmusic-moorings-watch-combat
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-wilds-generate, owner)
Notes
Ids follow audio bible §3 (
music-briarwood,music-moorings-watch; needs list saidmusic-forest,music-watchtower).Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 7 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.110Owner generates wilderness music families in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-wilds-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 7 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.109 Write wilderness music families Suno prompts (7) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.111Owner approval: wilderness music families
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-wilds-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each family is key/tempo-locked, the Briarwood feels open and hushed (no wolf howls), and Mooring's Watch feels windswept and dutiful. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 7 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each family is key/tempo-locked, the Briarwood feels open and hushed (no wolf howls), and Mooring's Watch feels windswept and dutiful.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-wilds-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.110 Owner generates wilderness music families in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.112Integrate approved wilderness music families into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers: . Art-dress / lighting-audio beads that wait on this integrate (top-level edges):
e25-wilds-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the Briarwood and Mooring's Watch, when the player sneaks past wolves and fights at the tower, then explore/stealth/tension/combat tracks switch on bar boundaries with 4 s / 6 s hysteresis and no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.111 Owner approval: wilderness music families open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.113Write mine and priory music families Suno prompts (8)
Description
Why
The mine is sparse and tense below the Bell Line; the priory is choral and melancholy — the Vesper hymn's home.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Deepworks family (C♯ Phrygian 64 BPM: sparse explore, stealth, tension, combat, quoting the Knot motif below the Bell Line) and Bellwater Priory family (F Dorian 60 BPM, choir: explore, stealth, tension, combat, quoting the Vesper hymn).
Asset ids (8):
music-deepworks-exploremusic-deepworks-stealthmusic-deepworks-tensionmusic-deepworks-combatmusic-bellwater-priory-exploremusic-bellwater-priory-stealthmusic-bellwater-priory-tensionmusic-bellwater-priory-combat
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-deepworks-priory-generate, owner)
Notes
Ids follow audio bible §3 (
music-deepworks,music-bellwater-priory; needs list saidmusic-old-mine,music-monastery).Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 8 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.114Owner generates mine and priory music families in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-deepworks-priory-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 8 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.113 Write mine and priory music families Suno prompts (8) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.115Owner approval: mine and priory music families
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-deepworks-priory-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the Deepworks leaves room for silence and ambience, the priory quotes the Vesper hymn recognisably, and both families cut seamlessly between states. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the Deepworks leaves room for silence and ambience, the priory quotes the Vesper hymn recognisably, and both families cut seamlessly between states.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-deepworks-priory-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.114 Owner generates mine and priory music families in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.116Integrate approved mine and priory music families into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers: . Art-dress / lighting-audio beads that wait on this integrate (top-level edges):
e26-dungeon-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the Deepworks and the priory, when goblins are alerted and the Forgotten engage, then state tracks switch on bar boundaries and the silence budget returns to ambience after 1–2 explore loops.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.115 Owner approval: mine and priory music families open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.117Write Knot, Fold and Cradle music Suno prompts (8)
Description
Why
The Knot's never-resolving turn, the Fold's tender reveal and the Cradle's drifting lullaby are the emotional spine of Act II–III.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Knot family (E Phrygian 58 BPM: drone explore, stealth, tension, combat), the Fold (E Lydian 66: Horn's reveal harmony), the Cradle (explore drone and tension with the Nightjar's song drifting out of tune), and a Nightjar-song stem layered by
song.intensity(0–3).Asset ids (8):
music-knot-exploremusic-knot-stealthmusic-knot-tensionmusic-knot-combatmusic-knot-fold-exploremusic-knot-cradle-exploremusic-knot-cradle-tensionmusic-knot-stem-song
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-knot-generate, owner)
Notes
Ids follow audio bible §3 and the
music-<family>-stem-<layer>pattern (needs list saidmusic-labyrinth,music-labyrinth-heart,music-labyrinth-stem-song).Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 8 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.118Owner generates Knot, Fold and Cradle music in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-knot-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 8 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.117 Write Knot, Fold and Cradle music Suno prompts (8) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.119Owner approval: Knot, Fold and Cradle music
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-knot-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the Knot motif never resolves, the Fold is unmistakably warm and tender, the Cradle is beautiful-uneasy, and the song stem layers over every Knot state without clashing. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the Knot motif never resolves, the Fold is unmistakably warm and tender, the Cradle is beautiful-uneasy, and the song stem layers over every Knot state without clashing.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-knot-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.118 Owner generates Knot, Fold and Cradle music in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.120Integrate approved Knot, Fold and Cradle music into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e13-song-intensity,e26-horns-hall-fold,e26-cradle-arena. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-dungeon-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the Knot, when
song.intensityis stepped 0→3 via the debug console and the player enters the Fold and the Cradle, then the song stem gain follows intensity, the Fold track takes over on entry, and nothing clips. - AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e26.17 Stage the Cradle arena for the abbot confrontation open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.119 Owner approval: Knot, Fold and Cradle music open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.121Write boss, climb and finale music Suno prompts (8)
Description
Why
The Warden fight (sorrow, not menace), the abbot confrontation (song versus hymn), the Climb and the bell-ringing finale are the MVP's set pieces.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Warden boss (E Phrygian 112 half-time: intro, loop A, loop B leaning toward the reveal harmony), abbot boss (E Phrygian/Locrian 100 in 3/4: intro, loop A, loop B braiding the Nightjar song with the inverted hymn), the Sounding Shaft climb (132, rising E Phrygian → D Dorian) and the Vespers finale (the bell rung, the song falters).
Asset ids (8):
music-boss-warden-intromusic-boss-warden-loop-amusic-boss-warden-loop-bmusic-boss-abbot-intromusic-boss-abbot-loop-amusic-boss-abbot-loop-bmusic-sounding-shaft-climbmusic-vespers-finale
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-bosses-climb-generate, owner)
Notes
Ids follow audio bible §3 (
music-boss-wardenfamily; the needs list saidmusic-boss-minotaur).music-vespers-finaleis new.Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 8 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.122Owner generates boss, climb and finale music in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-bosses-climb-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 8 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.121 Write boss, climb and finale music Suno prompts (8) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.123Owner approval: boss, climb and finale music
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-bosses-climb-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the Warden music is weighty and sad rather than menacing, the abbot music audibly braids song and hymn, the climb drives upward, and the finale resolves into the hymn. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the Warden music is weighty and sad rather than menacing, the abbot music audibly braids song and hymn, the climb drives upward, and the finale resolves into the hymn.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-bosses-climb-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.122 Owner generates boss, climb and finale music in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.124Integrate approved boss, climb and finale music into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e13-horn-encounter,e13-abbot-encounter,e26-sounding-shaft-climb,e26-vesper-bell-finale. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-dungeon-lighting-audio.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given the Warden and Cradle encounters and the climb, when each is started via the debug console, then boss intro → loop A → loop B transitions land on bar boundaries and the finale track plays on the bell-rung fact, with no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e13.14 Build the Warden boss encounter with yield, spare and Trial of Horns open
- mw-e13.16 Build the Cradle encounter with fight, trick, persuade and join paths open
- mw-e26.18 Build the Sounding Shaft climb with defenders and chase variants open
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.123 Owner approval: boss, climb and finale music open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.125Write ending beds and credits Suno prompts (5)
Description
Why
Each of the four endings needs its own epilogue bed so the player feels what their choices made; the credits close on the main theme.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Epilogue beds for "Vespers Rung" (hymn resolved, warm), "The Last Warden" (the Horn motif, alone), "The Hireling's Silence" (prosperous but hollow, a faint song on still nights), "The Long Vespers" (the lullaby, a doorway not a grave), and the credits (D Dorian → D Lydian 84, class idents woven in).
Asset ids (5):
music-ending-vespers-rungmusic-ending-last-wardenmusic-ending-hirelings-silencemusic-ending-long-vespersmusic-credits
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-endings-generate, owner)
Notes
Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 5 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.126Owner generates ending beds and credits in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-endings-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 5 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.125 Write ending beds and credits Suno prompts (5) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.127Owner approval: ending beds and credits
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-endings-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each ending bed is distinct in emotion yet one family, none is despairing, and the credits resolve the main theme. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 5 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each ending bed is distinct in emotion yet one family, none is despairing, and the credits resolve the main theme.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-endings-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.126 Owner generates ending beds and credits in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.128Integrate approved ending beds and credits into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Cut bar-aligned loop points, add -end tails, normalise to the §5.1 targets, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data (family → states).
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e26-ending-states.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then explore/stealth tracks are −18 LUFS ±1 (≤ −1.5 dBTP) and tension/combat/boss tracks −16 LUFS ±1 (≤ −1.0 dBTP), loop regions are whole bars ≥ 60 s with sample-accurate seams, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given each ending state forced via the debug console, when the epilogue plays, then the matching bed plays under the slides and the credits track follows, with no clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e26.23 Build the four endings' level states across town and dungeon open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.127 Owner approval: ending beds and credits open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.129Write world stingers and cues Suno prompts (20)
Description
Why
Location-keyed combat stingers mask state cuts in key (audio bible §2.3), and five story beats plus the diegetic hymn hum need their own cues.
What
Write one prompt file per stinger/cue in
assets/prompts/music/<asset-id>.mdfrom_templates/music-stinger.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.Combat-start (1–2 s) and combat-victory (3–5 s) stingers in each location key (d-dorian = Briar Glen day; d-aeolian = Briar Glen night; a-aeolian = Briarwood; e-aeolian = Mooring's Watch; cs-phrygian = Deepworks; f-dorian = Bellwater Priory; e-phrygian = the Knot and bosses); story stingers fold-reveal (Horn motif reveal variant, 6–10 s), song-swell (Nightjar fragment rising and detuning, 3–6 s), vespers-rung (bell toll + hymn cadence, 6–10 s), horn-sighting (distant Horn in the Outer Ring) and sour-bell (early tongueless strike); and
music-hymn-vesper-hum, a short loopable voice-neutral hum of the Vesper hymn derived from the hymn anchor (the player hums it; Clatter and Mirror Self reuse it).Asset ids (20):
music-stinger-combat-start-d-dorianmusic-stinger-combat-start-d-aeolianmusic-stinger-combat-start-a-aeolianmusic-stinger-combat-start-e-aeolianmusic-stinger-combat-start-cs-phrygianmusic-stinger-combat-start-f-dorianmusic-stinger-combat-start-e-phrygianmusic-stinger-combat-victory-d-dorianmusic-stinger-combat-victory-d-aeolianmusic-stinger-combat-victory-a-aeolianmusic-stinger-combat-victory-e-aeolianmusic-stinger-combat-victory-cs-phrygianmusic-stinger-combat-victory-f-dorianmusic-stinger-combat-victory-e-phrygianmusic-stinger-fold-revealmusic-stinger-song-swellmusic-stinger-vespers-rungmusic-stinger-horn-sightingmusic-stinger-sour-bellmusic-hymn-vesper-hum
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-stingers-world-generate, owner)
Notes
Part A's key-neutral
music-stinger-combat-startstays the fallback; these keyed variants replace it per location.music-stinger-fold-revealis the "Brother Horn reveal" part A referred to asmusic-stinger-horn-reveal.music-stinger-horn-sightingandmusic-stinger-sour-bellare extra world beats from the needs list. The hymn hum may alternatively be produced from the anchor by Claude (pitch/formant processing) if Suno takes contain syllables.Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 20 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
area:musicasset:musicepic:e38milestone:m3mw-e38.130Owner generates world stingers and cues in Suno
Description
Why
Suno has no official API; the owner pastes Claude's prompts (≈ 1 min per track) — contract §1.
What
For each prompt of
e38-asset-music-stingers-world-prompts, the owner pastes Style/Exclude/Lyrics into Suno (paid tier, Instrumental on; formusic-hymn-vesper-humonly, use a wordless hum (no words); reject takes with intelligible syllables), picks the best take and drops the WAV atassets/_incoming/music/<asset-id>.wav.Scope
- In: Owner Suno session
- In: Dropping takes in the incoming folder
- Out: Any audio editing (Claude does it in the integrate bead)
Notes
Record Suno tier and date in a one-line note per take (Claude turns it into the sidecar).
Acceptance criteria
- AC-1 manual Given the 20 prompts, when the owner finishes the session, then a WAV exists at
assets/_incoming/music/<asset-id>.wavfor every id. - AC-2 manual Given each take, when chosen, then it was generated on a paid Suno tier (recorded in the note).
- AC-3 integration Edge: given a missing or unreadable WAV, when Claude's intake check runs, then it lists the missing ids and does not block the ids that are present.
Waits on
- mw-e38.129 Write world stingers and cues Suno prompts (20) open
- mw-e38.8 Spike: prove a key/BPM-locked Suno family with bar-synced cuts open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.131Owner approval: world stingers and cues
Description
Why
Every shipped asset needs an owner decision at the music take gate; CI refuses final assets without one (contract §6).
What
The owner reviews the processed (trimmed, loop-pointed, loudness-normalised) takes played in context and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: music take, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-music-stingers-world-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each keyed stinger sits in its family key, victory stingers quote the main theme, story stingers are recognisable motif statements, and the hymn hum loops cleanly and sounds hummable. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 20 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "music take". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each keyed stinger sits in its family key, victory stingers quote the main theme, story stingers are recognisable motif statements, and the hymn hum loops cleanly and sounds hummable.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-music-stingers-world-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.130 Owner generates world stingers and cues in Suno open
area:musicasset:musicepic:e38milestone:m3needs-humanmw-e38.132Integrate approved world stingers and cues into adaptive music
Description
Why
Music only matters when it plays in the right state, in key, on the bar (audio bible §2).
What
Trim, fade tails, normalise to stinger loudness, encode Opus + AAC with loop/BPM/key sidecars, add CREDITS (Suno tier + date) and approval records, and bind ids in the music state data / stinger table.
Scope
- In: Post-processing, encode, CREDITS, approval records, data binding
- Out: Adaptive music controller logic (
e28-adaptive-music)
Notes
Consumers:
e26-horns-hall-fold,e13-song-intensity,e26-vesper-bell-finale,e26-outer-ring-encounters,e26-priory-belfry,e14-hum-vesper-hymn.Acceptance criteria
- AC-1 integration Given the approved takes, when processed, then each stinger is −16 LUFS ±1 (short-term max), ≤ −1.0 dBTP, transient within 20 ms of start and tail ≤ 2 s, and
e38-audio-validatoris green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with Suno tier, date and prompt path, plus an approval record. - AC-3 e2e Given each location, when combat starts and ends, then its keyed stingers fire on the music bus in key; and given the Fold entry, a song swell, the bell rung, a Horn sighting, an early bell strike and a player hum, then each story cue plays once without clipping.
- AC-4 integration Edge: given an id without an approval record, when the build runs, then its placeholder remains and the remaining-placeholder report lists it.
Waits on
- mw-e13.5 Drive creature behaviour from a global song-intensity world state open
- mw-e14.1 Let the player hum the Vesper hymn after reading the Vesperine Hours open
- mw-e26.12 Populate the Outer Ring: spiders, first Forgotten, Horn sighting open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e26.8 Build the belfry and the sour-bell consequence of early ringing open
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e38.131 Owner approval: world stingers and cues open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
area:musicasset:musicepic:e38milestone:m3mw-e38.133Write Briar Glen and wilderness ambience prompts (15 assets)
Description
Why
Ambience carries place and gameplay (audio bible §7.7): the town that falls silent past midnight, the wood without wolf howls, water you can hear before you see.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Briar Glen: day market, night (crickets, a distant dog, no wolves, the Nightjar faint on still nights), the Sleeping Ox murmur, Brand's Forge hammering, the Lamplit Stacks quiet (clock, pages, Mortimer's rustle), Glen Well cistern drips, cemetery wind. Wilderness: Briarwood day, Briarwood night (the wolves' silence), Briarwood night with howls (ending variant only), millpond water, drowned lane at night (faint mimic whisper), Standing Horn hum, Mooring's Watch wind and banner remnants, Deepworks gate exterior.
Asset ids (15):
amb-briar-glen-day-market-01amb-briar-glen-night-01amb-sleeping-ox-murmur-01amb-brand-forge-hammering-01amb-lamplit-stacks-quiet-01amb-glen-well-cistern-drips-01amb-cemetery-wind-01amb-briarwood-day-01amb-briarwood-night-silent-01amb-briarwood-night-howls-01amb-millpond-water-01amb-drowned-lane-night-01amb-standing-horn-hum-01amb-moorings-watch-wind-01amb-deepworks-gate-exterior-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-amb-surface-generate)
Notes
ElevenLabs clips are short: beds ≥ 30 s are stitched from several generations with crossfaded seams, or sourced from CC0 beds; memorable events (dog bark, bird call) are separate randomised one-shots. Replaces part A placeholders (
amb-placeholder-*).amb-briarwood-night-howls-01is used only by the ending states (e26-ending-states).Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 15 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each bed, when inspected, then it states loop length ≥ 30 s, stereo, the randomised one-shots kept out of the bed, and the §7.7 gameplay role (draught, water, song) it carries.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (ambience −24 LUFS ± 2, ≤ −3.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.134Generate Briar Glen and wilderness ambience via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout, loop-seam preview).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-amb-surface-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds. Loop beds are stitched from multiple generations where the API limits clip length.
Acceptance criteria
- AC-1 integration Given the 15 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then beds are stereo, ≥ 30 s, loop seams pass the discontinuity check, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.133 Write Briar Glen and wilderness ambience prompts (15 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.135Owner approval: Briar Glen and wilderness ambience
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 15 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-amb-surface-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each bed identifies its place with eyes closed, loops without an audible seam over 3 minutes, and the Briarwood night bed is audibly missing wildlife compared with day. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 15 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each bed identifies its place with eyes closed, loops without an audible seam over 3 minutes, and the Briarwood night bed is audibly missing wildlife compared with day.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-amb-surface-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.134 Generate Briar Glen and wilderness ambience via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.136Integrate approved Briar Glen and wilderness ambience
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets / ambience zone data, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-cue-sheets,e24-town-greybox,e25-briarwood-greybox,e25-mooring-watch-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e24-town-lighting-audio,e25-wilds-lighting-audio. Ambience zone playback ise28-ambience(P2); these P1 beds are bound through the cue sheets so they play even if ambience zones are cut — flag if that changes.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 15 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Briar Glen and the wilderness, when the player walks from the Market Green through the Briarwood to the mine gate by day and night, then the matching final beds play and cross-fade with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e24.2 Block out Briar Glen in grey-box with every named building shell done
- mw-e25.1 Block out the Briarwood with paths, clearings and the priory road done
- mw-e25.6 Block out Mooring's Watch: courtyard, tower floors, roof and tunnel done
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e38.135 Owner approval: Briar Glen and wilderness ambience open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.137Write Deepworks, priory and Knot ambience prompts (12 assets)
Description
Why
Dungeon ambience is most of what the player hears (silence budget, audio bible §2.1) and it hides gameplay cues: draughts at weak walls, the song rising toward the Cradle.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Deepworks drips, winch creaks, distant goblin camp; priory nave wind, scriptorium candles, belfry wind with the cracked bell's occasional hum; the Knot's hum, Ossuary bone rattles, Chapel of Echoes, the Fold's warm breathing (sleepers), Sounding Shaft updraft, and the Cradle dream bed carrying the Nightjar's song low and drifting.
Asset ids (12):
amb-deepworks-drips-01amb-deepworks-creaks-01amb-deepworks-goblin-camp-01amb-bellwater-nave-wind-01amb-bellwater-scriptorium-candles-01amb-bellwater-belfry-wind-01amb-knot-hum-01amb-knot-ossuary-bones-01amb-knot-chapel-echoes-01amb-knot-fold-breathing-01amb-knot-sounding-shaft-updraft-01amb-knot-cradle-dream-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-amb-dungeon-generate)
Notes
Ids use canon location names (audio bible §6 example
amb-deepworks-drips-01; the needs list saidamb-old-mine-*,amb-priory-*,amb-fold-*,amb-cradle-*). The Cradle bed is built from the approved Nightjar motif anchor. Replaces part A placeholderamb-placeholder-dungeon-drips-loop-01.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 12 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each bed, when inspected, then it states loop length ≥ 30 s, stereo, the randomised one-shots kept out of the bed, and the §7.7 gameplay role (draught, water, song) it carries.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (ambience −24 LUFS ± 2, ≤ −3.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.138Generate Deepworks, priory and Knot ambience via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout, loop-seam preview).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-amb-dungeon-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds. Loop beds are stitched from multiple generations where the API limits clip length.
Acceptance criteria
- AC-1 integration Given the 12 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then beds are stereo, ≥ 30 s, loop seams pass the discontinuity check, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.137 Write Deepworks, priory and Knot ambience prompts (12 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.139Owner approval: Deepworks, priory and Knot ambience
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 12 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-amb-dungeon-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each bed identifies its space, the Fold bed sounds warm and alive against the cold Knot, and the Cradle bed carries the Nightjar motif recognisably without becoming music. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each bed identifies its space, the Fold bed sounds warm and alive against the cold Knot, and the Cradle bed carries the Nightjar motif recognisably without becoming music.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-amb-dungeon-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.138 Generate Deepworks, priory and Knot ambience via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.140Integrate approved Deepworks, priory and Knot ambience
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets / ambience zone data, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-cue-sheets,e26-deepworks-greybox,e26-priory-greybox,e26-knot-outer-greybox,e26-knot-inner-greybox. Art-dress / lighting-audio beads that wait on this integrate (top-level edges):e26-dungeon-lighting-audio. Ambience zones aree28-ambience(P2); see the note one38-asset-amb-surface-integrate.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 12 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Deepworks, priory and Knot, when the player walks from the winch house to the Cradle, then each final bed plays in its space and cross-fades with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e26.10 Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
- mw-e26.5 Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt done
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e38.139 Owner approval: Deepworks, priory and Knot ambience open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.141Write bell and chime SFX prompts (10 assets)
Description
Why
The Vesper Bell is the game's signature sound (audio bible §7.8): tongueless it is a gameplay-loud mistake, rung it is "wrong and beautiful", mended it is true; the chapel chimes are a sound puzzle.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Tongueless sour strike (wakes the Forgotten, draws Hushlings and Horn), cracked ring with tongue (beating, sour partials, long bloom), mended true ring, the tongue being hung, Mooring's Watch "watch ended" iron bell, and five pitched chapel chimes for the Chimes puzzle.
Asset ids (10):
sfx-vesper-bell-sour-01sfx-vesper-bell-ring-01sfx-vesper-bell-ring-true-01sfx-vesper-bell-tongue-hang-01sfx-watchtower-bell-watch-ended-01sfx-chapel-chime-01sfx-chapel-chime-02sfx-chapel-chime-03sfx-chapel-chime-04sfx-chapel-chime-05
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-bells-generate)
Notes
Asset id
sfx-vesper-bell-tongue-hang-01follows audio bible §7.8 (sfx-vesper-bell-<variant>-<nn>; needs list saidsfx-bell-tongue-hang-01). Chimes may be synthesised or tuned in the pipeline to guarantee pitch; the bell ring is paired with thevespers-rungstinger. Bells are stereo-positional with long tails, an intentional exception to the dry-SFX rule.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 10 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.142Generate bell and chime SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-bells-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 10 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.141 Write bell and chime SFX prompts (10 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.143Owner approval: bell and chime SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 10 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-bells-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the three Vesper Bell states are instantly distinguishable (harsh clank, beautiful-wrong, true), the watch-ended bell reads as a signal, and the five chimes are distinct, in tune with each other and matched to the puzzle order. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 10 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the three Vesper Bell states are instantly distinguishable (harsh clank, beautiful-wrong, true), the watch-ended bell reads as a signal, and the five chimes are distinct, in tune with each other and matched to the puzzle order.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-bells-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.142 Generate bell and chime SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.144Integrate approved bell and chime SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e26-priory-belfry,e26-vesper-bell-finale,e25-mooring-watch-encounters,e26-chapel-of-echoes,e15-sequence-sound-puzzles.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 10 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the belfry and the Chapel of Echoes, when the bell is struck tongueless, rung and mended, and the chimes puzzle is solved by replay, then the final sounds play positionally with correct noise events and the replay passes.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e15.5 Support ordered, timed and sound-clue puzzle mechanisms done
- mw-e25.7 Place the Hollow Sentinels and goblin lookout with class resolutions open
- mw-e26.14 Build the Chapel of Echoes with the Chimes puzzle and portcullis trap open
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e26.8 Build the belfry and the sour-bell consequence of early ringing open
- mw-e38.143 Owner approval: bell and chime SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.145Write town and wilderness set-piece SFX prompts (9 assets)
Description
Why
Surface mechanisms (sluice, well, drain stone, signal mirror, mine gate) and two story beats (Tansy's hum, the death beat) need sounds players remember.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Sluice gate, the well stone landing twice (the hollow cistern tell), the jail drain stone, signal mirror pivot creak and glint "ting", horn-coin clink (heavier than silver), mine gate boards breaking, Tansy's hummed sleepwalk loop (the Nightjar's song, voice-neutral hum), and the m1 death beat.
Asset ids (9):
sfx-sluice-gate-01sfx-well-stone-double-land-01sfx-jail-drain-stone-01sfx-signal-mirror-rotate-01sfx-signal-mirror-glint-01sfx-horn-coin-clink-01sfx-mine-gate-boards-break-01sfx-tansy-hum-sleepwalk-loop-01sfx-death-beat-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-setpiece-surface-generate)
Notes
sfx-signal-mirror-glint-01andsfx-horn-coin-clink-01are added from audio bible §7.8 signature sounds. Tansy's hum is built from the Nightjar motif anchor (ElevenLabs hum or processed anchor).Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 9 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.146Generate town and wilderness set-piece SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-setpiece-surface-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 9 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.145 Write town and wilderness set-piece SFX prompts (9 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.147Owner approval: town and wilderness set-piece SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 9 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-setpiece-surface-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each mechanism sound tells the player what changed (the double-landing well stone reveals the hollow below), Tansy's hum is recognisably the Nightjar's song, and the death beat is weighty but not despairing. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 9 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each mechanism sound tells the player what changed (the double-landing well stone reveals the hollow below), Tansy's hum is recognisably the Nightjar's song, and the death beat is weighty but not despairing.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-setpiece-surface-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.146 Generate town and wilderness set-piece SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.148Integrate approved town and wilderness set-piece SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e25-millpond-drowned-lane,e24-glen-well-cistern,e24-watch-house-jail,e25-signal-mirror,e25-mine-gate-approach,e24-first-night,e01-death-respawn-loop.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 9 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Briar Glen and the wilderness, when the well stone is dropped, the drain stone lifted, the sluice opened, the mirror turned, the gate boards broken and Tansy followed, then final sounds play with the right noise events and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e01.8 Run the player death and respawn loop done
- mw-e24.10 Hide the cistern route under the Glen Well open
- mw-e24.14 Stage the first-night sleepwalk from the Ox to the mine gate open
- mw-e24.8 Build the Watch House and a jail cell whose drain leads out open
- mw-e25.10 Build the boarded mine gate with every class's way in open
- mw-e25.5 Build the millpond and drowned lane with a Hushling and a lost lantern open
- mw-e25.8 Turn the signal mirror to light the priory's hidden choir door open
- mw-e38.147 Owner approval: town and wilderness set-piece SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.149Write dungeon mechanism and story-beat SFX prompts (12 assets)
Description
Why
Dungeon mechanisms, collapses and the subtle loop-shift cue are how the Deepworks and the Knot talk back; the sleepers waking is the payoff.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Cage lift, mine support collapse, gas explosion, portcullis drop, the Knot loop shift (a subtle cue when runes loop you back), shortcut door unbar, the Fold horn-key door, breach seal, statue rotation (stone on stone), Cradle dream pulse, the sleepers waking, and waking in the Fold after death.
Asset ids (12):
sfx-mine-cage-lift-01sfx-mine-support-collapse-01sfx-mine-gas-explosion-01sfx-portcullis-drop-01sfx-knot-loop-shift-01sfx-shortcut-door-unbar-01sfx-fold-door-horn-key-01sfx-breach-seal-01sfx-statue-rotate-stone-01sfx-cradle-dream-pulse-01sfx-sleepers-wake-01sfx-wake-in-fold-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-setpiece-dungeon-generate)
Notes
Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 12 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.150Generate dungeon mechanism and story-beat SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-setpiece-dungeon-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 12 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.149 Write dungeon mechanism and story-beat SFX prompts (12 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.151Owner approval: dungeon mechanism and story-beat SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 12 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-setpiece-dungeon-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: collapses and explosions are weighty without harshness, the loop-shift cue is subtle enough to be missed by a careless player but learnable, and the sleepers-wake sound is warm and relieving. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then collapses and explosions are weighty without harshness, the loop-shift cue is subtle enough to be missed by a careless player but learnable, and the sleepers-wake sound is warm and relieving.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-setpiece-dungeon-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.150 Generate dungeon mechanism and story-beat SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.152Integrate approved dungeon mechanism and story-beat SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e26-deepworks-encounters,e26-shortcuts-loops,e26-knot-rune-navigation,e26-vane-gallery-breach,e26-priory-choir,e26-cradle-arena,e26-horns-hall-fold,e26-chapel-of-echoes,e26-vesper-bell-finale,e01-contextual-respawn.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 12 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Deepworks and the Knot, when the cage lift runs, a support collapses, the portcullis drops, a rune loop resets the player and the sleepers wake, then final sounds play with the right noise events and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e26.11 Make the Knot loop unless you follow its runes open
- mw-e26.14 Build the Chapel of Echoes with the Chimes puzzle and portcullis trap open
- mw-e26.16 Stage Horn's Hall and the Fold, the reveal and the wake-up point open
- mw-e26.17 Stage the Cradle arena for the abbot confrontation open
- mw-e26.19 Ring the Vesper Bell: hang the tongue, mend it and wake the sleepers open
- mw-e26.20 Wire dungeon shortcuts: crypt, cistern, cage lift and one-way doors open
- mw-e26.3 Populate the Deepworks: goblin camp, miner Forgotten, mine hazards open
- mw-e26.4 Build Vane's sealed gallery, the ledger and the sealable breach open
- mw-e26.6 Place the Watchers statue puzzle in the priory choir open
- mw-e38.151 Owner approval: dungeon mechanism and story-beat SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.153Write Forgotten habit, Clatter and goblin SFX prompts (20 assets)
Description
Why
The Forgotten give themselves away by their habits before you see them, and goblins are people whose gibberish carries emotion (audio bible §7.5).
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Forgotten reassembly, sweeping loop, censer chain loop, pick tapping, murmured prayer breath loop (unintelligible), the calm settle when the hymn is hummed; Clatter's hesitant fidget rattle and flat tuneless hum; goblin chatter (four emotional takes of processed gibberish), alarm pots, alert, hurt, flee, sling whirl, net throw, kettle-helm clank, and Mother Kettleback's lower, rounder voice.
Asset ids (20):
sfx-creature-forgotten-reassemble-01sfx-creature-forgotten-sweep-loop-01sfx-creature-forgotten-censer-loop-01sfx-creature-forgotten-pick-tap-01sfx-creature-forgotten-prayer-breath-loop-01sfx-creature-forgotten-calm-01sfx-creature-clatter-fidget-01sfx-creature-clatter-hum-flat-loop-01sfx-creature-goblin-chatter-01sfx-creature-goblin-chatter-02sfx-creature-goblin-chatter-03sfx-creature-goblin-chatter-04sfx-creature-goblin-alarm-pot-01sfx-creature-goblin-alert-01sfx-creature-goblin-hurt-01sfx-creature-goblin-flee-01sfx-creature-goblin-sling-whirl-01sfx-creature-goblin-net-throw-01sfx-creature-goblin-kettle-helm-clank-01sfx-creature-kettleback-voice-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-creatures-forgotten-goblins-generate)
Notes
Builds on part A's Forgotten set (
e38-asset-sfx-impacts-forgotten-*: idle rattle, alert, telegraphs, hurt, poise break, collapse) — none duplicated. Ids follow audio bible §6sfx-creature-<creature>-<vocal>-<nn>(needs list saidsfx-forgotten-*,sfx-goblin-*).Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 20 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.154Generate Forgotten habit, Clatter and goblin SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-creatures-forgotten-goblins-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 20 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.153 Write Forgotten habit, Clatter and goblin SFX prompts (20 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.155Owner approval: Forgotten habit, Clatter and goblin SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 20 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-creatures-forgotten-goblins-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: habit sounds identify what a Forgotten is doing before it is seen, the calm settle audibly slows the rattle, goblin chatter carries emotion with no intelligible words, and alarm pots are the loudest sound in the set. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 20 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then habit sounds identify what a Forgotten is doing before it is seen, the calm settle audibly slows the rattle, goblin chatter carries emotion with no intelligible words, and alarm pots are the loudest sound in the set.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-creatures-forgotten-goblins-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.154 Generate Forgotten habit, Clatter and goblin SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.156Integrate approved Forgotten habit, Clatter and goblin SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-creature-audio-hooks,e13-forgotten-habits,e13-forgotten-reassembly,e14-clatter,e13-rootcellar-goblins,e14-mother-kettleback.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 20 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Ossuary and the goblin hall, when Forgotten perform habits and are calmed by the hum and goblins chatter, raise the alarm and flee, then final sounds play from the creature audio slots with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e13.3 Forgotten crumble and reassemble unless bones are scattered or burned open
- mw-e13.4 Add monk Forgotten and habit routines that ignore quiet players open
- mw-e13.6 Add Rootcellar goblin combatants: slings, nets and loud alarms open
- mw-e14.3 Bring Mother Kettleback to life: trade, Duggan crate and carrying fees open
- mw-e14.5 Build Clatter: the apologetic skeleton who can remember his name open
- mw-e28.8 Creature audio slots and audible attack telegraphs done
- mw-e38.155 Owner approval: Forgotten habit, Clatter and goblin SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.157Write Brother Horn SFX prompts (12 assets)
Description
Why
The same creature must sound terrifying and sad (audio bible §7.5): effortful and reluctant in the fight, soft and lowing in the reveal.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Charge bellow, four heavy hoof footsteps on stone, grab, snorts, stone axe scrape, effortful hurt, low Hornfolk throat-song hum (calm loop), a gentle lowing call, and the yield exhale.
Asset ids (12):
sfx-creature-horn-bellow-charge-01sfx-creature-horn-footstep-heavy-01sfx-creature-horn-footstep-heavy-02sfx-creature-horn-footstep-heavy-03sfx-creature-horn-footstep-heavy-04sfx-creature-horn-grab-01sfx-creature-horn-snort-01sfx-creature-horn-axe-scrape-01sfx-creature-horn-hurt-01sfx-creature-horn-throat-song-loop-01sfx-creature-horn-lowing-call-01sfx-creature-horn-yield-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-creatures-horn-generate)
Notes
Ids follow audio bible §6 (
sfx-creature-horn-bellow-02example; needs list saidsfx-horn-*).Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 12 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.158Generate Brother Horn SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-creatures-horn-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 12 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.157 Write Brother Horn SFX prompts (12 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.159Owner approval: Brother Horn SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 12 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-creatures-horn-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: combat sounds are huge but reluctant rather than bloodthirsty, the throat-song and lowing read as gentle, and footsteps convey nine feet of weight. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then combat sounds are huge but reluctant rather than bloodthirsty, the throat-song and lowing read as gentle, and footsteps convey nine feet of weight.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-creatures-horn-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.158 Generate Brother Horn SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.160Integrate approved Brother Horn SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-creature-audio-hooks,e13-horn-moveset,e14-horn-befriend-help.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 12 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given Horn's Hall, when the Warden encounter and the befriend path run, then final Horn sounds play from his audio slots, footsteps follow animation markers, and no placeholder flags remain.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e13.13 Add Brother Horn's creature data: charges, grabs and throws to stop open
- mw-e14.6 Befriend and help Brother Horn without fighting open
- mw-e28.8 Creature audio slots and audible attack telegraphs done
- mw-e38.159 Owner approval: Brother Horn SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.161Write bestiary SFX prompts (22 assets)
Description
Why
Each systemic creature needs a sonic tell the player can act on: the pant of a silent pack, the skitter of spiders, the drilled march of a sentinel, a mimic's breath.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Briar Wolf pack pant, growl, fear whimper, eating (no howls); Loom Spider skitter loop, web spit, hiss, light recoil, silk-spin loop; Hollow Sentinel armour clank, salute, halting march step, hollow resonance loop; Tallow Ooze squelch, melt sizzle, frozen shatter, candle-stub clatter; mimic breath tell, bite, book page-snap, chest hinge groan, lectern creak.
Asset ids (22):
sfx-creature-briar-wolf-pack-pant-01sfx-creature-briar-wolf-growl-01sfx-creature-briar-wolf-whimper-01sfx-creature-briar-wolf-eat-01sfx-creature-loom-spider-skitter-loop-01sfx-creature-loom-spider-web-spit-01sfx-creature-loom-spider-hiss-01sfx-creature-loom-spider-light-recoil-01sfx-creature-loom-spider-silk-spin-loop-01sfx-creature-hollow-sentinel-clank-01sfx-creature-hollow-sentinel-salute-01sfx-creature-hollow-sentinel-march-01sfx-creature-hollow-sentinel-resonance-loop-01sfx-creature-tallow-ooze-squelch-01sfx-creature-tallow-ooze-melt-01sfx-creature-tallow-ooze-frozen-shatter-01sfx-creature-tallow-ooze-wick-clatter-01sfx-creature-mimic-breath-tell-01sfx-creature-mimic-bite-01sfx-creature-mimic-book-page-snap-01sfx-creature-mimic-chest-hinge-groan-01sfx-creature-mimic-lectern-creak-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-creatures-bestiary-generate)
Notes
Ids follow audio bible §6 with canon short names (needs list said
sfx-wolf-*,sfx-loom-spider-*,sfx-sentinel-*,sfx-tallow-ooze-*,sfx-mimic-*).Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 22 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.162Generate bestiary SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-creatures-bestiary-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 22 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.161 Write bestiary SFX prompts (22 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.163Owner approval: bestiary SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 22 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-creatures-bestiary-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each creature is identifiable by sound alone, wolves never howl, the ooze sounds waxy rather than elemental, and the mimic breath tell is quiet but audible within 3 m. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 22 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each creature is identifiable by sound alone, wolves never howl, the ooze sounds waxy rather than elemental, and the mimic breath tell is quiet but audible within 3 m.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-creatures-bestiary-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.162 Generate bestiary SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.164Integrate approved bestiary SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-creature-audio-hooks,e13-briar-wolves,e13-loom-spiders,e13-hollow-sentinels,e13-tallow-ooze,e13-mimics.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 22 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the creature arena, when each creature cycles idle, alert, attack, hurt and flee via the debug console, then final sounds play from its audio slots with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e13.10 Add Tallow Ooze that melts into fire puddles and shatters when frozen open
- mw-e13.11 Add book, lectern and chest mimics with tells and feeding open
- mw-e13.12 Add Hollow Sentinels bound to hold the tower until relieved open
- mw-e13.7 Add Loom Spiders that weave readable webs and fear bright light open
- mw-e13.8 Add silent Briar Wolf packs that can be fed, scared or remembered open
- mw-e28.8 Creature audio slots and audible attack telegraphs done
- mw-e38.163 Owner approval: bestiary SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.165Write Hushling and abbot SFX prompts (8 assets)
Description
Why
Hushlings hunt by sound and mimic the missing; the abbot is gentle presence until he sings — both are the dread the audio bible asks for, quiet and specific.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Hushling whisper-grasp, dissipation (ripping air scattering into whispers), idle hum loop of the Nightjar's song, and the voice-mimic murmur (pitch-contoured, fog-filtered, familiar intonation, no intelligible words; the line appears as a caption); the abbot's sung Nightjar fragment loop (warm wordless male voice, solid state), interrupted-song cut, lantern-chain creak loop, and fog hush loop.
Asset ids (8):
sfx-creature-hushling-whisper-grasp-01sfx-creature-hushling-dissipate-01sfx-creature-hushling-idle-hum-loop-01sfx-creature-hushling-mimic-01sfx-creature-abbot-sing-loop-01sfx-creature-abbot-interrupt-01sfx-creature-abbot-lantern-chain-loop-01sfx-creature-abbot-fog-hush-loop-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-creatures-dream-generate)
Notes
Ids follow audio bible §6 (
sfx-creature-hushling-mimic-01is the bible example). Sung/hummed fragments are built from the Nightjar motif anchor.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 8 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.103 Owner approval: main theme and motif anchor open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.166Generate Hushling and abbot SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-creatures-dream-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.165 Write Hushling and abbot SFX prompts (8 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.167Owner approval: Hushling and abbot SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 8 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-creatures-dream-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the Hushling mimic murmur has a familiar voice's contour with no intelligible words, the hum stopping is noticeable as silence, and the abbot is never a wail. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the Hushling mimic murmur has a familiar voice's contour with no intelligible words, the hum stopping is noticeable as silence, and the abbot is never a wail.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-creatures-dream-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.166 Generate Hushling and abbot SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.168Integrate approved Hushling and abbot SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-creature-audio-hooks,e13-hushlings,e13-abbot-wraith.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 8 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the drowned lane and the Cradle, when a Hushling idles, hunts, mimics and dissipates and the abbot sings and is interrupted, then final sounds play with captions for the mimic lines and no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e13.15 Add the Sleepless Abbot wraith: solid only while singing open
- mw-e13.9 Add blind Hushlings that hunt by sound and break at loud noises open
- mw-e28.8 Creature audio slots and audible attack telegraphs done
- mw-e38.167 Owner approval: Hushling and abbot SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.173Write world surface footsteps prompts (12 assets)
Description
Why
Mud lanes, frozen water, straw and the Ossuary's bone litter are location surfaces with their own noise radius (audio bible §7.3); bone litter is deliberately loud.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Round-robin footsteps (4–6 variants each) for mud, snow-ice (Frost-frozen surfaces), straw and bone-litter in sneak, walk and run gaits.
Asset ids (12):
sfx-foot-mud-sneaksfx-foot-mud-walksfx-foot-mud-runsfx-foot-snow-ice-sneaksfx-foot-snow-ice-walksfx-foot-snow-ice-runsfx-foot-straw-sneaksfx-foot-straw-walksfx-foot-straw-runsfx-foot-bone-litter-sneaksfx-foot-bone-litter-walksfx-foot-bone-litter-run
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-footsteps-world-generate)
Notes
Completes the audio bible §7.3 surface set left to the world planner by
e38-asset-sfx-footsteps-extra-armor-prompts. Ids follow part A's footstep naming (sfx-foot-<surface>-<gait>, variants inside).Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 12 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.174Generate world surface footsteps via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-footsteps-world-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 12 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.173 Write world surface footsteps prompts (12 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.175Owner approval: world surface footsteps
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 12 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-footsteps-world-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: the four surfaces respect the sim loudness ordering (bone-litter among the loudest, straw soft-crunchy) and sneak < walk < run holds within each surface. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 12 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then the four surfaces respect the sim loudness ordering (bone-litter among the loudest, straw soft-crunchy) and sneak < walk < run holds within each surface.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-footsteps-world-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.174 Generate world surface footsteps via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.176Integrate approved world surface footsteps
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-footsteps,e09-surface-acoustics,e02-footstep-events.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 12 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the stealth grey-box course with mud, frozen, straw and bone-litter strips, when the knight and thief walk them in each gait, then final footsteps play with no placeholder flags and noise events match the sim radii.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e02.11 Emit footstep events tagged with surface material and gait done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e28.6 Surface-aware footsteps modulated by stance and armor done
- mw-e38.175 Owner approval: world surface footsteps open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.179Placeholder audio pack is at ~2000 of its 2048 KB budget
Description
Why
PR #142 (mw-e04.20) left the generated placeholder SFX pack at about 2000 KB of its 2048 KB budget. The next few cue additions will fail CI.
What
Shrink the placeholders (lower sample rate/bit depth or shorter durations for placeholder-only sounds, or dedupe shared ones) or, if the budget itself is wrong for the placeholder phase, raise it with a recorded reason. Placeholders must still be keyed by final asset ids.
Acceptance criteria
- AC-1 content Given the placeholder audio pack, when CI's audio placeholder check runs, then the pack uses at most 60% of its budget and every cue still resolves to a sound.
area:musicepic:e38milestone:m3mw-e38.48Write P2 spell SFX prompts (6 assets)
Description
Why
If Fire Wall, Arcane Sight, Snuff and Stasis ship, they need final sound.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Phase sounds for fire-wall, arcane-sight, snuff, stasis.
Asset ids (6):
sfx-spell-fire-fire-wall-spawn-01sfx-spell-fire-fire-wall-loop-01sfx-spell-arcane-arcane-sight-cast-01sfx-spell-shadow-snuff-hush-01sfx-spell-time-stasis-lock-01sfx-spell-time-stasis-release-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-spells-secondary-generate)
Notes
P2 because every spell here is P2; cut together with its spell.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 6 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.49Generate P2 spell SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-spells-secondary-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 6 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.48 Write P2 spell SFX prompts (6 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.50Owner approval: P2 spell SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 6 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-spells-secondary-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each spell's phases sound like one spell and its school is identifiable blind. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 6 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each spell's phases sound like one spell and its school is identifiable blind.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-spells-secondary-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.49 Generate P2 spell SFX via ElevenLabs or CC0 open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3needs-humanmw-e38.51Integrate approved P2 spell SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e06-spell-cue-binding,e08-spell-fire-wall,e08-spell-arcane-sight,e08-spell-snuff,e08-spell-stasis.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 6 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the spell sandbox, when each secondary spell is cast, then every phase plays its final sound with no placeholder flags.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e06.17 Bind spell events to placeholder VFX and SFX cues open
- mw-e08.16 Add Arcane Sight: reveal hidden mechanisms and magical properties open
- mw-e08.19 Add Snuff: extinguish all light sources in a radius open
- mw-e08.23 Add Stasis: freeze one object or creature in time open
- mw-e08.4 Add Fire Wall: a persistent line of flame that blocks and burns open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.50 Owner approval: P2 spell SFX open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxclass:sorcererepic:e38milestone:m3mw-e38.56Write IRs and placeholder ambience prompts (8 assets)
Description
Why
SFX are delivered dry and the engine adds location reverb (audio bible §7.1); convincing spaces need good impulse responses.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Convolution impulse responses for cave, stone hall, wood interior, forest and open field, plus three placeholder ambience loops (dungeon drips, forest day, wind gust) until location ambience lands.
Asset ids (8):
ir-cave-01ir-stone-hall-01ir-wood-interior-01ir-forest-01ir-open-field-01amb-placeholder-dungeon-drips-loop-01amb-placeholder-forest-day-loop-01amb-placeholder-wind-gust-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / element / creature state) per sound
- Out: Generation (
e38-asset-sfx-irs-ambience-generate)
Notes
IRs cannot come from ElevenLabs: Claude synthesises them (scripted noise-decay IRs) or sources CC0-only IRs; no CC-BY-SA/NC sets. Final location ambience comes from the world asset planner; these ambience ids are placeholders only. P2 to match the reverb/ambience beads.
Prompts cite the locked reference sounds from
e38-asset-sonic-lock.Acceptance criteria
- AC-1 content Given the 8 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1.
- AC-5 content Edge: given a prompt that requests intelligible speech, music, or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.57Generate IRs and placeholder ambience via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-irs-ambience-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 8 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.56 Write IRs and placeholder ambience prompts (8 assets) open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.58Owner approval: IRs and placeholder ambience
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 8 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-irs-ambience-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: each IR makes a dry test sound feel like its space without metallic ringing. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 8 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then each IR makes a dry test sound feel like its space without metallic ringing.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-irs-ambience-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.57 Generate IRs and placeholder ambience via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.59Integrate approved IRs and placeholder ambience
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, update cue sheets where ids changed, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-reverb-zones,e28-ambience.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 8 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given reverb zones in the greybox testbed, when the player crosses cave, hall, interior, forest and field volumes, then the matching IR is applied with no audio glitches.
- AC-5 perf Given the audio needed at start, when
e28-audio-perfbudgets are measured, then it stays within the ≤ 8 MB initial audio share. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e28.12 Reverb zones per space: caves, halls, interiors, forest open
- mw-e28.13 Ambient beds and randomised positional one-shots done
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.58 Owner approval: IRs and placeholder ambience open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.169Write Snig and Mortimer SFX prompts (4 assets)
Description
Why
Snig's giggles and Mortimer's contented purr are small charm sounds for the P2 social characters.
What
Write one prompt file per asset (or round-robin set) in
assets/prompts/sfx/<asset-id>.mdfrom_templates/sfx.md, with the SFX PREAMBLE (audio bible §8) verbatim, the category add-on, sourcing (generated vs CC0), round-robin count, sim link, output spec and checklist.Mortimer's warning snap for shoplifters and contented page-rustle purr; Snig's giggle and quick chatter.
Asset ids (4):
sfx-creature-mimic-mortimer-snap-01sfx-creature-mimic-mortimer-purr-01sfx-creature-goblin-snig-giggle-01sfx-creature-goblin-snig-chatter-01
Scope
- In: One prompt file per asset id above
- In: Sim link (surface id / noise radius / creature state / world fact) per sound
- Out: Generation (
e38-asset-sfx-creatures-social-secondary-generate)
Notes
P2 to match
e14-snigande14-mortimer; linked to the creature audio hooks only.Prompts cite the locked reference sounds from
e38-asset-sonic-lock. Canon names followdocs/narrative/story-bible.md; no intelligible speech anywhere (text-only MVP).Acceptance criteria
- AC-1 content Given the 4 asset ids, when the asset validator runs over
assets/prompts/sfx/, then each has exactly one prompt file whose id equals its filename stem and matches audio bible §6 naming. - AC-2 content Given each prompt file, when the validator compares its preamble with docs/audio/audio-bible.md §8, then it matches byte-for-byte.
- AC-3 content Given each repeated sound, when inspected, then its round-robin count is ≥ 3 (footsteps 4–6) and its sim link names the sim id it must match.
- AC-4 content Given each prompt file, when inspected, then it states duration, mono/stereo, loop yes/no and loudness target from audio bible §5.1 (SFX one-shots peak −3 dBFS, ≤ −1.0 dBTP).
- AC-5 content Edge: given a prompt that requests intelligible speech, music (unless the bible names a diegetic melody), or contains an API key pattern, when the validator runs, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.155 Owner approval: Forgotten habit, Clatter and goblin SFX open
- mw-e38.163 Owner approval: bestiary SFX open
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
- mw-e38.64 Lock approved reference sounds for all later SFX prompts open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.170Generate Snig and Mortimer SFX via ElevenLabs or CC0
Description
Why
Claude produces every SFX candidate end-to-end so the owner only listens (audio bible §9.1).
What
Generate each prompt via
e38-sfx-gen-client(or source CC0 pere38-sfx-tool-decision) intoassets/_incoming/<asset-id>/vN.wav, run the audio import pipeline + validator, and build a listening page (playlist grouped by set, loudness readout).Scope
- In: Generation/sourcing, trimming, normalisation via pipeline
- In: Listening page for the approval bead
- Out: Owner approval (
e38-asset-sfx-creatures-social-secondary-approve)
Notes
Record API tier / CC0 source URL per candidate. Paid tier for shipping sounds.
Acceptance criteria
- AC-1 integration Given the 4 prompt files, when the batch completes, then every id (and every round-robin variant) has a
vN.wav+ sidecar (tool or source URL, licence, tier, date) inassets/_incoming/. - AC-2 integration Given the candidates, when
e38-audio-validatorruns, then leading silence ≤ 10 ms, positional sounds are mono, loops have clean seams, and LUFS/true peak are within audio bible §5.1 tolerances. - AC-3 content Given the listening page, when opened, then every id plays with its prompt path and loudness readout.
- AC-4 integration Edge: given the SFX client hits a usage/rate limit or network error mid-batch, when the batch is re-run, then it resumes from the first id without a candidate, never regenerates an id that already has a candidate or an approval record, and logs the back-off without printing any API key.
Waits on
- mw-e38.169 Write Snig and Mortimer SFX prompts (4 assets) open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.171Owner approval: Snig and Mortimer SFX
Description
Why
Every shipped asset needs an owner decision at the sfx batch gate; CI refuses final assets without one (contract §6).
What
The owner reviews the listening page of 4 sounds and approves or rejects each item with a note. Claude records every decision in
assets/approvals.json(asset id, gate: sfx batch, version, SHA-256, approver, date, notes). Rejected items loop back toe38-asset-sfx-creatures-social-secondary-generatewith the note appended to their iteration log.Scope
- In: Owner decision per asset
- In: Approval records written by Claude
- In: Re-queue of rejected items
- Out: Generation or editing by the owner — the owner never edits assets
Notes
Approval criteria: Mortimer's snap is a clear warning and his purr reads contented, and Snig sounds younger and quicker than other goblins. Batch reviews are designed to take ≤ 15 minutes of owner time.
Acceptance criteria
- AC-1 manual Given the review page, when the owner finishes the session, then each of the 4 ids has an approve or reject decision with notes recorded in
assets/approvals.jsonfor gate "sfx batch". - AC-2 manual Given the approved items viewed side by side, when the owner compares them, then Mortimer's snap is a clear warning and his purr reads contented, and Snig sounds younger and quicker than other goblins.
- AC-3 integration Edge: given a rejected id, when its decision is recorded, then it is re-queued for
e38-asset-sfx-creatures-social-secondary-generatewith the owner note in its iteration log, and no already-approved id is regenerated. - AC-4 integration Given an approval record whose SHA-256 does not match the file promoted to
assets/source/, when CI runs, then the approval check fails naming the id.
Waits on
- mw-e37.3 Record owner approval gates and block unapproved assets in CI open
- mw-e38.170 Generate Snig and Mortimer SFX via ElevenLabs or CC0 open
area:sfxasset:sfxepic:e38milestone:m3needs-humanmw-e38.172Integrate approved Snig and Mortimer SFX
Description
Why
Final audio replaces placeholders by file swap under the same ids (audio bible §9.3).
What
Promote approved sources, encode Opus + AAC with sidecars via
e38-audio-import-pipeline, add CREDITS rows and approval records, bind ids in the cue sheets, and clear placeholder flags.Scope
- In: Encode, sidecar, CREDITS, approval records
- In: Placeholder swap in the manifest
- Out: Cue logic and mixing (consuming beads)
Notes
Consumers:
e28-creature-audio-hooks,e14-snig,e14-mortimer. Lower-priority consumers (e14-snig,e14-mortimer) are referenced here but not linked, to avoid a priority inversion.Acceptance criteria
- AC-1 integration Given the approved sources, when the import pipeline and
e38-audio-validatorrun, then.ogg+.m4a+ sidecar exist underpublic/assets/audio/for all 4 ids and validation is green. - AC-2 content Given
assets/CREDITS.md, when audited, then each id has a row with tool/source, licence, tier and date, and an approval record exists. - AC-3 integration Given the placeholder manifest, when integration completes, then no id in this set is flagged
placeholder: true. - AC-4 e2e Given the Lamplit Stacks and the Deepworks, when Mortimer is approached by a shoplifter and Snig is met, then final sounds play with no placeholder flags.
- AC-5 perf Given the owning location's audio package, when
e28-audio-perfbudgets are measured, then streamed audio stays within the location package target (≤ 25 MB total package) and the ≤ 8 MB initial audio share is not exceeded. - AC-6 integration Edge: given an id without an approval record, when the pipeline runs, then it is not promoted, the placeholder keeps playing, and the remaining-placeholder report lists it.
Waits on
- mw-e28.8 Creature audio slots and audible attack telegraphs done
- mw-e38.171 Owner approval: Snig and Mortimer SFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.6 Placeholder audio kit: CC0 stand-ins with final asset ids open
area:sfxasset:sfxepic:e38milestone:m3mw-e38.178Probe the SFX PREAMBLE on a footstep, a spell cast and a UI click
Description
Why
The audio bible was signed off (mw-e38.1) before its SFX PREAMBLE was tested. Three contrasting sounds show whether the preamble produces the tactile, heavy, true palette of audio bible §7 before the SFX batches start.
What
Using the ElevenLabs client (mw-e38.7), generate a stone footstep, an Ember spell cast and a UI click from the audio bible §8 preamble. Run them through the loudness and naming rules (§5, §6), commit them as placeholders with CREDITS rows, and put them up for owner approval. Revise the preamble if they miss.
Scope
- In: 3 probe sounds, placeholder CREDITS rows, preamble revisions
- Out: the SFX batches themselves
Acceptance criteria
- AC-1 manual Given the three probe sounds, when the owner reviews them, then they are approved as matching §7, or the preamble is revised and re-probed.
- AC-2 content Given the committed probes, when validated, then each has a CREDITS row flagged placeholder: yes and meets the §5 loudness targets.
Waits on
- mw-e38.7 ElevenLabs Sound Effects API client for Claude-run SFX generation open
area:musicarea:sfxepic:e38milestone:m3mw-e38.180Placeholder SFX pack is at 2010 KB of its 2 MB budget
Description
Why
PR #181 added a synthesised placeholder telegraph sound; the placeholder pack is now 2010 KB against its 2 MB budget, leaving almost no room for further placeholders until real SFX (ElevenLabs) land.
What
Shrink placeholders (shorter, lower sample rate/mono, shared synth recipes generated at runtime) or revisit the budget with the owner.
Acceptance criteria
AC-1 [ci] The placeholder pack is under 1.8 MB with all referenced cues still resolving.
area:audioarea:musicepic:e38milestone:m3
E39Story & NarrativeThe MVP adventure "The Labyrinth Beneath Briar Glen" is a coherent, reactive story: the town, the Briarwood, the River Wend, Mooring's Watch, the Deepworks, Bellwater Priory, the Knot and the optional harbour town of Wendmouth are populated with memorable characters, class-flavoured dialogue, multi-resolution quests, rumours that lead somewhere, and books/item text that carry the lore. The minotaur twist lands, failure bends the story instead of ending it, and every class experiences the key beats differently.28/48 done · 58%
M3 MVP content 19
mw-e39.38Emit the main-quest events and set its facts from level scripts
Description
mw-e39.4 stages wait on area/event objectives (enterArea briar-glen, mine-gate, knot-outer-ring, ossuary-galleries, horns-hall, the-fold, the-cradle, belfry; events horned-shape-seen, friend-note-found, abbot-revealed, bell-tongue-destroyed, recast-tongue-delivered, deepworks-entered-<route> x6) and on facts nothing writes yet: bell.rung (belfry scene), tern.sailed is written by the quest, crate.intercepted (Crate for Mr. S), bounty.outcome and horn.spoken-for (Vane/Hollis/Hale dialogue), epilogue.finished (slideshow). Needs mw-e23.3 bindings.
mw-e39.18Write failure-state narratives: the Fold, jail, capture, sour bell
Description
Why
"Failure creates stories." Death, capture and botched actions should land the player somewhere new and be remembered.
What
As a player, I want failures to move the story sideways so that I keep playing instead of reloading. Content and fact wiring per bible §4.5 and §9: waking in the Fold after dying in the Knot (pre- and post-reveal text; goblin camp or Forgotten variants if Horn is dead), waking at the Sleeping Ox after a forest death, jail with the drain escape, goblin capture, ringing the tongueless bell, misfired Lull dream, NPCs remembering embarrassments; Wendmouth: thrown off the pier (breakwater sea-door), losing the drinking contest (wake in the Gilded Tern's hold), books confiscated at customs (customs strongroom).
Scope
- In: narrative/failure data: wake-up texts and conditions
- In: facts: player.rescues.byHorn, player.jailed.count, player.captured.byGoblins, bell.rung.sour
- In: NPC remark hooks for embarrassment facts
- Out: respawn mechanics and save rules (Related: E01/E30)
- Out: jail/escape level geometry (Related: E24)
Notes
Kettleback's "carrying fee" must never exceed what the player can pay (take a trinket if broke).
Acceptance criteria
- AC-1 unit Given a death in the Knot with horn.fate = alive and quest before the reveal, when the wake-up is resolved, then the eerie Fold text plays and player.rescues.byHorn increments by 1.
- AC-2 unit Given horn.fate = dead, when a death in the Knot occurs, then the goblin-camp variant plays if goblins.standing ≥ wary, else the Forgotten variant.
- AC-3 unit Edge: given the player has 0 gold, when Kettleback charges the carrying fee, then a trinket is taken instead, or waived if none, and no negative gold results.
- AC-4 unit Given the bell is rung without its tongue, when resolved, then bell.rung.sour is set and the Hushling/Horn-approach encounter flag is raised.
- AC-5 unit Given the player is caught by dockhands in Wendmouth, when resolved, then the thrown-off-the-pier text plays, the player is placed at the breakwater and harbor.seadoor.discovered is set; given restricted books are confiscated, then they move to the customs strongroom container, never destroyed.
- AC-6 manual Given a playtest, when a tester dies in the Knot twice, then they describe the Fold wake-up as intriguing rather than as a punishment.
Waits on
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
area:narrativeclass:allepic:e39milestone:m3size:smw-e39.14Write spellbook titles, authors and flavour texts
Description
Why
For the sorcerer, books are progression; each spellbook should feel like a found object with a voice, not a stat card.
What
As a sorcerer, I want each spellbook to have a title, an author and a human detail so that buying or finding one feels special. Flavour for every MVP spell (~15–20), following bible §7.3 house style, plus Lantern Court seal status (licensed/restricted/forbidden), and damaged-copy variant text for books that teach misfiring versions.
Scope
- In: flavour fields on spellbook item data (title, author, blurb, seal status, damaged variant)
- In: Nine Sleeps ch. 1 (Lull) forbidden flavour
- Out: spell mechanics and the spell catalogue (Related: E08 spell catalogue)
- Out: learning mechanics (e07-spellbook-learning)
Notes
School legality per bible §3.3. Keep each blurb ≤ 80 words and include one concrete, human or funny detail.
Acceptance criteria
- AC-1 unit Given the spellbook data, when validated, then every spell in the MVP catalogue has exactly one spellbook with title, author, blurb and seal status.
- AC-2 unit Given each spellbook, when its seal status is compared with its school, then it matches the legality table in bible §3.3.
- AC-3 unit Given each blurb, when measured, then it is ≤ 80 words.
- AC-4 content Edge: given a book with a damaged variant, when validated, then the damaged text differs from the intact text and names the misfire flavour.
- AC-5 manual Given a read-through, when the narrative designer reviews all blurbs, then none reads as a numeric stat description.
Waits on
- mw-e07.2 Learn spells by reading and studying book items done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
area:narrativeclass:sorcererepic:e39milestone:m3size:smw-e39.15Write item and creature description text
Description
Why
Lore lives in item text (Dark Souls) and creatures are characters, not target dummies; descriptions should imply history and hint at non-combat options.
What
As a player, I want item and creature descriptions that hint at stories and weaknesses so that reading them helps me play. 1–3 sentence descriptions for every MVP key item, quest item, notable equipment and consumable; descriptions for Wendmouth imports (foreign weapons/armour, exotic arrows, Undertow thief tools) and the harbour's human factions (Low Tide Company, harbour watch, Tern crew); bestiary/inspect text for all 10 creature types in bible §8 (Wendmouth adds none), each hinting at at least one non-lethal or systemic option (fear of light, sound sensitivity, flammability, feeding).
Scope
- In: desc-* fields for items and creatures
- In: bestiary entries with unlock tiers (seen / studied)
- Out: creature stats/behaviour (Related: E12/E13)
- Out: loot placement (Related: E18)
Notes
Voice examples in bible §7.4. Avoid numbers in flavour text; mechanics belong in tooltips.
Acceptance criteria
- AC-1 unit Given item and creature data, when validated, then every MVP item and each of the 10 creature types has a description of 1–3 sentences.
- AC-2 content Given each creature entry, when reviewed against bible §8, then it hints at ≥ 1 non-combat or systemic interaction.
- AC-3 unit Given all flavour text, when scanned, then it contains no digits (numbers belong in tooltips).
- AC-4 unit Edge: given a creature at "seen" tier, when its bestiary entry is shown, then only the seen-tier text is returned and studied-tier hints stay hidden.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.29Horn dialogue follow-ups: failed Trial of Horns, trust after the abbot's gift, vouching rhyme verse
Description
Left out of PR #433 (mw-e39.7): a failed Trial of Horns branch; Horn trusting the player less after accepting the abbot's gift; the public-vouching rhyme verse in ending lines. Owner to confirm canon for each before writing.
area:narrativeepic:e39milestone:m3mw-e39.30Consume harbour dialogue hooks: barge.ride-booked / barge.adit-route-offered travel, lock.gossip-paid, lowtide.job-offered, armwrestle.requested
Description
mw-e39.19 emits hooks nobody consumes yet: barge.ride-booked (route river-run or weeping-adit; fare gold or deal; night refused in dialogue), barge.adit-route-offered, lock.gossip-paid (Old Reed's toll, also reed.toll-paid), lowtide.job-offered, tidefair.gull-shoot-sponsored, undertow.access-granted / undertow.restricted-books-offered, armwrestle.requested (mw-e40.6 owns the match). Wire each to the travel, Kestrel Lock, Undertow, Tidefair systems once Wendmouth exists.
area:narrativeepic:e39milestone:m3mw-e39.31Wire commission.dropped consequences and the horn-coin item to Nell's drop
Description
nell-drowned-gull sets commission.outcome (delivered/kept/faked) and emits commission.dropped, and reads commission.coin-held as a stand-in for the horn-coin item. Needed: the item pickup writing commission.coin-held, the buyer's reaction and epilogue reading commission.outcome (bible 4.3, thief commission).
area:narrativeepic:e39milestone:m3mw-e39.33Add the Wendmouth evidence items and Cray's romance novel, retire the stand-in facts
Description
mw-e39.20 (cray-customs, quell-harbor-agent) reads the stand-in facts evidence.foundry-invoice, evidence.honest-manifest, evidence.dead-clerk-seal and cray.romance-brought; nothing writes them yet and no matching items exist. Needed: add the three evidence items and the romance novel (items, tagged plot-critical where they are quest evidence so Cray never confiscates them), have pickup set the stand-in facts or replace the facts with hasItem conditions in the dialogues, and have the Quell deal (quell.manifest-handed event) give the honest manifest item. Also add the plot-critical tag to the item schema if it does not exist (mw-e39.20 AC-5 test currently checks tags only when present).
area:narrativeepic:e39milestone:m3mw-e39.34Consume Cray's confiscation hook: take restricted spellbooks, never plot-critical items
Description
mw-e39.20 emits cray.confiscate { scope: restricted-spellbooks, exclude: plot-critical,forbidden-plot, route: strongroom } from cray-customs when a sorcerer submits to the search. Nothing consumes it. Needed: take the player's restricted spellbooks into the customs-house strongroom (retrievable via Contraband Pages), skipping any item tagged plot-critical or forbidden-plot; also consume cray.books-released (release the impounded crate) and cray.seizure-opened / cray.writ-explained / cray.bribe-recorded for the quest and ledger.
area:narrativeepic:e39milestone:m3mw-e39.35Implement Tereza's first-blood duel (E04 knight combat hook)
Description
mw-e39.20 (tereza-arms-stall) emits tereza.duel-start { stakes: first-blood, arena: tidemarket } on accept or rematch. Needed: run a first-blood duel (E04 knight combat), then write tereza.duel.fought = true and tereza.duel.victor = player or tereza; the dialogue entries duel-won and duel-lost then set tereza.duel.result and clear fought. Also consume tereza.under-counter-opened / tereza.shop-requested for the Tidemarket stock (mw-e40.10).
area:narrativeepic:e39milestone:m3mw-e39.36Wire Wendmouth scene hooks for Quell and Hesketh (crate, departure, shanghai, crew, boarding)
Description
mw-e39.20 emits quell.crate-opened, quell.manifest-handed, quell.departs, quell.collapsed, quell.guard-job-offered, hesketh.crew-signed, hesketh.board-requested, hesketh.shanghai-scene { outcome: work|released } and reads/writes quell.left and hesketh.shanghaied. Needed when the Wendmouth scene exists: gate Quell's presence (and his office note, speaker narrator) on quell.left; have the Drowned Gull drinking-contest loss set hesketh.shanghaied and run the hold scene; consume crew signing and Tereza-vouched boarding (travel to the Tern); open bay nine's crate on quell.crate-access.
area:narrativeepic:e39milestone:m3mw-e39.37Manual narrative consistency read of all content against the bible
Description
Follow-up to mw-e39.24 (PR #436): the narrative lint landed, but its AC-6 manual full-content read against story-bible.md was not done. Read all dialogue, facts, notes and books once the Wendmouth dialogue (#434/#435) and Horn (#433) land; fix contradictions in a PR.
Waits on
- mw-e39.16 Write opening, first-night and ending/epilogue text done
- mw-e39.17 Write class-specific arrivals, routes and signature beats done
- mw-e39.18 Write failure-state narratives: the Fold, jail, capture, sour bell open
- mw-e39.4 Script the main quest graph with four endings as quest data done
- mw-e39.7 Write Brother Horn's dialogue across all Warden resolutions done
- mw-e39.9 Write the Sleepless Abbot's notes, temptations and Cradle dialogue done
area:narrativeepic:e39milestone:m3mw-e39.39Play the Gilded Tern sailing in world at Act III start
Description
mw-e39.4 sets tern.sailed when Act III begins (warden to second-reveal). Nothing consumes it: the Wendmouth scene should move the Tern out of the pool and the epilogue harbor-tern-ox slide reads it.
area:narrativeepic:e39milestone:m3mw-e39.41Write Horn befriend triggers and priory crypt unseal from the main quest
Description
Dialogue facts name the main quest as writer: horn.hymn-hummed, horn.ring-held, horn.name-stone-held, clatter.vouched, horn.weapon-lowered, horn.trial-won (+horn.befriended); horn.crypt-open should unseal world.priory-crypt.unsealed. Not done in mw-e39.4: they need gesture, item and duel systems.
area:narrativeepic:e39milestone:m3mw-e39.43Consume the abbot's dialogue hooks and write his scene facts (abbot.in-cradle, abbot.wraith-down, tongue, dreamer mark effects)
Description
abbot-cradle (mw-e39.9) emits events nothing consumes yet and reads facts nothing writes yet. Hooks: abbot.reveal-spoken, abbot.help-accepted {class}, abbot.lull-taught (sorcerer; Lull spell E08/E06), abbot.fight-begun (start the wraith fight, E13), abbot.trick-sprung {method: thief|sorcerer|archer}, abbot.persuaded, abbot.joined, abbot.fate-set {fate}. Facts to write from scenes: abbot.in-cradle (Cradle scene, E26), abbot.wraith-down (wraith combat, E13), abbot.tongue-recovered read by the belfry climb. Also: dreamer.marked consequences (Hushlings ignore the player, the song heard in town) belong to the creature and ambience systems. Done: each hook has a consumer and a test, or a documented decision not to.
area:narrativeepic:e39milestone:m3mw-e39.46Hook up The Crate for Mr. S— to the Wendmouth level
Description
quest-crate-for-mr-s (mw-e39.21) completes through enterArea (bonded-warehouse, undertow) and event objectives that Wendmouth does not emit yet: crate-bay-opened, crate-ballast-swapped, crate-resealed, crate-seized, crate-net-cut, crate-fished-out, bay-nine-brawl-won, crate-diverted, crate-collected. The level must emit them from the route obstacles (route-matrix bay-nine, customs-strongroom, gilded-tern), write heist.alarm on the watchman's alarm, and read cray.arrival.flagged, lowtide.standing, tern.sails-with-ballast. Related: mw-e40.20.
area:narrativeepic:e39milestone:m3mw-e39.45Resolve book and scroll textKeys: text for spell tomes and scrolls, and the reader lookup
Description
Story-book page text resolves through bookPageText (src/content/types/readable.ts), but spell tomes and scrolls (mage-hand, ember, scroll-of-ember/firebolt/mage-hand) point at keys (book.mage-hand.page-1, book.scroll-of-ember.text) that resolve nowhere. The spellbook-flavour text (bible 7.3) must land in a text home (extend the book content type or a sibling) and the reader UI (mw-e07.9) must call the lookup. Out of scope of mw-e39.13.
area:narrativeepic:e39milestone:m3mw-e39.47Items for the Crate for Mr. S—: name-stone, honest manifest, Mr. S—'s sealed letter
Description
The quest writes stand-in facts (namestone.obtained, horn.name-stone-held, manifest.honest.obtained, evidence.honest-manifest). Replace with real items (name-stone, manifest, the sealed letter, ballast, customs-seal stamp, forged manifest) and an optional letter fact; Mr. S—'s letter is unreadable beyond one line in the MVP (bible 6.1 quest 6).
area:narrativeepic:e39milestone:m3mw-e39.48Cray and Nell dialogue reactions to the crate outcome
Description
Read crate.method, cray.arrival.flagged, lowtide.standing and heist.undetected in cray-customs and nell-drowned-gull (Cray's arrival branch after the force brawl; Nell hostile under lowtide.standing = hostile; talk of the ballast after an undetected heist).
area:narrativeepic:e39milestone:m3
M4 Fun validation 1
mw-e39.25Run a playtest narrative-comprehension check and fix gaps
Description
Why
A twist no one notices is not a twist. The story must be understandable without a wiki, through play alone.
What
As the narrative designer, I want observed playtests with a short comprehension questionnaire so that I know which beats land and which need clearer delivery. 5+ testers (mix of classes) play through; afterwards they answer: who was taking people and why, what Horn is, who the "friend below" was, what the bell does, what their class did differently. Findings become fix beads or immediate text edits.
Scope
- In: docs/narrative/playtest-comprehension.md (script, questionnaire, results)
- In: text fixes for beats scoring < 60% comprehension
- Out: fun/feel validation (Related: E35 MVP fun validation)
- Out: telemetry dashboards (Related: E34)
Notes
Do not prompt testers about the twist during play; observe only. Record at least one tester per class.
Acceptance criteria
- AC-1 manual Given ≥ 5 testers covering all 4 classes, when the questionnaire is scored, then results per question are recorded in docs/narrative/playtest-comprehension.md.
- AC-2 manual Given the results, when ≥ 4 of 5 testers explain the minotaur twist and the bell solution in their own words, then the check passes; otherwise fixes are made and the check repeated with new testers.
- AC-3 manual Given the results, when any beat scores < 60% comprehension, then a text fix or a follow-up bead is created and linked in the results doc.
- AC-4 content Edge: given a tester reaches an ending the designer did not expect, when analysed, then the fact trail is recorded and checked against the ending rules in e39-main-quest-data.
Waits on
- mw-e39.16 Write opening, first-night and ending/epilogue text done
- mw-e39.24 Add narrative lint for names, canon facts and dangling IDs done
- mw-e39.4 Script the main quest graph with four endings as quest data done
area:narrativearea:qaepic:e39milestone:m4needs-humansize:s
E40Wendmouth HarborWendmouth, the rowdy harbour town at the mouth of the River Wend, is an optional-but-rewarding MVP level where shenanigans ensue: sneakiness is richly rewarded (customs house, bonded warehouse, manifests, rooftops, rigging, crowds, the Undertow), every other class gets hijinks (brawls, arm-wrestling, duels, the Gull Shoot, the Regatta, freezing the harbour pool), and everyone can equip themselves better than in Briar Glen (the Tidemarket and the Undertow). The Crate for Mr. S— heist, the Tidefair and Contraband Pages play out here.2/26 done · 8%
M3 MVP content 23
mw-e40.26Link the towpath and the barge to the Wendmouth Stone Wharf
Description
Planned world-map edges towpath-lock-wharf and barge-lock-wharf (bible 3.1, 3.6; mw-e24.1): the always-open towpath from Kestrel Lock and the morning barge arrive at the Stone Wharf; Wendmouth stays optional for the main quest.
Acceptance criteria
AC-1 integration Given the player at Kestrel Lock, when they walk the towpath or ride the barge in the morning, then the Stone Wharf loads. AC-2 content edges list their transitions and the main quest still never needs them (world:validate AC-5).
area:contentepic:e40milestone:m3mw-e40.2Build the Stone Wharf with working cranes, cargo nets, crates and block yards
Description
Why
The wharf is the harbour's toybox: swinging loads, cuttable nets and crate and block-yard mazes for every class.
What
As a player, I want cranes I can operate, nets I can cut and crates and stone blocks I can climb or hide behind so that the wharf invites hijinks.
The great stepped glenstone quay where stone barges unload: two treadwheel cranes on stone piers (operable winch + jib rotation, suspended loads on ropes), cargo nets (rope supports: cuttable, flammable, shootable), block yards of dressed glenstone in rows (climbable, hiding spots), stacked crates (climbable, hiding spots, pushable), the Lantern Court notice board, the open flagstones marked for Tidefair events, and at the far end the Silver Quay behind an iron gate and imperial guards (a non-enterable set piece: seen, not entered).
Scope
- In: Crane mechanisms, net/rope entities, crate stacks and block yards with material presets, Tidefair space markers, the gated Silver Quay backdrop
- Out: Tidefair event logic (Tidefair items)
- Out: Heist hoist sequence (e40-crate-heist)
Notes
Everything via properties: a net drops when its supporting rope is cut, burnt or shot — never scripted per weapon.
Acceptance criteria
- AC-1 integration Given a loaded cargo net hanging from a crane, when its rope is severed by any means, then the load falls under physics and lands or splashes into the water volume below.
- AC-2 integration Given the crane jib rotated with a load swinging, when the load strikes a creature, then impulse damage applies via the damage model.
- AC-3 integration Given a crate stack pushed over, when it collapses onto the only walkway, then a climb or push route over the debris remains (edge: no softlock).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.13 Support hanging objects on ropes that burn, cut or snap done
- mw-e05.13 Shootable triggers and cuttable ropes activate mechanisms done
- mw-e09.8 Hiding places: wardrobes, barrels, tall grass and shadowed alcoves open
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentepic:e40milestone:m3mw-e40.3Build the Customs House, its strongroom and Cray's book confiscation
Description
Why
Inspector Cray is the MVP's only on-screen Lantern Court official; confiscation turns carrying books into a story.
What
As a sorcerer, I want a customs inspector who confiscates restricted books I carry openly so that smuggling them becomes my own heist.
Customs House: cargo scale, Cray's office (the well-read shelf, confiscated romances), the manifest copy of bay nine, the strongroom of confiscated goods (skylight/roof route, locked door). Inspection: at the barge berth and the customs gate, any book item with legality restricted and no writ in the player's visible inventory is moved to the strongroom as an owned-by-customs item (never forbidden-plot items). Recovery: buy back with a writ, steal back (a crime via e22-crime-bounty), or charm back (Contraband Pages).
Scope
- In: Interior, strongroom container, inspection trigger + legality check, writ condition, confiscation facts
- Out: Contraband Pages quest graph (e39-side-quests-harbor)
- Out: Cray dialogue (e39-dialogue-harbor-trade)
- Out: Crime/bounty rules (e22-crime-bounty)
Notes
Related: e21-stolen-forbidden-stock (P2) for reselling recovered restricted books. Stowaway arrivals (e25-barge-travel) and sneaking past the gate skip inspection.
Acceptance criteria
- AC-1 integration Given the player carries a restricted book without a writ, when inspected, then the book moves to the strongroom container and fact customs.confiscated.<bookId> is set.
- AC-2 integration Given a book flagged as a plot item, when inspected, then it is never confiscated (edge).
- AC-3 integration Given a confiscated book stolen back from the strongroom while witnessed, when reported, then a Wendmouth crime is recorded via e22-crime-bounty.
- AC-4 integration Given the player holds a writ fact, when inspected, then nothing is confiscated and Cray's disposition is unchanged.
Waits on
- mw-e07.1 Define book item schema: spell, language, condition, legality done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
- mw-e39.22 Script harbour side quests The Tidefair and Contraband Pages done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentclass:sorcererepic:e40milestone:m3mw-e40.4Build the Bonded Warehouse: bay nine, skylights, hoists and manifests
Description
Why
The warehouse is the thief's showcase: a sealed bay, a watchman and a desk full of paper.
What
As a thief, I want a warehouse with skylights, hoist beams and a manifest desk so that I can case it and choose my way in.
Sealed bays; bay nine (Vane Deepworks) with a customs-seal entity (intact / broken / re-sealed, stamp-verifiable); skylights (climb, rope-arrow anchor); hoist beams (traversable, can lower crates); the night watchman's patrol and lantern; the manifest desk (swap or forge the bay nine manifest); Jasper Quell's office (foundry invoice, honest manifest, Mr. S— letters).
Scope
- In: Interior, seal state entity, skylight and beam routes, watchman patrol data, manifest desk and Quell's office items
- Out: Heist outcomes (e40-crate-heist)
- Out: Quell dialogue (e39-dialogue-harbor-trade)
Notes
The seal records who stamped it; the dead clerk E. Ardley's stamp is evidence (bible canon).
Acceptance criteria
- AC-1 integration Given the watchman's night patrol, when the stealth replay enters via a skylight, then bay nine can be reached with the watchman never exceeding Suspicious.
- AC-2 integration Given the bay nine seal broken, when re-sealed with the customs-seal stamp item, then the seal reads intact to inspection; without the stamp it reads broken.
- AC-3 integration Given the watchman finds a broken seal on his round, when perceived, then he raises the alarm and the harbour watch investigates (edge: failure creates pursuit, not reload).
- AC-4 content Given the obstacle registry, when validated, then bay nine declares >= 3 class routes.
Waits on
- mw-e05.10 Rope arrows anchor into wood and create climbable ropes open
- mw-e09.9 Guards notice world anomalies: bodies, open doors, dark torches open
- mw-e10.7 Pick locks as an in-world, interruptible, noisy action open
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentclass:thiefepic:e40milestone:m3mw-e40.6Build the Drowned Gull: arm-wrestling, dice, drinking and the trapdoor
Description
Why
Nell's tavern is the harbour's social hub, the thief's drop point and a place where losing lands you somewhere new.
What
As a player, I want a rowdy tavern where I can arm-wrestle Big Oona, drink, gamble, start or end a brawl and find the trapdoor to the Undertow so that Wendmouth's people become my allies or rivals.
Interior on the breakwater end: arm-wrestling table (contest), dice table, drinking contest (losing wakes you shanghaied in the Gilded Tern's hold via a wake-in-place rule), brawl trigger (non-lethal brawling), the thief commission drop with Nell (deliver the coin, keep it, or deliver a fake), and the trapdoor (found by search or granted by Nell's trust).
Scope
- In: Interior, contest instances (arm-wrestling, dice, drinking), brawl trigger, drop interaction facts, trapdoor, wake rule
- Out: Nell and Oona dialogue (e39-dialogue-harbor-folk)
- Out: Tidefair brawl (e40-tidefair-strength)
Notes
Brawls use non-lethal damage tags: knocked-out, never killed, for everyone involved.
Acceptance criteria
- AC-1 integration Given the player loses the drinking contest, when it resolves, then they wake in the Tern's hold, fact tern.shanghaied is set and no reload occurs.
- AC-2 integration Given the thief delivers a fake horn-coin to Nell, when evaluated, then commission.fakeDelivered is set and the payout still occurs.
- AC-3 integration Given a brawl started in the tavern, when it ends, then no participant has died and the watch is not called (dockhands settle it).
- AC-4 integration Given the trapdoor is found without Nell's trust, when used, then the Undertow is reachable but Nell's disposition drops one band (edge).
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e22.9 Start conversations with NPCs in-world and handle interruptions done
- mw-e39.19 Write dialogue for Mags Oakum, Nell Gannet, Big Oona and Old Reed done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
- mw-e40.5 Run data-defined contests with entry, scoring, cheating and bets done
area:contentepic:e40milestone:m3mw-e40.7Stage the Tidefair arm-wrestling bout and the Gull brawl
Description
Why
Knights get hijinks too: beat Big Oona, then survive the free-for-all when someone loses badly.
What
As a knight, I want to arm-wrestle and brawl at the Tidefair so that strength and timing win me coin and friends.
Arm-wrestling: a timed hold contest scored from stamina management and timed pushes (reuses e04 stamina). The Gull brawl: a non-lethal free-for-all on the wharf using melee and hit reactions with a knockout tag; environment pushes into the water are allowed. Both run on the contest framework with cheat rules (magic, dirty tricks) and a lose-gracefully branch (Oona buys you a drink).
Scope
- In: Two contest definitions, arm-wrestle input/stamina binding, brawl non-lethal rules, participants
- Out: Combat systems (E04)
- Out: Quest rewards (e39-side-quests-harbor)
Notes
Any class may enter; knights have the flavour advantage (poise, stamina).
Acceptance criteria
- AC-1 integration Given the arm-wrestling contest, when the player outlasts Oona's stamina, then contest win fires and the Tidefair reputation fact increments.
- AC-2 integration Given the brawl, when any participant reaches 0 health, then they are knocked out (not killed) and leave the brawl.
- AC-3 integration Given the player is shoved into the harbour during the brawl, when they surface, then they are out of the contest and may climb back via a ladder (edge).
Waits on
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
- mw-e40.18 Add the harbour watch, Low Tide Company and Tern crew as humans open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.5 Run data-defined contests with entry, scoring, cheating and bets done
area:contentclass:knightepic:e40milestone:m3mw-e40.8Stage the Tidefair Gull Shoot and the Pocket Race
Description
Why
Archers win shooting contests and thieves lift ribbons from five marks — one of them the Inspector.
What
As an archer, I want to hit wooden gulls on a swinging boom and then the live gull that stole the ribbon; as a thief, I want to lift a ribbon from each of five marked contestants without being caught.
Gull Shoot: boom-mounted targets on a swinging rope (moving-target scoring), then a live harbour gull target that flees to the rooftops. Pocket Race: five marked NPCs wandering the wharf crowd (one is Cray), each carrying a ribbon; caught = out.
Scope
- In: Two contest definitions, swinging boom prop, live-gull chase target, five marked contestants
- Out: Archery and pickpocket mechanics (E05/E10)
Notes
Sabotage is possible: cut the boom rope, or plant a ribbon on a rival. Both have consequences via cheat rules.
Acceptance criteria
- AC-1 integration Given the boom swinging, when an arrow hits a wooden gull, then the hit scores and the gull drops.
- AC-2 integration Given the Pocket Race, when the player lifts all five ribbons unseen, then they win; if Cray notices a lift, then she files a note (fact cray.noticedThief) but does not arrest.
- AC-3 integration Given the live gull flees to a rooftop, when the player follows, then its hoard location becomes reachable and the ribbon is retrievable (edge: prize never lost).
Waits on
- mw-e05.3 Bow draw, aim and release firing selected arrow type in progress
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e40.16 Fill the harbour with pickpocketable crowds that give stealth cover open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.5 Run data-defined contests with entry, scoring, cheating and bets done
area:contentepic:e40milestone:m3mw-e40.10Author the Tidemarket: Tereza's arms stall and the foreign book cart
Description
Why
Everyone can equip themselves better in Wendmouth than in Briar Glen; the Tidemarket is where.
What
As a player of any class, I want an import market with foreign steel, light armour, exotic arrows and rare spellbooks so that the trip downriver upgrades my kit.
Merchant data on e20: Tereza Maelo's arms stall (curved Saltmarch sabres, harpoon-spears, lacquered round shields, sharkskin brigandine; an under-the-counter tier gated by duel/Tidefair facts); exotic arrows as foreign variants of MVP arrow types (whistling = noise, grapple = rope; data only); spice/silk/oddities stall. Foreign bookseller's cart authored on the e21 bookshop template (Storm, Illusion and Gravity texts; restricted ones subject to customs).
Scope
- In: Covered market interior, three merchant files, one bookshop file, under-the-counter conditions
- Out: Arrow mechanics (E05)
- Out: Merchant dialogue (e39-dialogue-harbor-trade)
- Out: Tereza's duel (e40-tereza-duel)
Notes
Related: e05-trick-arrows-extended (P3) — netting, tar and glass-headed arrows appear only if that lands. Mirela pays well for anything the cart won't sell (Related: e24-lamplit-stacks buyback).
Acceptance criteria
- AC-1 content Given the Tidemarket stock, when compared with all Briar Glen merchants, then every class has >= 2 purchasable items not sold in Briar Glen.
- AC-2 content Given the foreign book cart, when pnpm validate:bookshops runs, then it passes and no unique book is duplicated with the Lamplit Stacks.
- AC-3 integration Given the under-the-counter tier, when neither the duel nor a Tidefair win fact is set, then it is hidden (edge); with either, it appears.
- AC-4 content Given the arrow stock, when validated, then every arrow item references an existing e05 arrow type.
Waits on
- mw-e04.16 Armor weight sets roll class, stamina regen, poise and noise done
- mw-e05.1 Data schema for arrow types and property-based payloads done
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e21.9 Provide a bookshop authoring template, validator and fixture shop open
- mw-e39.20 Write dialogue for Inspector Cray, Tereza Maelo, Quell and Hesketh done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentepic:e40milestone:m3mw-e40.12Build the Undertow black market and its smugglers' tunnels
Description
Why
The Undertow sells what the Tidemarket can't — the thief's best tools — and links the harbour underground.
What
As a thief, I want a black market in the smugglers' cellars with tunnels to the tavern and the harbour wall so that I can equip myself and move unseen.
Cellars beneath the fish market; tunnels to the Drowned Gull trapdoor and the breakwater sea-door; Nell Gannet's fence/merchant data: smuggler thief tools (tide-silk rope, glue-soled boots, smoke pearls, skeleton-key ring, customs-seal stamp), fenced goods, and the dockhand disguise kit item. Access: trust, the trapdoor, or the sea-door.
Scope
- In: Cellar scene, tunnels, fence merchant data, thief tool item data (on e10 tool schema)
- Out: Tool mechanics (E10)
- Out: Disguise behaviour (e40-dockhand-disguises)
Notes
Tool items reuse existing verbs: tide-silk rope → rope anchor, glue-soled boots → quieter footsteps on wet surfaces, smoke pearls → smoke escape, skeleton-key ring → lockpick bonus, customs-seal stamp → re-seal action.
Acceptance criteria
- AC-1 content Given Nell's merchant file, when validated, then isFence is true and every tool references an existing capability or tool type.
- AC-2 integration Given the player enters by the sea-door, when first arriving, then the Undertow merchants are Wary rather than Hostile and trade is allowed after one exchange (edge).
- AC-3 integration Given the customs-seal stamp bought, when used on a broken seal, then the seal reads intact (ties to e40-bonded-warehouse).
Waits on
- mw-e10.1 Define thief tool item data: lockpicks, throwables, smoke, traps open
- mw-e10.13 Smoke bombs and flash powder break line of sight for escapes open
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e20.6 Support fences and merchant reactions to stolen goods open
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentclass:thiefepic:e40milestone:m3mw-e40.13Build a continuous rooftop and net-loft route across the waterfront
Description
Why
Climbers should cross the whole harbour above the crowd — and archers need rigging-shot vantages.
What
As a thief, I want a roof route from the Stone Wharf to the breakwater via net lofts so that I can reach skylights, the customs roof and the Tern's rigging line unseen.
Connected roofs and net lofts (hanging nets climbable and flammable), plank bridges, gull-hoard ledges, archer vantage points over the pool and the Tern's rigging.
Scope
- In: Roof surfaces with acoustic tags, climbable nets, plank bridges, vantage points
- Out: Climbing mechanics (E10/E02)
Notes
Tarred roofs are quiet; tin roofs are loud (acoustic tags).
Acceptance criteria
- AC-1 integration Given the rooftop replay, when a thief crosses from the Stone Wharf to the breakwater, then they never touch street navmesh.
- AC-2 integration Given a climbable net ignited, when it burns, then the route segment closes and an alternative segment remains (edge).
- AC-3 integration Given an archer at a vantage point, when aiming at the Tern's hoist rope, then it is within effective bow range and unobstructed.
Waits on
- mw-e02.12 Mantle onto and grab ledges detected from world geometry done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentclass:thiefepic:e40milestone:m3mw-e40.14Build the Gilded Tern: rigging, hold, cabin, locked berth, departure
Description
Why
The merchantman is the heist's destination, a shanghai wake point and a ship you can climb.
What
As a player, I want to board the three-masted Tern by lighter, swim, rigging line, a frozen pool, Tereza's favour or signing on as crew so that the ship becomes a playground.
Ship at anchor in the pool: climbable rigging and yards, deck, hold (crates, shanghai wake spot), Captain Hesketh's cabin, the locked berth of Mr. S—'s agent (never opens in the MVP; interact hooks), crew patrols. World variant: when Act III begins the Tern is gone (fact tern.sailed) with or without Vane's crate.
Scope
- In: Ship scene/area, rigging climb routes, hold and cabin, locked berth, boarding routes, sailed variant
- Out: Crew behaviour (e40-harbor-opposition)
- Out: Hesketh dialogue (e39-dialogue-harbor-trade)
Notes
The pool is standing water: freezing it at night makes a walkable path to the hull (bible sorcerer route).
Acceptance criteria
- AC-1 integration Given the pool frozen by any cold stimulus at night, when walked, then the Tern's hull ladder is reachable on foot.
- AC-2 integration Given fact act3.started, when the harbour loads, then the Tern is absent and tern.sailed is set; the crate outcome is recorded as shipped if not resolved.
- AC-3 integration Given the player is on board when Act III starts, when the harbour next loads, then they are placed on the wharf, never stranded at sea (edge).
- AC-4 content Given the obstacle registry, when validated, then boarding the Tern declares >= 4 class routes.
Waits on
- mw-e02.14 Wade and swim in water and slide on frozen surfaces done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e10.9 Climb tagged surfaces, ledges and ropes with stamina done
- mw-e27.8 Spawn fact-gated entity variants so the world reacts to choices done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentepic:e40milestone:m3mw-e40.15Build the breakwater, sunken figurehead, sea-door and harbour tides
Description
Why
Getting thrown off the pier should land you somewhere new: the figurehead and the smugglers' sea-door.
What
As a player, I want being thrown off the pier to wash me up by a secret door so that failure in Wendmouth opens a route.
Stone breakwater, the sunken figurehead (debunks the sea-serpent rumour 18 by diving), the smugglers' sea-door into the Undertow tunnels. Harbour tide: two standing-water levels scheduled on the world clock (high/low); low tide exposes the sea-door and the crate-drop spot. Wake rule: "thrown off the pier" (subdued in Wendmouth) → surface at the breakwater.
Scope
- In: Breakwater area, figurehead dive spot, sea-door, two-state tide schedule, wake rule content
- Out: Flowing water / waves (not planned)
Notes
Tide is two scheduled water-level states, not simulation.
Acceptance criteria
- AC-1 integration Given the player is subdued by dockhands or the watch in Wendmouth, when resolved, then they surface at the breakwater and harbor.thrownOffPier increments.
- AC-2 integration Given low tide on the clock schedule, when the harbour loads, then the sea-door is above water and enterable; at high tide it is reachable only by swimming (edge).
- AC-3 integration Given the player dives to the figurehead, when reached, then rumour 18 resolves as false.
Waits on
- mw-e01.12 Wake in context after death instead of reloading open
- mw-e02.14 Wade and swim in water and slide on frozen surfaces done
- mw-e22.11 Add rumor system that points players to places and secrets done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentepic:e40milestone:m3mw-e40.16Fill the harbour with pickpocketable crowds that give stealth cover
Description
Why
Pickpocketable crowds make the thief's playground; market days should feel packed.
What
As a thief, I want dense market-day crowds I can blend into and pick pockets from so that sneaking in plain sight is rewarded.
Crowd NPC sets on schedule windows (market days denser), each with a small pocket loot table; crowd volumes reduce the player's visibility score while moving at walk speed inside them; bumping a crowd NPC at run speed emits a noise/complaint.
Scope
- In: Crowd schedule data, pocket loot tables, crowd-cover visibility modifier volumes, bump barks
- Out: Disguises (e40-dockhand-disguises)
- Out: Pickpocket mechanics (e10-pickpocketing)
Notes
Crowd cover is a visibility modifier volume on e09-visibility-model inputs, not a new stealth system.
Acceptance criteria
- AC-1 integration Given the player walking inside a crowd volume, when a watchman's visibility score is computed, then it is reduced by the configured crowd factor versus open ground.
- AC-2 integration Given a crowd NPC pickpocketed unseen, when checked, then an item from its pocket table is taken and no crime is recorded.
- AC-3 perf Given a market-day crowd of 40 agents on the Stone Wharf, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) and p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat (edge: crowd LOD).
Waits on
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e10.8 Pickpocket and plant items on creatures from behind open
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e24.13 Place Briar Glen's NPCs on day/night schedules in progress
- mw-e40.1 Block out Wendmouth harbour in grey-box with every named place done
area:contentclass:thiefepic:e40milestone:m3mw-e40.18Add the harbour watch, Low Tide Company and Tern crew as humans
Description
Why
Wendmouth adds no creature types: its opposition is human and social — bribe, trick, drink with, out-brawl.
What
As a player, I want harbour people who arrest, throw me off the pier or brawl instead of killing me so that getting caught in Wendmouth is a story.
Human creature data on e12: harbour watch (non-lethal by default; arrest via e22-crime-bounty or subdue → thrown off pier), Low Tide Company smugglers (clubs, knives, nets, whistles; alarms via whistles), Tern crew (deck patrols, belaying-pin clubs). Factions and relationships (Low Tide vs watch), bribe/drink affordances, retreat/surrender.
Scope
- In: Three human archetypes as creature data, move sets, faction table entries, affordances, subdue outcomes
- Out: Crime/bounty (e22-crime-bounty)
- Out: Dialogue (e39-dialogue-harbor-folk, e39-dialogue-harbor-trade)
Notes
Readability rules apply to human attacks too. Knives are lethal only if the player draws steel first (escalation fact).
Acceptance criteria
- AC-1 content Given the three human archetypes, when validated, then they pass CreatureDef and all moves pass readability.
- AC-2 integration Given the watch subdues the player without a bounty, when resolved, then the player is thrown off the pier (breakwater wake) rather than jailed.
- AC-3 integration Given a Low Tide smuggler blows a whistle, when heard by allies within range, then they converge on the whistle location.
- AC-4 integration Given the player bribes a watchman with enough coin, when accepted, then that watchman ignores trespass for 10 game minutes (edge: expires).
Waits on
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e11.12 Allies share alerts by shouts, sight signals and alarm devices open
- mw-e11.13 Combat behaviour: engage, space, select attacks and share targets done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.8 Faction table and relationship queries between creatures done
- mw-e22.1 Turn witnessed crimes into settlement bounties with pay/jail/flee open
- mw-e22.12 Let players give gifts and bribes that shift disposition open
- mw-e40.15 Build the breakwater, sunken figurehead, sea-door and harbour tides open
area:contentepic:e40milestone:m3mw-e40.20Place The Crate for Mr. S— heist with all six solution routes
Description
Why
The harbour's centrepiece: a stealth-first heist that every class can solve its own way.
What
As a player of any class, I want to take Vane's relic crate by heist, paper, net, force, ice or leverage so that I win the name-stone and the manifest my way.
Wire the bible §6.1 quest 6 routes into the level: heist (case, roof/skylight entry, dodge the watchman, swap contents for ballast, re-seal with the stamp); paper (swap/forge the manifest so Cray seizes the crate legally, needing Quell's honest manifest or the dead clerk's seal); net (cut the net mid-hoist so the crate drops into the harbour, fish it out at low tide before the Low Tide Company); force (dockside brawl with Nell's crew and the watch; Cray arrives at the end); ice (freeze the pool at night and take it from the hold, or Gust the hoist so it lands on the wharf); leverage (confront Quell with the foundry invoice). Outcomes: name-stone, manifest, sealed letter facts.
Scope
- In: Hoist sequence timing, crate entity with contents state, route wiring, outcome facts, low-tide retrieval spot
- Out: Quest graph (e39-harbor-heist-quest)
- Out: Disguise entry (Related: e40-dockhand-disguises, P2)
Notes
Crate contents per canon: 40 horn-coins, 3 carved stones, 1 name-stone. The name-stone is a befriend-Horn trigger (Related: e14-horn-befriend-help).
Acceptance criteria
- AC-1 integration Given each of the six routes, when its replay runs, then the crate outcome fact is set to the route id and the name-stone is obtainable.
- AC-2 integration Given the perfect heist (contents swapped, seal re-stamped, no alert), when the Tern sails, then heist.perfect is set and no one in Wendmouth reacts.
- AC-3 integration Given the crate dropped into the harbour, when the Low Tide Company reaches it first at low tide, then the crate moves to the Undertow and remains obtainable there (edge: failure relocates).
- AC-4 integration Given Act III starts before any route resolves, when evaluated, then crate.shipped is set and the customs manifest copy still proves the shipments.
Waits on
- mw-e39.21 Script the heist side quest The Crate for Mr. S— done
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
- mw-e40.14 Build the Gilded Tern: rigging, hold, cabin, locked berth, departure open
- mw-e40.15 Build the breakwater, sunken figurehead, sea-door and harbour tides open
- mw-e40.18 Add the harbour watch, Low Tide Company and Tern crew as humans open
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
- mw-e40.4 Build the Bonded Warehouse: bay nine, skylights, hoists and manifests open
area:contentclass:allepic:e40milestone:m3mw-e40.21Declare Wendmouth obstacles and class routes in the registry
Description
Why
Sneakiness is richly rewarded, but every class must get its hijinks; the audit has to see the harbour.
What
As a player of any class, I want every harbour obstacle to offer my class a route so that Wendmouth plays to my fantasy.
Registry entries and replays for: customs strongroom, bay nine, boarding the Tern, the Undertow entry, the crate (six routes), and the Tidefair (one event per class flavour). Obstacles are marked mandatory within their quest (the town itself is optional).
Scope
- In: Registry entries, route affordances added where missing, replays
- Out: Audit gate (e16-route-audit)
Notes
Target 4 classes for bay nine and the Tern.
Acceptance criteria
- AC-1 content Given the registry, when validated, then every E40 quest-mandatory obstacle has >= 3 class routes and bay nine and the Tern have 4.
- AC-2 integration Given each declared route, when replayed, then the obstacle exit fact is set.
- AC-3 integration Given an obstacle whose only thief route needs the P2 disguise, when audited without it, then >= 3 other routes still pass (edge).
Waits on
- mw-e16.2 Declare obstacles and class routes as validated content data open
- mw-e40.13 Build a continuous rooftop and net-loft route across the waterfront open
- mw-e40.20 Place The Crate for Mr. S— heist with all six solution routes open
area:contentclass:allepic:e40milestone:m3mw-e40.22Place Wendmouth rumours, lore texts, secrets and treasure
Description
Why
Rumours 13–18 must lead somewhere; every vaulted warehouse and net loft should hide something.
What
As a curious player, I want harbour gossip that points at real places so that talking in Wendmouth leads to mischief.
Bind rumours 13–18 to sources (Mags, Nell, Old Reed, Tidemarket gossip) with produced resolve facts; place the Lantern Court notice, the bay nine manifest copies, the foundry invoice, Mr. S—'s sealed letter; 10–14 treasures incl. smugglers' stashes and gull hoards.
Scope
- In: Rumour source bindings, fact producers, lore/treasure placements
- Out: Texts (e39-harbor-rumors-texts)
Notes
River rumour 16 resolves in e25-weeping-adit; 18 in e40-breakwater-sea-door.
Acceptance criteria
- AC-1 content Given rumours 13–18, when validated, then each has >= 1 source and a resolvesWhen fact produced by placed content (no warnings).
- AC-2 integration Given rumour 15 heard, when the player enters the Undertow, then it resolves.
- AC-3 integration Given a stash looted, when saved and reloaded, then it stays empty (edge).
Waits on
- mw-e18.5 Support designer-placed and hidden treasure with discovery rules open
- mw-e22.11 Add rumor system that points players to places and secrets done
- mw-e39.23 Write harbour rumours and in-world texts (manifest, notice, tolls) done
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
- mw-e40.3 Build the Customs House, its strongroom and Cray's book confiscation open
area:contentepic:e40milestone:m3mw-e40.23Art-dress Wendmouth with glenstone harbour assets and the Tern
Description
Why
Colossal glenstone quays, vaulted warehouses, net lofts and a three-master: Wendmouth must look loud, cheerful and built like a city next to quiet Briar Glen.
What
As a player, I want Wendmouth to look like a crooked, salt-weathered port built from the valley's stone so that arriving feels like a change of air.
Monumental glenstone harbour kit (stepped quays, mason-marked block breakwater, vaulted warehouses, columned Customs House, treadwheel cranes on stone piers, block yards) with timber net lofts and sheds perched on the stone, props, the Gilded Tern hero model, crowd variants, water surface materials; keep collision and gameplay ids intact.
Scope
- In: Art swap for the harbour, the Tern, interiors, crowd visuals
- Out: Asset creation (E37)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item.
Acceptance criteria
- AC-1 integration Given the dressed harbour, when all E40 route and heist replays run, then they pass unchanged.
- AC-2 perf Given the Stone Wharf on market day with the Tern in view, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent) and draw calls <= 1500.
- AC-3 manual Given the art review, when compared to the style bible, then interactable affordances (climbable nets, cuttable ropes) remain legible against dressing (edge).
Waits on
- mw-e37.13 Shared stylized lit material with ramp, rim and wet/emissive masks open
- mw-e37.14 Legibility kit: visual cues for every exploitable world property open
- mw-e37.150 Integrate approved Wendmouth environment concepts open
- mw-e37.158 Integrate approved Wendmouth tileables and trims open
- mw-e37.200 Integrate approved waterfront and Tidefair props open
- mw-e37.204 Integrate approved harbour interior props open
- mw-e37.210 Integrate approved harbour heist and story props open
- mw-e37.8 Model import pipeline: glTF optimise, meshopt and KTX2 compression open
- mw-e40.16 Fill the harbour with pickpocketable crowds that give stealth cover open
- mw-e40.21 Declare Wendmouth obstacles and class routes in the registry open
area:artepic:e40milestone:m3mw-e40.24Light and score Wendmouth for market day, night and the tavern
Description
Why
Loud and cheerful by day, lantern-lit and sneaky by night: the harbour's mood changes the play.
What
As a player, I want Wendmouth to sound busy by day and to have dark pools of shadow at night so that both hijinks and heists feel right.
Lighting per phase (dock lanterns, ship lamps, lighthouse sweep on the breakwater), music cues (harbour day/night, Drowned Gull diegetic, Tidefair, heist tension), ambience bindings (gulls, rigging creak, surf), reverb zones for cellars.
Scope
- In: Lighting, music bindings, ambience/reverb bindings
- Out: Audio assets (E38)
Notes
Related: final assets come from E37/E38 per-asset prompt/generate/integrate beads created by the asset-prompt wave from plan/world-asset-needs.md; those integrate beads should block this item. Gameplay never waits on this item. Related: e28-ambience, e28-reverb-zones and e29-environment-fx are P2 in the combat plan; bind ambience beds, reverb zones and fog/firefly FX here when they land, otherwise ship lighting + music only.
Acceptance criteria
- AC-1 integration Given the clock crosses dusk, when the phase event fires, then lighting blends to night within 5 s and music switches to the harbour night cue.
- AC-2 integration Given night lighting, when sampled along the warehouse heist routes, then >= 40% of each route has light <= 0.3 (edge: stealth viable).
- AC-3 perf Given night with all lanterns lit and the Tern's lamps, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e28.11 Adaptive music controller with layered stems and stingers done
- mw-e37.242 Integrate approved river and harbour VFX open
- mw-e38.5 Audio import pipeline: normalise, encode Opus/AAC, loop metadata open
- mw-e38.72 Integrate approved Wendmouth music families into adaptive music open
- mw-e38.84 Integrate approved Wendmouth ambience open
- mw-e38.9 Data-driven music state mapping from alert states with hysteresis open
- mw-e40.23 Art-dress Wendmouth with glenstone harbour assets and the Tern open
area:audioepic:e40milestone:m3mw-e40.9Stage the Tidefair Regatta rowing race on the harbour pool
Description
Why
The sorcerer's hijinks: "no magic" is a rule nobody enforces until Cray walks by.
What
As a sorcerer, I want to win a rowing race by gusting my sail or freezing a rival's oars so that magic creates comedy.
Boats are kinematic course-followers on the standing-water pool (no flow sim): speed from a rowing input rhythm, plus force stimuli on the sail (any impulse), minus frozen oars (frozen property halts a boat for 3 s). Cheat rule: magic observed by Cray disqualifies.
Scope
- In: Boat kinematic course entity, rowing input, stimulus responses, contest definition
- Out: Boat physics or flowing water (not planned)
Notes
True extra: cut first if schedule slips.
Acceptance criteria
- AC-1 integration Given a force impulse applied to the player's sail, when evaluated, then boat speed rises by the impulse-derived boost for its duration.
- AC-2 integration Given a rival's oars frozen by any cold stimulus, when evaluated, then that boat halts for 3 s.
- AC-3 integration Given Cray perceives a cast during the race, when detected, then the player is disqualified and fact cray.regattaCheat is set (edge).
Waits on
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e06.8 Apply spell forces: impulse, knockback, pull and lift done
- mw-e40.2 Build the Stone Wharf with working cranes, cargo nets, crates and block yards open
- mw-e40.5 Run data-defined contests with entry, scoring, cheating and bets done
area:contentclass:sorcererepic:e40milestone:m3mw-e40.11Stage Tereza Maelo's first-blood duel
Description
Why
A retired duellist looking for a reason not to be: a knight's signature Wendmouth moment.
What
As a knight, I want to accept Tereza's first-blood duel so that winning unlocks her under-the-counter stock and her respect.
Duel mode: non-lethal, first clean hit landed wins; Tereza uses a fast parry-heavy sabre move set (readability rules apply); dirty tricks count as a loss of honour (fact).
Scope
- In: Duel encounter state, Tereza combat moves as creature data, outcome facts
- Out: Stall stock (e40-tidemarket)
Notes
Any class may duel; knights receive the challenge unprompted.
Acceptance criteria
- AC-1 content Given Tereza's move data, when validated, then every move passes the readability rule.
- AC-2 integration Given the player lands the first clean hit, when resolved, then tereza.duelWon is set and combat ends immediately with no further damage.
- AC-3 integration Given the player uses a thrown distraction or poison during the duel, when detected, then tereza.dishonoured is set and the stock stays hidden (edge).
Waits on
- mw-e04.12 Parry window and riposte critical attack done
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e40.10 Author the Tidemarket: Tereza's arms stall and the foreign book cart open
area:contentclass:knightepic:e40milestone:m3mw-e40.17Let dockhand disguises fool the watch and warehouse crews
Description
Why
A dockhand disguise from the Undertow is the classic heist entry.
What
As a thief, I want a dockhand disguise that lets me walk into restricted wharf areas so that I can case the warehouse in daylight.
Disguise data for the dockhand faction look: accepted by harbour watch and warehouse crew until suspicious behaviour (running, climbing, entering bay nine, picking locks in view) exposes it.
Scope
- In: Disguise definition, restricted-area rules, exposure behaviours for harbour factions
- Out: Disguise system (e10-disguises)
Notes
Related: e40-crate-heist lists the disguise as one heist entry when this lands.
Acceptance criteria
- AC-1 integration Given the disguise worn, when the player walks through the warehouse yard by day, then the watchman stays Unaware.
- AC-2 integration Given the disguised player climbs in view, when perceived, then the disguise is exposed and crime.disguise.exposed fires.
- AC-3 integration Given Nell's crew sees the disguise, when perceived, then they recognise it and do not report (edge: faction-specific).
Waits on
- mw-e10.10 Disguises that fool factions until behaviour gives you away open
- mw-e40.12 Build the Undertow black market and its smugglers' tunnels open
- mw-e40.16 Fill the harbour with pickpocketable crowds that give stealth cover open
area:contentclass:thiefepic:e40milestone:m3mw-e40.19Add harbour gulls that steal shiny things and rats that give you away
Description
Why
Ambient life with systemic hooks: gulls with criminal records and rats that squeal at the worst moment.
What
As a player, I want gulls that snatch unattended shiny items and rats that scatter from light so that the harbour feels alive and mischievous.
Gulls: ambient agents that grab unattended items tagged shiny and fly to rooftop hoards (followable, lootable). Wharf rats: flee light, emit a startle noise event when a sneaking player passes within 1.5 m.
Scope
- In: Two ambient agent definitions (not creature types), hoard containers, startle noise rule
- Out: Creature roster (stays at 10 types)
Notes
Not counted as creature types (bible §8).
Acceptance criteria
- AC-1 integration Given an unattended shiny item on the wharf, when a gull reaches it, then it is carried to the nearest hoard container.
- AC-2 integration Given a sneaking player passes within 1.5 m of a rat, when evaluated, then a startle noise event is emitted that nearby watchers can hear.
- AC-3 integration Given a gull carrying a quest item, when it lands, then the item is placed in a reachable hoard (edge: quest items never lost).
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e12.4 Spawn data-defined creatures into the sim and grey-box scenes done
- mw-e40.13 Build a continuous rooftop and net-loft route across the waterfront open
area:contentepic:e40milestone:m3
M4 Fun validation 1
mw-e40.25Perf-pass and playtest Wendmouth on both presets
Description
Why
Crowds, water, a ship and a brawl in one scene are a perf risk; hijinks must be discovered, not explained.
What
Perf suite runs on market day, the Tidefair brawl and night heist scenes on reference and low-tier machines; a 5-tester playtest (one per class plus one free): do they find a hijink and a gear upgrade unprompted within 30 minutes? Findings in docs/playtests/wendmouth.md with beads.
Scope
- In: Perf captures and fixes, playtest, findings
- Out: Global perf (E32)
Notes
Related: e32-reference-runbook.
Acceptance criteria
- AC-1 perf Given market day with 40 crowd agents on the Stone Wharf, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
- AC-2 perf Given the Tidefair brawl on Low, when measured, then p95 <= 16.7 ms at 1080p on the Low preset on an Intel Iris Xe / base M1 8 GB class machine, with p99 <= 33.3 ms (30 fps floor) during worst-case combat.
- AC-3 perf Given a warm cache, when arriving by barge, then the harbour is playable in <= 3 s and heap stays <= 1.5 GB.
- AC-4 manual Given 5 testers, when playing 30 minutes in Wendmouth, then >= 4 buy or earn a gear upgrade and >= 4 attempt a hijink or heist unprompted.
Waits on
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
- mw-e32.6 Implement Low/High graphics presets and dynamic resolution open
- mw-e40.24 Light and score Wendmouth for market day, night and the tavern open
area:perfepic:e40milestone:m4
E41CI & Release AutomationA PR gets a trustworthy verdict in about 6 minutes, only gates that must run per PR run per PR, the rest run nightly and5/22 done · 23%
Unscheduled 17
mw-e41.4Skip heavy jobs for docs, beads, site and script-only PRs
Description
Why
Bead-export, docs and site PRs run the full 12-minute pipeline although nothing they touch can break it.
What
Add a changes job that classifies the diff and gate the heavy jobs on it. Required checks stay green through an always-running aggregator (the e2e-smoke pattern), because a required check skipped by a path filter blocks merging.
Scope
- In: ci.yml, a classifier script with tests under scripts/
- Out: merge queue (separate bead)
Notes
Default to the full pipeline on any doubt: unknown paths run everything.
Acceptance criteria
- AC-1 e2e Given a PR that changes only .beads, docs, site or plan files, then unit, coverage, e2e, playthrough and build are skipped and e2e-smoke and the other required checks still report success.
- AC-2 unit Given a list of changed paths, when classified, then src, e2e, tests, package or lockfile, vite and playwright config and workflow changes select the full pipeline.
- AC-3 e2e Given a skipped required check, then the PR is still mergeable.
Waits on
- mw-e41.1 Rebalance e2e: isolate cross-browser audio and shard chromium by duration done
mw-e41.11Deploy every main build to a playable preview URL
Description
Why
The vertical slice is complete (PR 210) but the game itself is deployed nowhere; publish-site only ships the landing page and backlog.
What
Reuse the build artifact, sync it to S3 under /play/next/, invalidate that prefix, and run a Playwright smoke against the live URL.
Scope
- In: new workflow, infra/site.yml, a deployed-URL smoke spec
- Out: tagged release (see the release bead); custom domain changes
Notes
Reuse the existing OIDC role pattern from publish-site and its pull_request_target safety rules.
Acceptance criteria
- AC-1 e2e Given a merge to main, then the dist artifact is published under a /play/next/ prefix behind CloudFront with immutable hashed assets cached and index.html short-cached.
- AC-2 e2e Given the deployed URL, then a smoke spec boots the title screen with no console errors.
- AC-3 unit Given infra/site.yml, then the OIDC role can write only that prefix.
Waits on
- mw-e41.3 Build dist once and reuse it in e2e, playthrough and perf jobs open
mw-e41.12Release gate: run the full nightly suite against a release candidate
Description
Why
Moving gates off PRs only works if something still enforces them before a release goes out.
What
A reusable workflow shared by the nightly run and the release workflow, so the release gate is the same code that runs nightly.
Scope
- In: .github/workflows, docs/ci-gates.md
- Out: tagging and publishing (see the release bead)
Notes
Owner-run reference-machine perf measurements (mw-e32) stay a manual release checklist item.
Acceptance criteria
- AC-1 e2e Given workflow_dispatch with a ref, then it runs the release tier (cross-browser audio, bench, perf budgets, playthrough, e2e) and reports one pass or fail.
- AC-2 unit Given docs/ci-gates.md, then the release gate lists exactly the nightly and release tier jobs.
- AC-3 e2e Given a failing gate, then the release workflow does not create a tag.
Waits on
- mw-e41.5 Define per-PR, nightly and release gates and move the slow ones off PRs done
mw-e41.13Cut tagged releases with a dist zip and notes from the changelog
Description
Why
There is no version, tag or release notes (package.json is 0.0.0); the changelog check already forces a note per user-facing PR, so the notes exist and only need compiling.
What
A release workflow and script: verify the gate, roll the changelog, tag, create the GitHub release with the dist zip, and publish to versioned and latest prefixes.
Scope
- In: release workflow, changelog roll script with tests, docs/releasing.md
- Out: auto-publishing on every merge (previews cover that)
Notes
Date-based versions fit a game with no API consumers; revisit if semver is wanted.
Acceptance criteria
- AC-1 e2e Given workflow_dispatch on main with the release gate green, then a date-based tag vYYYY.MM.DD.N and a GitHub release are created.
- AC-2 unit Given CHANGELOG.md, when releasing, then the Unreleased section becomes the dated version section and a fresh Unreleased section remains.
- AC-3 e2e Given the release, then it attaches the dist zip and publishes the build to /play/<tag>/ and /play/latest/.
Waits on
mw-e41.14Build ci-status: a concise CI summary for agents
Description
Why
Agents currently dig through gh run view --log with grep to learn why CI failed; it is slow, lossy and floods context.
What
A pnpm ci:status CLI (PR or run id) that summarizes status, behind-main state, failing tests, flakes and artifact links, with fixtures-based tests of the parsers.
Scope
- In: scripts/ci-status*.ts, package.json, CLAUDE.md pointer
- Out: any CI mutation
Notes
Reads Playwright JSON from the flake report artifact when present; falls back to log parsing.
Acceptance criteria
- AC-1 unit Given a PR number, when its branch is behind main, then the first line says so.
- AC-2 unit Given a failed run, then output lists each failing job, step and test title with the first assertion line, and marks flaky versus failed.
- AC-3 unit Given a green run, then output is one line and never contains raw logs.
mw-e41.15Build ship: update, wait, squash merge and clean up in one command
Description
Why
The manual post-PR checklist (up to date, checks, merge, close beads, delete branch, remove worktree) is repeated about 100 times a week and easy to partly skip.
What
A pnpm ship CLI implementing the working agreement: merge once green, never force, stop on anything unexpected.
Scope
- In: scripts/ship*.ts with injectable gh and bd runners for tests
- Out: PR creation (see pr-open)
Notes
Honours the owner working agreement of 2026-09-27 (agent merges after CI passes).
Acceptance criteria
- AC-1 unit Given a PR behind main, then ship rebases or merges main and stops with a message if CI must re-run.
- AC-2 unit Given green checks, then ship squash merges with branch deletion and closes every Closes bead with the PR number.
- AC-3 unit Given the merge, then it removes the worktree and local branch and runs git fetch --prune.
- AC-4 unit Given red checks or a missing Closes line, then ship refuses and prints why.
Waits on
- mw-e41.14 Build ci-status: a concise CI summary for agents open
mw-e41.6Run only baseline-producing jobs on push to main
Description
Why
With up-to-date branches required, the squash commit's tree equals the tree CI already tested, so re-running everything on push finds nothing new (3 of 29 recent main runs failed on flakes nobody could act on).
What
Limit push-to-main CI to coverage-gate and the perf baseline upload. Revisit if a merge queue lands, where the queue run replaces both.
Scope
- In: ci.yml triggers and job conditions
- Out: nightly gates
Notes
Coverage ratchet and perf compare download artifacts from the latest successful main push run, so those runs must remain.
Acceptance criteria
- AC-1 e2e Given a squash merge to main, then only the jobs that upload the coverage and perf baselines run.
- AC-2 e2e Given the next PR, then coverage-ratchet still finds a baseline from main.
Waits on
- mw-e41.5 Define per-PR, nightly and release gates and move the slow ones off PRs done
mw-e41.7Cut e2e retries to 1 and emit a flake report on every run
Description
Why
retries: 2 triples the cost of every failing test (a 30 s timeout costs 90 s) and hides flakes: a green run with '3 flaky' in a log is invisible.
What
Reduce retries, add the Playwright JSON reporter, parse it into the job summary and upload it with 14-day retention.
Scope
- In: playwright.config.ts, ci.yml, a report script with tests
- Out: nightly aggregation (see the flake aggregator bead)
Notes
Pair with mw-e00.33 and the wall-clock assertion bead: fewer retries only works once the known flakes are fixed or moved.
Acceptance criteria
- AC-1 e2e Given CI, then Playwright retries is 1.
- AC-2 e2e Given a run with a flaky test, then the job summary lists it and a JSON report artifact records spec, title, status and duration.
- AC-3 unit Given Playwright JSON output, when parsed, then flaky, failed and slowest tests are extracted.
mw-e41.9Adopt a GitHub merge queue and drop the rebase-and-rerun cycle
Description
Why
Branch protection has strict: true, so each merge forces every open PR to rebase and re-run about 12 minutes of CI. About 100 PRs merge per week.
What
Enable the merge queue, add merge_group triggers, and update repo-settings.env, configure-repo.sh and docs/repo-settings.md.
Scope
- In: workflows, repo settings scripts and docs
- Out: changing the squash-only policy
Notes
Owner must enable the setting. Once the queue run is the pre-merge verification, the main-push bead can shrink further.
Acceptance criteria
- AC-1 e2e Given the CI and security workflows, then they trigger on merge_group and required checks report there.
- AC-2 unit Given scripts/repo-settings.env and verify-repo-settings.sh, then the merge queue is expected and verified.
- AC-3 e2e Given two PRs queued together, then neither needs a manual rebase to merge.
Waits on
- mw-e41.4 Skip heavy jobs for docs, beads, site and script-only PRs open
mw-e41.10Export beads from a post-merge bot instead of in the next PR
Description
Why
66 of the last 100 merged PRs touched .beads/issues.jsonl, a likely conflict hotspot that also forces extra export commits and rebases.
What
A workflow on push to main that runs bd export and commits the result (or a guarded alternative if branch protection forbids bot pushes), and update the workflow docs in CLAUDE.md.
Scope
- In: workflow, CLAUDE.md and AGENTS.md workflow step
- Out: changing the Dolt sync model
Notes
Verify the conflict claim first by counting rebase conflicts in recent PRs; the bot needs a token that can bypass the required checks or a merge-queue-compatible path.
Acceptance criteria
- AC-1 e2e Given a merge to main that closes beads, then .beads/issues.jsonl on main is regenerated without a human PR.
- AC-2 unit Given a feature PR, then it does not need to touch .beads/issues.jsonl.
- AC-3 e2e Given the publish-site workflow, then it runs after the export lands.
mw-e41.16Build wt-new: claim a bead and create a ready worktree
Description
Why
Every task starts with the same fetch, worktree, install and claim steps; a script makes the required worktree workflow one command.
What
A pnpm wt:new CLI with injectable runners and tests.
Scope
- In: scripts/wt-new*.ts
- Out: cleanup (ship handles it)
Notes
Node 24 is required (engine-strict); the script should select it or fail with the PATH hint.
Acceptance criteria
- AC-1 unit Given a bead id, then wt-new claims it, fetches origin/main and creates .claude/worktrees/<branch> from it.
- AC-2 unit Given the new worktree, then dependencies are installed with the shared store and the bead's acceptance criteria are printed.
- AC-3 unit Given an existing worktree for the bead, then it reports the path instead of creating another.
mw-e41.17Build pr-open: generate the PR body with Closes lines and changelog note
Description
Why
pr-lint and changelog failures are the most common avoidable PR check failures.
What
A pnpm pr:open CLI that rebases on main, builds the body from commits and bead titles, and creates the PR with the required attribution.
Scope
- In: scripts/pr-open*.ts
- Out: merging (ship)
Notes
Reuse scripts/pr-body-lint.ts and changelog-check.ts so rules are not duplicated.
Acceptance criteria
- AC-1 unit Given commits referencing bead ids, then the body has one Closes line per bead.
- AC-2 unit Given the generated body, then scripts/pr-body-lint.ts accepts it.
- AC-3 unit Given user-facing path changes without a changelog entry, then pr-open warns before creating the PR.
mw-e41.18Add pnpm check: lint, format, typecheck, exclusions and affected tests in parallel
Description
Why
lint (90 s) and coverage:exclusions failures are cheap to catch locally but cost a CI cycle.
What
A parallel local gate matching CI's fast checks.
Scope
- In: scripts/check*.ts, package.json, pre-push hook wiring
- Out: e2e
Notes
Keep it under about 60 s on the reference machine.
Acceptance criteria
- AC-1 unit Given a change, then pnpm check runs lint, format:check, typecheck, coverage:exclusions and vitest --changed in parallel and prints one summary.
- AC-2 unit Given a failure, then the exit code is non-zero and the failing command output is shown once.
- AC-3 e2e Given the pre-push hook, then it can call pnpm check.
mw-e41.20Nightly flake aggregator that files beads for repeat offenders
Description
Why
Without aggregation, flakes only cost time; nothing turns them into work.
What
A step in the nightly workflow that downloads recent flake report artifacts, aggregates them, and files or updates beads.
Scope
- In: nightly workflow step, scripts/flake-aggregate*.ts
- Out: auto-quarantine of tests
Notes
Needs bead creation from CI; decide between a committed report file and a GitHub issue per test.
Acceptance criteria
- AC-1 unit Given 14 days of flake reports, then tests flaking in at least 3 runs are listed with counts.
- AC-2 e2e Given a new repeat offender, then one bead is filed or updated and none are duplicated.
- AC-3 unit Given a fixed test with no flakes for 14 days, then it drops off the list.
Waits on
mw-e41.3Build dist once and reuse it in e2e, playthrough and perf jobs
Description
Why
Five jobs (three e2e shards, playthrough, perf) each run vite build through the Playwright webServer, and the build job builds a sixth time.
What
Upload dist from the build job, download it in the e2e jobs, and make the webServer command skip the build when a dist directory is supplied.
Scope
- In: ci.yml, playwright.config.ts, playwright.perf.config.ts
- Out: changing build output or bundle budgets
Notes
Saves roughly 20-30 s per job. Keep the local default (build then preview) so pnpm e2e works unchanged.
Acceptance criteria
- AC-1 e2e Given a PR run, then vite build runs once in the build job and its dist artifact is downloaded by every job that serves the app.
- AC-2 e2e Given the Playwright config in CI, then webServer runs vite preview only and no job other than build runs vite build.
- AC-3 e2e Given a local run, then pnpm e2e still builds and serves the app without extra steps.
mw-e41.19Select affected e2e specs from changed source files
Description
Why
Local and agent runs of 44 e2e specs take minutes; most changes touch one area.
What
A maintained path-to-spec map and a pnpm e2e:affected command, usable locally and by path-aware CI.
Scope
- In: scripts/e2e-affected*.ts, e2e/affected-map.json
- Out: skipping specs in required CI
Notes
Fail safe: unmapped paths run everything.
Acceptance criteria
- AC-1 unit Given changed files under src/ui, then only specs mapped to that area plus smoke are selected.
- AC-2 unit Given a change outside the map, then all specs are selected.
- AC-3 unit Given the map, then every spec file is covered by at least one area.
Waits on
- mw-e41.1 Rebalance e2e: isolate cross-browser audio and shard chromium by duration done
mw-e41.21Build flake-check: run a spec repeatedly under CI-like constraints
Description
Why
Agents need to tell a flake from a regression before pushing.
What
A pnpm flake:check CLI wrapping Playwright and Vitest repeat runs with CI-equivalent settings.
Scope
- In: scripts/flake-check*.ts
- Out: fixing flakes
Notes
Complements mw-e00.33 (CPU-stress reproduction for unit tests).
Acceptance criteria
- AC-1 unit Given a spec path and N, then it runs N times with CI retries and worker settings and reports pass rate and failures.
- AC-2 unit Given a non-zero failure rate, then the exit code is non-zero.
Waits on
- mw-e41.7 Cut e2e retries to 1 and emit a flake report on every run open
E42Open Valley & Town EconomyA grand-feeling valley outside the start: a cliff overlook above Briar Glen and the mountain (backdrop art, tight walkable paths), a bridge over the Wend to enter the village, a river that sinks armoured players, shops (clothes, staves, swords, armor, helmets, scrolls, potions, ingredients, breakable jars, arrows, bows, food, knives, gloves, inn bed), and enough varied skeletons whose gold and gear drops fund real purchases.33/42 done · 79%
M3 MVP content 9
mw-ju8.15Add a shop.deal sim command so buys, sells and buybacks replay deterministically
Description
Why
The shop UI (mw-e20.10) calls Shops.buy/sell/buyBack directly while the sim is paused, so deals are not in the replay log and pack events lag until the next sim step.
What
A shop.deal command (merchant id, op, line/instance id, count) handled in the sim step, so deals are recorded, replayed and emit events on the same tick. UI submits the command instead of calling Shops directly.
Acceptance
- AC-1 integration Given a recorded buy then sell, when replayed, then crowns, pack and merchant state hash match the live run.
- AC-2 unit Given the command, when stepped, then shop.bought fires on that tick.
area:contentarea:simepic:e42milestone:m3mw-ju8.27Run 50 area transitions in the memory-leak harness (mw-e01.11 AC-5)
Description
mw-e01.11 AC-5: 50 back-and-forth area transitions, heap growth after GC <= 5%. The leak harness (mw-e32.12) does not exist yet. Until then src/game/transit/transit.integration.test.ts has an in-process stand-in (50 round trips, carried state does not grow). Transitions reload the page, so the harness should reload-cycle valley-01 <-> valley-02 via ?scene= and the tp console gate crossing.
area:contentepic:e42milestone:m3mw-ju8.30Walk on ice and slide: freeze water under a swimmer, ice sliding, melt drop (mw-e02.14 AC-3/4/5)
Description
mw-e02.14 shipped wading, swimming, sinking, breath, drowning and wetness (src/sim/character/water.ts). Three of its acceptance criteria need the e03 freeze/melt simulation, which does not exist yet (no frozen-water state: only the waterSurface/frozen/friction properties; mw-e03.7 and mw-e03.6 are open), so they are left. AC-3 unit Given a water volume transitions to frozen while the player swims on the surface, when resolved, then the player is placed on top of the ice rather than embedded in it. The water hook (waterTraversal) reads static scene regions (waterVolumes); it needs to read the live water-surface entity/volume from e03.6 (a frozen surface becomes a walkable collider, the volume's surface drops out) and lift a swimmer to the ice top. AC-4 unit Given a frozen surface (or friction < 0.2), when the player stops input at run speed, then they slide >= 2.0 m (vs <= 0.5 m on stone). The controller (src/sim/character/controller.ts locomotion step 2) uses a fixed accelTime/decelTime; it needs the ground's friction property (via the ground collider's owner, as src/sim/climb/surfaces.ts ownerOfCollider does) to scale accel/decel. This part is not blocked by e03.7: ice material already has friction 0.05. AC-5 unit Given the player stands on ice that melts, when the tick runs, then the player drops into the water volume and state becomes wading/swimming. Needs the melt transition from e03.7 restoring the water volume so waterTraversal picks the character up on the next tick.
mw-ju8.32Climb out of the water onto banks, and a quick drop-armor action (mw-e02.14 follow-ups)
Description
mw-e02.14 lets a swimmer or sinker leave the water only where the bottom comes up to wading depth (valley-03 got a ramp up each bank for this). Needed: (1) a swimmer next to a bank edge within reach of the ledge hook mantles out of the water (the ledge hook does not run while the water hook has the character: mode 'swim' is exclusive), with a stamina cost; (2) a quick 'drop armor' action that unequips body armor and shield in one press so a sinking character can shed the load (the load class is already read every tick, so unequipping floats it up: it works today through the inventory screen); (3) price drowning in the damage model (e04): the placeholder is 10 blunt per second tagged environment+drowning, and armor currently absorbs it. AC-1 unit Given a swimmer at the surface beside a 1.5 m bank, when it presses jump toward it, then it mantles onto the bank. AC-2 unit Given a sinking character, when drop armor is pressed, then its armor and shield are unequipped and it rises to the surface.
area:contentarea:simepic:e42milestone:m3mw-ju8.39Playtest: a first-time player finds the sorcery teacher under the bridge at night (mw-ju8.14 AC-3)
Description
mw-ju8.14 AC-3 manual: given a first-time player exploring at night, when they look around Briar Glen, then Idris Quillon is findable without a guide. Route: lane riverbank-path gap past the Bridge Watch post (lit alley) -> under-the-bridge; hinted by Dot/Mirela rumours 19-20 (story bible 6.2; no dialogue system yet, so rumours are not yet playable). Check: is the gap and lantern noticeable; does the player think to go at dusk; do rumours need to ship first. Teacher present 18:00-05:00 only (time set 1 22:00 in the debug console).
area:contentepic:e42milestone:m3needs-humanmw-ju8.41Playtest: Forgotten archer, shield-bearer and brute attacks read clearly
Description
Split from the manual ACs of mw-ju8.23, mw-ju8.24 and mw-ju8.25 (bespoke hand-keyed clips, 2026-11-07). In ?scene=skeleton-pen (or wherever each spawns), watch each Forgotten variant attack: archer draw/aim/loose and shove, shield-bearer raise/hold/bash/counter with the shield covering the torso while guarding, brute's slow lift-hold-slam overhead and wide low sweep. Note any pose that reads wrong (sweep axes are the least certain).
Acceptance criteria
AC-1 manual the owner can name each attack from its pose at mid range, or files a note per clip that reads wrong.
area:contentepic:e42milestone:m3needs-humanmw-ju8.28True in-place area loading (no page reload per crossing)
Description
Area transitions (mw-e01.11, #242) reload the page per crossing because main.ts builds exactly one scene per boot (~650 lines of per-scene setup). It works and guarantees nothing leaks, but is not seamless. Refactor main.ts scene setup into load/unload so a crossing swaps the scene in place, keeping the carry/arrival code. AC-1 integration Given a crossing, then no page reload occurs and the carry/arrival state is identical. AC-2 perf reference-mode area-transition budget improves.
area:contentarea:gameepic:e42milestone:m3mw-ju8.31Swim and wade animation clips, underwater camera tint and muffled audio (mw-e02.14 placeholders)
Description
mw-e02.14 reuses the nearest clips: wading plays the run clip at 60% speed, swimming and sinking publish the locomotion state 'swim', which the grey-box humanoid graph (src/content/data/anim-graph/greybox-humanoid.json) does not map, so the body keeps its previous pose. Needed: a swim (float/stroke) clip and a dive clip, a wade clip with legs through water, and a sinking pose, wired to the 'swim' state (and a dive weight from SwimTraversal.diving). Also: an underwater camera tint or fog and low-pass audio while the head is under (CharacterBreath / breath fraction < 1 and depth > headHeight), swimming stroke and underwater footstep sets, and a surface-ripple VFX. AC-1 e2e Given the player swimming in the river, then the animation probe reports a swim state with a clip, not the held previous pose.
area:artarea:contentepic:e42milestone:m3mw-ju8.33Creature-versus-creature engagement: Bridge Watch guards fight monsters in-scene
Description
Follow-up to mw-ju8.12: faction hostility (bridge-watch vs unaligned) is data only because AI targets only the player. Add creature targets to perception/awareness so guards engage hostile-faction creatures, give guards a melee attack, and retaliation when attacked.
area:contentepic:e42milestone:m3
Other work81/171 done · 47%
M1 Grey-box slice 2
mw-7kfProps that fall below the scene fall for ever; burning ones trail heat chunks through the void
Description
Found by mw-e03.52. A physics prop that leaves the room (a crate past the floor edge) falls without limit: only quest-critical objects with a recovery anchor (mw-e17.14) get a kill plane. A burning one holds flame cells all the way down and each leaves about 80 awake element-field chunks, so 24 such crates cost 2,000 chunks and ~25 ms per tick (before mw-e03.52's kernel speed-up, 40-60 ms). Decide a kill plane for ordinary props (despawn or sleep them; a burning one should go out or be destroyed) and add it. This changes sim results, so it needs its own bead and replay re-blessing. Discovered from mw-e03.52.
area:simepic:e03milestone:m1mw-548Verify Rapier deterministic state hashes match on x86 in CI
Description
Why
ADR-0001 checks Rapier determinism only across browsers on one Apple Silicon machine. Cross-architecture equality (x86 vs arm64) is untested, and saves and replays depend on it.
What
Add a CI job that runs spikes/determinism (node-run) on ubuntu x86_64 and compares the final hash with the committed arm64 hash.
Scope
- In: CI job, expected-hash file
- Out: production physics wiring
Acceptance criteria
- AC-1 integration Given the committed 600-step script, when run on an x86_64 CI runner, then the final hash equals the arm64 hash recorded in ADR-0001, or the job fails with both hashes printed.
area:simepic:e00milestone:m1
M2 Class fantasies 5
mw-1jqClimbing is quiet and silhouetted: -10 dB vs walk on walls, contrast rule against lit backdrops
Description
Left out of mw-e10.9 (stamina, difficulty, ropes and falls shipped there). The e10.9 text says climbing is quiet (-10 dB vs walk) but you are silhouetted on walls (contrast rule). Today a climber emits no footstep noise at all (the controller is airborne) and the stealth visibility model has no wall-backdrop term. Add a climbing movement noise (walk level minus 10 dB per metre climbed) and a visibility contrast term for a climber against a lit wall. AC: unit tests pinning the dB and the contrast term.
area:simepic:e10milestone:m2mw-6z6Apply shock damage and stagger to actors from the shocked event
Description
mw-e03.8 emits a shocked event (src/sim/electricity/rules.ts: entity, intensity = held charge, hops, origin) every tick an entity holds shockCharge or more, liquid layers excepted, and names no actor kind. The damage model should consume it for creatures (damage scaled by intensity and tick length, stagger), and ignore inanimate entities. Damage numbers were out of scope for mw-e03.8.
area:simepic:e04milestone:m2size:smw-dzcSpawn charge receivers from scene data (chargeReceiver on a scene spawn)
Description
mw-e03.8 added makeChargeReceiver and ChargeReceiverComponent (src/sim/mechanisms/charge-receivers.ts) but scenes can only spawn light receivers (scene schema lightReceiver, src/sim/mechanisms/scene.ts). Add a chargeReceiver spawn field (threshold, mode) mirroring lightReceiver, call syncChargeReceiverNodes at scene start, regenerate schemas/docs. AC: a scene spawn with chargeReceiver and a conductive material becomes a receiver that drives its bound lever node when charged.
area:simepic:e03milestone:m2size:smw-w4pTeach damaged spells that misfire from damaged books
Description
Why
docs/design/books.md and the story bible (3.3) say a damaged book (missing pages) teaches a damaged spell that misfires. mw-e07.2's study runtime ignores book.condition and teaches the normal spell.
What
Carry a damaged flag from a damaged book into the learned spell (per-spell known state), have the miscast system (mw-e06.13) misfire it, and let restoring a book or finding the missing pages repair it. Hook: StudyRules completion (src/sim/magic/study.ts #complete) and BookStudyDef.
mw-ikhGive the testbed room lever a live switch state and convert testbed-room-lever to a live goal
Description
From mw-e15.2/e15.3. The example puzzle testbed-room-lever (src/content/data/puzzle/testbed-room-lever.json) still reads the stand-in fact testbed.room-lever.thrown because the testbed's room-lever prop has no switch or signal state a goal can read. Giving it one (a switch spawn field and a signal graph, or a lever property) makes the testbed a mechanisms scene, which moves the testbed replay hashes and the testbed e2e, and the rule is no new props in the shared testbed, so it needs its own decision: either give the lever a switch and rebless, or move the example puzzle to the mechanism-room scene (which already has north-lever with a switch state) and delete the stand-in fact.
Acceptance criteria
AC-1 unit The example puzzle's goal reads the lever's live state (signal or property) instead of the stand-in fact, and pulling, Mage Hand and a crate impact each solve it. AC-2 integration The testbed replay hashes are re-recorded once, with the reason in the PR, or the puzzle lives on a scene that needs no re-record.
area:simepic:e15milestone:m2
M3 MVP content 12
mw-sbaWire the Act I main-quest triggers: horned shape on night one, follow Tansy to the mine gate, friend-note found
Description
Split from mw-e39.38. Quest objectives with no built source yet: event horned-shape-seen (night-one Tansy/horned shape beat in Briar Glen: needs a night-time scripted encounter emitting it), enterArea mine-gate (follow-tansy; the mine road and gate exterior are mw-e25.23/mw-e25.10, add a player-filtered volume node mine-gate to the mine gate scene), event friend-note-found (the friend-below notes are mw-e39.42: each should emit friend-note-found on first read), enterArea briar-glen for return-to-briar-glen (needs a mine-road/Market Green return volume that is not visited before the morning-after stage, since early visits are remembered). Add the volumes as signal graphs like src/content/data/signal-graph/briar-glen-arrival.json.
area:narrativeepic:e39milestone:m3mw-0ml8Replace the riverbank-wharf-link stopgap with the real towpath once Kestrel Lock exists
Description
mw-e40.26 linked under-the-bridge straight to the Wendmouth Stone Wharf (edge riverbank-wharf-link, transitions under-the-bridge/towpath-down and wendmouth-harbour/towpath-gate) because Briar Glen Quay, the Millweir and Kestrel Lock have no scenes. When mw-e25.24 and mw-e25.14 build them, move the wharf transition to Kestrel Lock (edge towpath-lock-wharf, bead mw-e40.26 planned edge), remove the stopgap edge and the east towpath strip in under-the-bridge, and move Old Reed to the lock if the owner agrees. AC-1 integration the towpath from Kestrel Lock loads the Stone Wharf; AC-2 content riverbank-wharf-link is gone and tests updated.
area:contentepic:e40milestone:m3mw-4l3zBarge travel from Kestrel Lock to the Stone Wharf (barge-lock-wharf)
Description
The barge-lock-wharf edge (mornings only, travel: barge) stays planned: it needs mw-e25.15's travel service and the Kestrel Lock scene. The harbour's barge-berth arrival is ready; add the arrive-from-<scene> spawn and transition when the service exists.
area:contentepic:e40milestone:m3mw-5wgEmit main-quest Knot and Cradle events and facts: knot-outer-ring, ossuary-galleries, horns-hall, the-fold, the-cradle, bell-tongue-destroyed, recast-tongue-delivered, bell.rung
Description
Split from mw-e39.38. Area volumes (enterArea knot-outer-ring, ossuary-galleries, horns-hall, the-fold, the-cradle) belong to the E26 Knot scenes, which are not built: add a player-filtered volume node with the area id as its node id in each scene. The belfry area volume is done (signal-graph belfry-deck). bell-tongue-destroyed (destroying the tongue, mw-e39.43 / Cradle), recast-tongue-delivered (collecting the recast from Oswin Brand: a deliverItem or dialogue emit), and the fact bell.rung (belfry bell scene, mw-e26.8) have no source yet. Wendmouth: crate.intercepted is the crate heist (mw-e39.21), tern.sailed and epilogue.finished are mw-e39.39 / mw-e39.40; bounty.outcome and horn.spoken-for need Vane/Hollis/Hale and town testimony dialogue effects (mw-e39.41 for Horn).
area:narrativeepic:e39milestone:m3mw-6jvConsume the hub dialogue hooks: rumour trades, fence, jail sentence, well fall
Description
mw-e39.5 (dot-sleeping-ox, mirela-stacks, hale-watch) emit hook events for systems that do not exist yet. Needed: rumor.told {rumor: N, source} (rumours 6, 8, 19 from Dot; 20 from Mirela) should become revealRumor effects once the rumour type and ids exist (mw-e22.11, e39-rumor-set), with Dot's drink/coin/swap trading and her rumour list; dot.fence-opened / dot.fence-trade (fence prices, shop rules); mirela.maps-offered, mirela.hours-offered, mirela.shelf-opened, mirela.thief-job-offered (bookshop stock, lending, the retrieval quest); hale.list-shared / hale.map-shared (a list item and map reveal); hale.scout-asked, hale.knight-asked, hale.boots-asked (side quest starts, with The Captain's Boots mw-e39.10 which also writes hale.sleepwalking.revealed, hale.secret.kept and hale.secret.exposed); jail.sentence {days} and hale.jailed written by the theft system (mw-e22.1, mw-e24.8); well.player-fell written when the player falls down the Glen Well; hale.testified for Act III. Facts are in src/content/data/fact/hub-dialogue.json.
area:narrativeepic:e39milestone:m3mw-164Deepworks blockout polish: ceilings, mine lamps, cage car visual and cage travel
Description
Follow-up to mw-e26.2. The Deepworks greybox has no ceilings or lamps, and the cage lift is a stair run behind cage-gate (the door opens when the winch lever or shot pulley calls the cage down). Add: roofing/ceilings for the galleries, mine lamps and a dark-mine lighting pass, a visible cage car that travels top to bottom with the door, and a moving-platform ride in place of the stair run if the platform kit gains one. Acceptance: AC-1 content every gallery has a ceiling and light sources; AC-2 integration the cage car moves between cage-car-top and cage-car-bottom when called.
area:contentepic:e26milestone:m3mw-cw6Flaky CI: coverage-gate tests time out on slow runners (replay-content-hash at 5 s, scaffold AC-4 earlier)
Description
coverage-gate runs the full suite under v8 coverage on a shared runner. tests/integration/replay-content-hash.test.ts AC-1 timed out at the default 5000 ms on PR #231 (a gear-catalogue PR that does not touch replay); scaffold AC-4 hit its 60 s limit on #224 and #226 (fixed in #227 by raising to 180 s). Fix: set a larger testTimeout when running under coverage (vitest config, e.g. 30 s for the coverage run), or give slow integration tests explicit timeouts; keep local runs strict. AC-1 integration Given a loaded runner, when coverage-gate runs, then no test fails on timeout alone.
area:toolsmilestone:m3mw-dm3Wire Mortimer's day-long ban and the ginger-biscuits item to Mirela's dialogue
Description
mw-e39.5 (mirela-stacks) models Mortimer's ban as the fact mirela.banned and reads mirela.banned-days and mirela.biscuits-brought. Nothing writes them yet. Needed: (1) when Mortimer catches a shoplifter in the Lamplit Stacks, set mirela.banned true; (2) the game clock increments mirela.banned-days at each midnight while banned (the dialogue lifts the ban at >= 1 and resets it); (3) add the ginger-biscuits item (bible 5.1, 9) whose pickup/hold sets mirela.biscuits-brought (the dialogue clears it when given), or switch the dialogue option to hasItem; (4) restricted shelf stock reads mirela.restricted-access. Nine Sleeps must stay unsellable. Facts live in src/content/data/fact/hub-dialogue.json.
area:contentepic:e39milestone:m3mw-f25Emit the six deepworks-entered-<route> events from the mine entrances
Description
Split from mw-e39.38. The Getting in stage waits on events deepworks-entered-front-gate, -side-boarding, -winch-cage, -inner-bar, -supply-tunnel, -weeping-adit. None of the Deepworks levels exist (E26); the built entrances are only the supply-tunnel door in moorings-watch (sealed, mw-e25.9) and the Weeping Adit mouth (mw-e25.13). When each entrance is built, add a player-filtered volume past the threshold and emit the matching event (a signal-graph volume plus a receiver; or a level script emit effect), and set deepworks.route.
area:narrativeepic:e39milestone:m3mw-hl3yWire horned-shape-seen: the night-one Tansy and horned shape beat in Briar Glen
Description
see-the-horned-shape (event horned-shape-seen, stage first-night) has no source. Needs a night-time scripted encounter in Briar Glen (Tansy walks up the street humming, a horned shape leads her to the mine gate) that emits horned-shape-seen via an emit effect or dialogue. No scripted-encounter mechanism exists yet for a timed scene beat; the stage falls through on time.day>=2 meanwhile.
area:narrativeepic:e39milestone:m3mw-ows1Gate the return-to-briar-glen area so early visits are not credited
Description
return-to-briar-glen (enterArea briar-glen, stage morning-after) must complete only for a return after the Vespers, but QuestRuntime.observe remembers enterArea visits to an inactive objective, and any volume named briar-glen is entered constantly. Findings: a signal graph cannot read facts or quest stage (no fact-reading node), a scene signals entry has no condition, and a sensor binds a spawn that must exist (spawnIf would break the binding). Options: (a) add a stage/fact gate to volume nodes in src/sim/signals (new node option, sim tests); (b) make the objective a fact objective written by a fact-gated receiver; (c) have the morning-after scene script emit an event. Also decide the volume's scene (lane town-end or Market Green).
area:narrativeepic:e39milestone:m3mw-t9yFind the ~100 ms frame hitch in valley-02 on the reference machine
Description
Why
mw-ju8.2's reference runs show valley-02 with exactly one frame of about 100 ms at High (102.4, 103.6, 105.2 ms in three runs) and about 80 ms at Low (83.6, 78.5, 83.0 ms). Valley-01 and valley-03 do not have it (max 4 to 5 ms). It is under the 200 ms long-task budget and does not move p95, but it is a visible stutter, repeatable, and specific to valley-02.
What
Find what valley-02 does in that frame (first use of a material, shadow or texture upload, the river region, or a physics or navmesh warm-up) and move it to load or remove it. Start by logging the frame index of the spike (
?perfprobe samples) and a Chrome performance trace.Acceptance criteria
AC-1 perf Given valley-02 on the reference M1 Pro at High, when 1,800 frames are sampled after the warm-up, then the max frame interval is <= 33 ms (today one frame of ~100 ms).
area:renderepic:e42milestone:m3
Unscheduled 71
mw-nh24Wire arrival volumes into rumour VISIT_TRIGGERS; add Millweir and bay-nine volumes
Description
mw-heoa added observed player volumes: briarwood, standing-horn (scene briarwood), mooring-watch (moorings-watch), glen-well, lamplit-stacks (briar-glen-green), weeping-adit (deepworks), breakwater (wendmouth-harbour). Add VISIT_TRIGGERS entries (place-briarwood -> briarwood, place-standing-horn -> standing-horn, place-mooring-watch -> mooring-watch, place-glen-well -> glen-well, place-lamplit-stacks -> lamplit-stacks, place-weeping-adit -> weeping-adit, place-breakwater -> breakwater) once PR 477 merges, with a test that a heard rumour resolves. Also: place-millweir has no scene (millweir-sluice graph is unused) so needs a volume when the Millweir scene exists; place-bay-nine could get its own volume inside bonded-warehouse.
mw-77xHANDOFF: thevesperbell
Description
Lead session: dfb41a21 (main checkout) owns merging and beads export.
Merged 2026-11-13/14: #448-#476. All beads closed.
Open parents: mw-e39.38, mw-sba, mw-e40.26, mw-ily9 (needs mw-r5nw), mw-5wg, mw-e24.13 (needs mw-8stk), mw-e23.6 (AC-3 to AC-5 are mw-e23.6.1).
Merge chain (background waiters, serial):
- #477 mw-e22.11 rumour system: in the merge queue.
- #478 mw-heoa arrival volumes: after #477.
- #479 mw-1myt journal (Part of mw-e23.6): after #478.
- #339 sword grip: DO NOT merge until the owner approves.
No workers are running.
Next once #477 merges:
- mw-nh24 (wire the new volumes into VISIT_TRIGGERS)
- mw-wbr7 (wire the rumour book into the journal Rumours tab)
Both are small; one worker can do them together after #479.
CI notes:
- vitest testTimeout is 30 s on CI.
- coverage-gate needs 100% branches on changed src/game, src/ui and src/tools files.
- Debug ?scene= boots must not open blocking UI.
- A scene's first signals entry lights its braziers.
Owner action:
- Delete the stale remote branch feat/note-reader.
Waiting on owner:
- #339 look
- mw-ows1 gate
- mw-hl3y timed encounters
- Many PR-body content and tuning questions
Updated: 2026-11-14, session dfb41a21
mw-0hwBind liquid volume drains and fills to signals and levers
Description
mw-e03.6 ships setDrainOpen/setFillOpen on liquid volumes (src/sim/liquids) but no signal receiver, so a lever cannot open a basin drain yet.
Acceptance criteria
Given a drain bound to a lever signal, when the lever is pulled, then setDrainOpen runs and the level falls at the drain rate.
area:simclass:allepic:e03mw-1buFlaky bench recurred: light-field p95 over 0.5 ms on CI
Description
mw-64w was closed but the light-field AC-6 benchmark failed again on 2026-10-02 in PR #131's unit job (p95 0.517 ms vs 0.5 ms limit); it passed on rerun. Investigate whether the CI gate needs the same looser-CI/strict-local split used for line-of-sight (owner decision 2026-09-29), or whether the bench has noise that can be reduced. Related: mw-64w.
Acceptance criteria
- AC-1 perf Given 20 consecutive CI runs, when the light-field bench runs, then it never fails spuriously, while a real 2x regression still fails it.
mw-1iyMake wall-clock e2e budgets and the coverage-scaffold test robust on slow CI
Description
Recurring CI flakes: title.spec AC-1 (10 s load), slice-saves AC-3 (3 s warm reload), slice-restart AC-3 (3 s, mw-e01.16), scenes AC-1 (5 s) and tests/toolchain/scaffold.test.ts AC-4 (60 s test timeout for a child coverage run). CI renders in software GL on 2-core runners, so wall-clock budgets written for the reference hardware (contract §1) sit at or over their limit; main's own runs hide 1-2 retried tests every time. Decide a policy (e.g. measure ticks/in-page timings with a documented CI factor, keep the real budgets in the perf-budget suite on the reference profile) and apply it; do not simply widen product budgets silently.
mw-3spInstall spells and fire in the headless game world and re-record the replays
Description
createGameWorld (src/tools/replay/testbed-player-scenario.ts) takes an opt-in magic flag (mw-e06.15); src/main.ts always installs the spell pipeline, mana, the element field and fire rules. Make the headless world match by default, run settleAuthoredFires after load, and re-record the replay fixtures (tests/integration/fixtures/*.replay.json, tests/replays) with the cause explained.
area:gamemw-5ap5Hornfolk inscription fragments and the seal-dispel that open the Cradle's secret route
Description
mw-e26.10 AC-2: the secret sorcerer route (hornfolk-door, lock hornfolk-seal, edge horns-hall-cradle-secret) is greyboxed as a sealed door with the existing inscription-knot-carved-faces as the runes beside it. Missing: the Hornfolk inscription fragment items/texts (bible 4.2/7), a dedicated runes text for the door, the sorcerer's dispel (reading with fragments sets world.hornfolk-seal.dispelled and opens the door), and the capability for the edge (spell.mage-hand stands in).
area:contentepic:e26mw-5v5sPlace the Hollow Sentinels in Mooring's Watch and give the tower a bell cord shot, dispel and sneak
Description
The Watch Still Stands is dialogue-driven (sentinel-watch, npc-hollow-sentinel, debug conversation room only). Needs: Sentinel creatures placed on the four armour stands (mw-e25.7), an interactable bell cord that an arrow can cut, spell.dispel (mw-e08), stealth detection for the thief's unseen walk, the old-words technique (mw-e04) behind the knight's relief, and a real fight for the smash route. Then bind the quest to those instead of the dialogue options.
mw-5v6Wall recoil presentation: stronger sparks and cut the swing animation short
Description
mw-e04.15 emits WallRecoil and PR #362 binds a placeholder clang and the deflect sparks. In ?scene=recoil-corridor the sparks read small and faint at the player camera's distance, and the knight's swing animation plays through to its end although the sim has ended the move and locked him for 30 ticks. Make the recoil read: bigger or brighter sparks, a short recoil animation or a stagger pose, maybe a camera nudge.
mw-7nsFrozen-solid actors become temporary physics objects (push off a ledge)
Description
From mw-e03.24 Notes: frozen actors are immobile and brittle (shatter on a hard hit, Died/loot intact) but are not yet physics bodies, so they cannot be pushed or fall. Needs a feet-vs-centre placement conversion for creatures while a body exists and removal on thaw. Also: player frozen can still attack.
mw-8stkMeasure Market Green midday frame time with all scheduled townsfolk (mw-e24.13 AC-4)
Description
Ten townsfolk plus Vane, Mirela and Hale are at Market Green at 11:00-15:00. Add a perf scenario at 12:00 and record p95 <= 16.7 ms on the reference machine.
mw-47dpKnot outer greybox: lamps, ceilings and the Chapel acoustic volume
Description
The knot-outer blockout (mw-e26.9) has no ceilings or lamps (open dark fog above 4.5 m walls, 9 m in the Chapel) and no acoustic/reverb volume for the Chapel of Echoes. Add light-source spawns, ceilings where the camera allows and a sound-echo region for the nave. Beads mw-e26.14 (Chimes) and mw-e38.140 (ambience) build on it.
mw-d4nOwner decision: should the sword clang off corridor walls?
Description
mw-e04.15 (PR #362) ships wall recoil for the long reach class only. The bead text says medium and long, but with medium on, the knight's opening attack sword-light-1 (medium, horizontal) recoils in any corridor under 2.7 m wide (clear width, knight centred, facing along it), always on its first active tick, so a creature straight ahead is never hit (its arc starts about 55 degrees off to the right, so the blade is in the wall on tick 1 and the creature is reached on tick 2). That covers the slice's 1.8 m corridors, the testbed corridor and Briar Glen lane.
Measured (real moves, Rapier colliders, headless):
- sword-light-1: recoils at every width 1.0 to 2.7 m, never at 2.8 m and wider; with a creature 1.5 m ahead it recoils anyway and the creature is not hit at 1.0 to 2.7 m, and is hit on active tick 1 at 2.8 m and wider.
- sword-light-2: recoils alone at 1.0 to 1.9 m, not at 2.0 m and wider; with a creature 1.5 m ahead the creature is hit first and nothing recoils.
- sword-light-3, sword-heavy, charged heavy: vertical, never recoil. Riposte, roll attack, bash, kick: thrusts, never recoil.
- halberd-sweep (long): recoils at every width up to 3.0 m (the widest tested).
- Real scenes, mid-corridor facing along it: testbed corridor 1.8 m, slice 1.8 m, Briar Glen lane 1.8 m all recoil; 2.55 m and wider spots do not; doorways of 1.2 to 1.4 m do not, because the wall ends within 0.5 m of the blade.
Options:
- Keep medium off (current data): the sword never clangs, only long weapons do.
- Turn medium on in src/content/data/reach-class/reach-classes.json (one word in wallRecoil.reaches), possibly also excluding the arc's start from the probe (for example only probing the blade once it is inside the forward cone, or skipping tick 1) so a swing at a creature straight ahead still lands in a corridor.
Decide, then close this.
mw-d9nRain fills and puddles open ground; puddles evaporate; partial wetting in shallow liquid
Description
mw-e03.6 puddles only wet fully submerged entities and never shrink; rain adds no liquid; entities standing in shallow liquid are not wetted.
Acceptance criteria
Puddles shrink at a documented rate and wet entities standing in them to the depth submerged; rain raises puddle depth.
area:simclass:allepic:e03mw-h63Break surface ice under heavy impacts and drop what stands on it
Description
Follow-up of mw-e03.7. Surface ice colliders (src/sim/liquids/ice.ts) only melt. The bead text says ice can be broken: a heavy impact or force stimulus on an ice cell should crack it (thickness vs. hit energy), remove the collider and wake what rests on it. Needs per-cell hit routing from physicsImpact / blunt and force stimuli.
area:simepic:e03mw-h97Per-load roll animation timing and armour absorption and poise tuning
Description
From PR #356 (mw-e04.16): medium (40 ticks) and heavy (46 ticks) rolls reuse anim-knight-dodge-roll, tuned for 36 ticks, so they may look off. No shipped armour sets flat absorption or poise yet, so those AC-4 rules are unused in play; setting them on the mail hauberk changes the knight's numbers, so tune deliberately.
mw-hopjContest play input: gamepad and rebindable keys, plus grip/dice juice
Description
mw-opf6 ships keyboard-only play keys (F push or roll, H loaded die, N walk away) read directly from the window, not through the bindings system. Add bindable actions and gamepad buttons, glyph hints, and replay-log capture; sound and animation for the push and the dice.
area:gameepic:e40mw-ily9Place the Contraband Pages levels and events in the customs house
Description
mw-e39.22 binds objectives to events the level must emit: books-lifted (the player takes the books from the strongroom via the roof hatch route; scene refs customs-roof-hatch, strongroom, confiscated-books), inspector-guise-passed and inspector-guise-blown (a borrowed inspector's coat or Mirror Self at the customs door; Cray knows every inspector). Also place note-impound-ledger on Cray's desk, note-tidefair-board on the Stone Wharf, and the area volumes customs-house and customs-strongroom may need tuning (wendmouth-harbour-areas graph). Relates to mw-e40.3.
Waits on
- mw-r5nw Emitters for the Contraband Pages customs events (books-lifted, inspector-guise-passed, inspector-guise-blown) open
mw-jrz3Place Duggan Marl, the Rootcellar camp and the crate so Framed in Brass is playable in the world
Description
Framed in Brass runs through kettleback-kitchen, duggan-testimony and vane-bounty, which are only reachable in the debug conversation room. Needs the camp, the crate, the goblin fight for the by-force route (goblins turn hostile), and Vane's house scene. The ledger readable in the sealed gallery and Quell's invoice are already placed or fact-based.
mw-ma8oTidefair rewards: Tereza's discount and under-counter stock, and a foreign spellbook
Description
mw-e39.22 sets tereza.tidefair-discount and tereza.under-counter (tidefair.regarded) and tereza.tidefair-rewarded after an honest Tidefair win. The shop stock (E20/E21) must read tereza.tidefair-discount for prices and the under-counter list; the sorcerer's foreign spellbook trade is the existing story trade (tereza.spellbook-traded).
mw-mlvRead detectionSpeed and searchDuration difficulty multipliers in stealth/AI
Description
mw-e31.10 added DifficultyConfig.searchDuration and Story/Veteran preset values, but no system reads detectionSpeed or searchDuration yet (damageTaken, damageDealt, parryWindow, dodgeWindow, fallDamage are wired). Scale awareness accumulation by detectionSpeed (e11 awareness) and the last-known-position Searching duration by searchDuration via scaleDifficulty (e09/e11). Add ACs under mw-e09.17. puzzleHints is read by mw-e15.11.
mw-n40fBuild the Market Green to priory road way (green-priory-road)
Description
The east-wall exit of briar-glen-green (exit-priory-road, lock road-not-open) is still a planned stub; the way into the Briarwood is the lane's woodside path. Decide where the green's priory road enters the Briarwood (the priory-road spot at the north end, or the town trail) and build the transitions both ways, then flip the green-priory-road edge to built.
mw-nm7Hiding spot follow-ups: solid spots, shared wardrobe and loot key, take bodies back out
Description
From PR #359 (mw-e03.23): testbed hiding spots have no collider so you can walk through them; a spot and a lootable container cannot be one entity since Interact only fires the primary affordance, so a wardrobe with loot needs a secondary action; a stashed body cannot be taken out of a chest except by breaking it, and it is a kind and weight rather than a specific actor until corpses can be carried (mw-e10.5); a plain hideable entity with no spot prompts 'Nowhere to hide'.
mw-pqcLive spawn variant swaps skip drops, slain facts, presence and other starters
Description
From PR #371 (mw-e27.8): a spawn brought in live by a fact change (SpawnVariants / watchSpawnVariants) does not get installSlainFacts, startCreatureDrops, presence re-sync, or registration with the mechanisms, carry and light starters; a signal graph bound to a gated spawn may misbehave while it is absent. The content check (checkVariants) blocks most gated features, but not all. Bring live swaps to parity with scene load, or extend the check.
area:simepic:e27mw-qbgwClass beat triggers still missing: night-one, mooring-watch, horn, climax
Description
mw-aw1y wired the class-beat narration at triggers that exist: arrival and first-journal (class pick), getting-in (enter-by-supply-tunnel / winch-cage / inner-bar objective complete, thief world.vane-house.roof-entered) and wendmouth (wendmouth.visited). Not wired for lack of a trigger (add one entry to BEAT_TRIGGERS in src/game/class-beats/class-beats.ts plus a class-beat.<beat>.shown fact in src/content/data/fact/class-beats.json each): night-one (mw-e25.2, player first spots Tansy), mooring-watch (mw-e25.7, mw-e25.8, no arrival volume/fact for the tower), horn (mw-e13.14, mw-e26.14, Warden trial), climax (mw-e26.19). Also the knight's getting-in text fires on the supply-tunnel objective (mw-e25.9) and the thief's on the roof fact only; mw-e24.12 rooftops share it. Discovered from mw-aw1y.
area:narrativeepic:e39mw-qfxValley-02 skeletons fight and kill each other
Description
Found while checking PR #351: running valley-02 for 3 simulated minutes with an idle player, the player dies at tick 727, then skeletons fight each other: skel-valley-02-trail-miner is killed by entity 55's melee and entity 54's arrows (tick 3758), and skel-valley-02-ford-bearer is killed by 54's arrows (tick 6761). Not from environment damage. Not yet checked whether it starts with stray-arrow retaliation, whether it happens while the player is alive, or whether it predates #351 (the damage comes from attacks that #351 does not touch).
mw-r5nwEmitters for the Contraband Pages customs events (books-lifted, inspector-guise-passed, inspector-guise-blown)
Description
mw-ily9 found no source for the three events quest-contraband-pages binds. books-lifted: the confiscated books are stand-in facts, with no book items or strongroom container to take them from; needs impounded book items in a container at confiscated-books (strongroom) and a pickup-to-emit hook (item.pickedUp exists in sim). inspector-guise-passed/blown: no disguise system or Mirror Self spell exists; blocked by mw-e10.10 (disguises) and mw-e40.17; then Cray dialogue (or a customs-door volume) emits by whether the disguise holds. Scene refs already present: customs-roof-hatch, strongroom, confiscated-books, customs-strongroom volume.
mw-s14Night should affect stealth: the sim light field ignores the game clock
Description
From mw-ju8.43 (PR #336): the lane's day and night look is render-only. The sim light field keeps the scene's static ambient and moon level, so guards see the player the same at noon and at night even though the screen is much darker. Decide whether time of day should feed the sim light field. If it does, stealth becomes clock-dependent and golden recordings need re-recording, or the recordings must pin the clock. AC-1 unit Given a scene with a day and night look, when the clock moves from 12:00 to 22:00, then the light level the stealth system reads at an unlit point falls by the look's ratio. AC-2 integration Same seed and clock give the same result, so replays stay deterministic. AC-3 manual Owner confirms sneaking at night feels different.
area:simepic:e42mw-t4sKnight has no distinct block pose: the shield does not raise on block
Description
Found while checking mw-e8n (PR #216): the block animation shows no guard pose, the shield stays at the side. Animation clip data (not skin weights). Check the block clip / anim graph for the knight and give it a raised-shield pose; verify with in-game screenshots.
area:artarea:rendermw-tqwShelter and occlusion for rain zones
Description
mw-e03.6 rain zones wet everything inside their box; roofs, awnings and overhangs should shield what is under them.
Acceptance criteria
Given an entity under a roof inside a rain zone, when it rains, then its wetness does not rise.
area:simclass:allepic:e03mw-tscfPlace the sleepwalking Hale: night route, Bell Line stones, mine-gate walk-in, bedpost and door bell (The Captain's Boots)
Description
The Captain's Boots narrates the night walk, the fetch from the mine gate, the tied bed and the door bell in hale-watch. Needs Hale moving at night (the Watch House door to the Bell Line stones along the fern path), a placed Hale at the barred mine gate while hale.walked-in holds (and absent from the Watch House), the Briarwood fern path as a real route (route.briarwood.revealed unlocking the priory shortcut), and the hand-bell and rope as placed things. Also an optional readable Vane-gains-authority effect when hale.secret.exposed.
mw-u2kWet or splashed liquid layers: water on oil layers and liquid layer fuel scaling
Description
mw-e03.6 layer entities are ordinary objects: a water stimulus raising wetness on an oil layer puts it out, and an oil layer burns for its preset fuel regardless of depth.
Acceptance criteria
Oil fuel scales with layer depth; rain on an oil slick does not soak the layer entity.
area:simclass:allepic:e03mw-v645Place Snig, the Glen Well tunnel route and Mortimer so The Bookshop Burglar's trail is playable
Description
The trail ends at the Glen Well (readable note) and Snig is met in snig-tunnels (caught-1), but his tunnels, the Rootcellar camp and the Stacks interior (Mortimer, the transom, the ink pot) are not placed, so the burglar quest binds to dialogue nodes and facts. Place Snig and Mortimer, build the well descent, and replace the stakeout dialogue stand-in with the real night encounter (a sorcerer's light as a real spell cast at the right moment).
mw-wmdvWire the Gull Shoot, Regatta and Pocket Race contests to the tidefair facts
Description
mw-e39.22 wrote quest-tidefair against facts the unbuilt contests must write. Each contest must set tidefair.results.<event> (won, lost, thrown, cheated) in its win/lose/disqualified/throwMatch effects, as drowned-gull-arm-wrestling now does, plus tidefair.cheated on a caught cheat: Gull Shoot and Pocket Race (mw-e40.8), Regatta (mw-e40.9). The regatta writes tidefair.regatta.magic-used (a spell cast during the race) and the scene writes tidefair.regatta.cray-present while Cray walks the quay (start, turn, finish): a won, magic-used, cray-present regatta is voided by the quest (fine branch in cray-customs). The pocket race writes tidefair.pocket.cray-lifted or cray-caught for Cray's ribbon. Done when each contest has data, the facts are written, and tests/integration/harbour-side-quests.test.ts has a contest-driven case per event.
mw-wmuElement look pass: wet reads only as darker, steam puff not seen on screen, drips show on frozen things
Description
From PR #364 (mw-e29.8) screenshots: a wet crate reads as darker with faint drips, not clearly wet; the burning to wet steam puff was not visible in a screenshot (unit tests prove it is requested; the e2e check only asserts the VFX count did not drop); wet drips still spawn on a crate that is also frozen. Also a doused thing may show both vfx-extinguish-puff and the new steam puff. Tune or suppress lower-precedence effects, and capture the puff on screen. Feeds the owner's AC-5 playtest.
area:renderepic:e29mw-0ftCI: a cancelled duplicate changelog/pr-lint run blocks merging until rerun
Description
PRs #368 and #376: the pull_request 'labeled' event (no-changelog added at or after creation) starts a second changelog and pr-lint run; concurrency cancels one, and the cancelled check run keeps mergeStateStatus BLOCKED although the newest run passed. Rerunning the cancelled run clears it. Fix the workflow concurrency or cancel-in-progress settings so a superseded run does not leave a cancelled required check.
area:toolsmw-3j7Arrow VFX look pass: fire flames read as flat triangles, trails grey, sparks and noise ring faint
Description
From PR #348 (mw-e29.11) screenshots: fire-arrow flames appear as hard flat triangles and come from the tip rather than along the shaft; ribbon trails look grey at 0.3 alpha instead of the arrow's tint; water burst droplets small at camera distance; deflect sparks hard to tell from the dust puff; noise ring is a faint flat disc edge-on from low cameras. Demo: ?scene=testbed&arrows=demo. Trick arrows are not in play yet (quiver has standard, broadhead, blunt).
mw-4vrNoise from rope snaps, ice cracking, electrical and fire crackle
Description
From PR #352 (mw-e03.25): docs/design/noise-events.md lists these as not emitted. A snapping rope and a shattering ice sheet would be useful stealth signals.
mw-4whRegenerate e2e shard weights for new specs (woodside-path, vertical-hall, npc-conversation, briar-glen-town)
Description
Several long e2e specs were added in Nov 2026 and get the median weight in e2e/shards.json. Regenerate with pnpm e2e:shards:regenerate from recent main e2e-report artifacts so shards stay balanced.
mw-5qaLabel discriminated-union variants in generated content docs
Description
pnpm content:docs flattens discriminated unions (spell delivery, spell effects[], fact) into one field list: rows from different variants share a path (e.g. effects[].impulse is an object for damage and a number for force), and the variant descriptions (.describe on each op/delivery) are dropped. Group rows per variant with its discriminator value and description. Found while shipping mw-e06.1 (PR #88).
area:contentsize:smw-5ydRoute any future motion blur, head bob or chromatic aberration through the motion comfort settings
Description
mw-e31.7 added reduce-flashing and reduce-screen-effects but the game has no motion blur, head bob or chromatic aberration yet, so no setting exists for them. When one is added, add its off switch (and make reduce-screen-effects cover it) and send any new full-screen flash through FlashLimiter.admit (src/game/vfx/screen-feedback.ts).
mw-9jiCamera auto-centre keeps its pacing counter in a closure, not world state; non-default look settings are not recorded
Description
From PR #379 (mw-e31.6): the look system's auto-centre idle counter lives in a closure inside playerLookSystem, so snapshots, hashes and restores do not include it. Look sensitivity, invert and auto-centre are read live from settings inside the sim and are not part of the recorded input, so a run recorded with non-default settings does not replay identically (replays use content defaults; same as hold/toggle in #377). Move the counter into a component and consider recording the active look settings with a replay.
area:simepic:e31mw-15u7Decide the Knot-side crypt edge: ossuary-crypt starts in the Ossuary but the passage stub stands off Horn's Hall
Description
mw-e26.10 built a sealed crypt passage stub in knot-inner (crypt-door, lock crypt-sealed). Edge ossuary-crypt (mw-e26.20) is from ossuary-galleries; no transition links knot-inner to bellwater-priory yet.
area:narrativeepic:e26mw-23rKnight tabard hem flaps beside the thigh at full stride (run/sprint)
Description
Left over after mw-e8n (PR #216): at full stride in run, sprint and walk poses a small ragged tabard hem flap lifts beside the swinging thigh. It reads as cloth, not a stretched ribbon. A cloth-specific weighting rule (or hem vertices pinned to the hip bone) would remove it. Low priority polish until real rigged character models land (mw-e37.21).
area:artarea:rendermw-26tShow conduction on the debug overlay: graph nodes, edges, charge
Description
ConductionGraphCache (src/sim/electricity/network.ts) holds the cached graph and a builds counter; expose the nodes, edges and per-node charge in the debug overlay so storms and puddles can be tuned. Not needed for mw-e03.8 ACs.
area:simepic:e03size:smw-36lSwap the vertical-hall greybox for the stealth course's vertical hall; add a thief rough-face and rope-tie e2e
Description
mw-e10.9 AC-5 found no stealth course vertical hall, so it added ?scene=vertical-hall (ladder, rough face, rope hook, 5 m rafter platform). When mw-e10.17 builds the real course, point e2e/vertical-hall.spec.ts at it (or keep the hall as a climbing test room). Also cover in e2e: the thief climbs the rough face with a jump, and tying the rope at rafter-hook gives a rope everyone can climb.
area:gameclass:thiefepic:e10mw-44nStock or loot the climbing rope and give it its own icon
Description
mw-e10.9 added the climbing-rope tool (placeholder icon shared with the rope arrow, not stocked: tests/integration/shop-row-content.test.ts lists it NOT_FOR_SALE). Decide where players get it (a shop line, loot tables, the thief kit), re-base its value in the economy pass, make icon-item-climbing-rope-01, and remove it from NOT_FOR_SALE.
area:contentepic:e10mw-99uvHollis's Bible as a placed pickup and the thief's stair in the bakery upstairs
Description
mw-3n0f made the letter a real item (letter-anselm-hobb) given by Hollis's dialogue. Still to build: the Bible as a placed container upstairs in the Pell bakery, with a steal path that does not go through the dialogue option.
mw-at3Pick a gamepad button for the kick (mw-e04.18 left it unbound)
Description
The kick has a keyboard binding (K) but no pad button: every face, shoulder, stick and d-pad button is taken. Block + attack without a shield still kicks on pad. Decide with the owner (a chord such as LB+B, or a spare button) and bind it in DEFAULT_PAD_BINDINGS and docs/design/controls.md.
area:gameepic:e04mw-bnzOwnership: player's own items put in an owned chest read as stolen when taken back; refused return add not rolled back
Description
From PR #374 (mw-e27.9): an owned container treats everything in it as the owner's, so a player who puts their own item into Ottilie's back-box and takes it out records a theft. Track per-stack ownership instead. Also: a return clears the stolen flag and resolves the theft just before the container add; if the add is refused (9999 unit guard) it is not rolled back.
area:simepic:e27mw-bxxAt phone width the page title sits behind the vitals bars
Description
Seen in screenshots for PRs #378, #384 and #385 at 390 px: the 'The Vesper Bell' heading at top-left overlaps the player health, stamina and mana bars. Present before those changes. Hide or shrink the title during play on narrow screens, or move the bars.
area:uiepic:e31mw-bzsVoice volume slider does nothing: no voice bus in the audio engine
Description
Found during mw-e31.9 (PR #382): audio.voice exists in the settings schema and the options menu, but the engine has no voice bus, so the slider has no effect. Add a voice bus (for barks and dialogue lines) and route voice cues to it, or hide the slider until voice exists.
area:audioepic:e31mw-f90nMake The Glasswright's Primer a real book item with spell text and a Lamplit Stacks stock row
Description
From PR #463: the Primer is bundled inside crate-impounded-books; mirela.illusion-stocked is still a fact. Add a book item with its Illusion spell text (see mw-e39.45 for tome textKeys) and Mirela's stock row once the crate is delivered.
mw-fmr7Decide whether the Knot outer half splits into streamed scenes (Outer Ring / Ossuary / Chapel)
Description
mw-e26.9 built the three areas as ONE scene (knot-outer, ~125 x 260 m, ~210 placements) with named in-scene edges; the bead note suggests separate scenes or streamed chunks for memory. If the CI perf job (knot-outer-frame-p95-high) or load budgets say so, split at the Scales gate and the chapel door (each has two ways, so a split needs a transition per way).
mw-gf2hSorcerer memory-read on Clatter and the Tansy/Clatter family scene as real actions
Description
mw-3n0f follow-up: the sorcerer reading is still a dialogue option with a check; tansy.kin.known is set but no scene reads it yet (E29 / epilogue).
mw-jgpcSounding Shaft foot: climb, sounding-shaft-top volume and shaft-door content
Description
knot-inner has the shaft foot room (shaft-mouth marker) only. mw-e26.18 builds the climb; add the player volume sounding-shaft-top (mw-5wg) there.
area:contentepic:e26mw-k8pAdd Tansy's library card and the Pell bakery spare key as items
Description
mw-e39.6 stand-ins: hollis-pell sets facts hollis.library-card-given (sorcerer) and hollis.spare-key-given (thief) and emits hollis.gave-library-card / hollis.gave-spare-key because no item exists for either. Add the two items (and a use for them: Mirela's library fine, the bakery back door) and swap the setFact effects for giveItem.
mw-kanInventory selection looks like focus; shop hint line cut off at 720 px
Description
From PR #386 (mw-e31.11) screenshots: the inventory's orange selected item looks much like a focused item, so selection and focus are ambiguous when focus is on the tab strip; give selection its own non-colour mark. The shop's bottom hint line is cut off at 1280x720 (the panel scrolls).
area:uiepic:e31mw-mlfRope support load ignores what an actor wears and carries
Description
Found in mw-e03.13: a support span's load (src/sim/mechanisms/supports.ts, weighSpan) counts the weight property of what rests on it, but unlike pressure plates (weighPlate with gear and the carry ledger) not armour worn or objects carried, so a knight in plate on a rotten rope bridge weighs only his own weight property. Reuse the plates' gear and hold lookups.
Acceptance criteria
A bridge's load includes an actor's worn kilograms and the weight of what it carries, as a pressure plate's does.
area:simepic:e03mw-mmaPlaytest: blocked hits read apart from clean hits by visuals alone
Description
mw-e29.4 AC-5 (manual): in the combat sandbox three playtesters each say which hits were blocked versus clean from the visuals alone. Needs people; the effects ship in the mw-e29.4 PR.
needs-humanmw-n37rKnot inner: lamps, ceilings, Horn's Hall statues and the Cradle's carved faces as art
Description
knot-inner is a lampless, ceilingless blockout with markers for statues, moss beds, carved faces. Dress after mw-e37.258 and decide whether the inner half streams separately from knot-outer.
area:contentepic:e26mw-ol69Real items and a changing keeper's log for The Watch Still Stands rewards
Description
watch.key-held, watch.mirror-held and moorings.safe-base are fact stand-ins. Make Sergeant Mooring's watch-key and the signal mirror inventory items with icons (the mirror feeds the priory light puzzle mw-e25.8), make the tower a rest point and fast route to the mine, and add the closing line in the keeper's log (a text that reads differently once sentinels.relieved).
mw-qk2Quest authoring: locate file/line for quest validation issues
Description
Mirror dialogue-source.ts/json-locate.ts (PR #407) for quests: validateQuestText(file,text) and describeQuestIssue reporting file, objective/resolution/failure id and line for schema and validateQuestLogic issues. Out of scope of mw-e23.1; needs #407 merged.
Waits on
- mw-e23.1 Define quest authoring format with objectives and resolutions done
mw-qt6Decide what happens to arrows stuck in the player
Description
From PR #360 (mw-e05.7): arrows stuck in the player, from the player's own bow or a trap with no shooter, cannot be taken while alive. On death they do not drop or save; by code reading they follow the player as extra shafts until the 64 arrow cap removes the oldest or the page reloads (not checked visually). Enemy archers fire projectile attacks, which leave no arrow at rest. Decide whether they break, fall off or return to the quiver on death or after some time. Also: arrows in training dummies count as in a living body, so they cannot be taken back until it dies; decide if target practice should allow that.
area:simepic:e05mw-tmlvJail system: Snig in Hale's cell and the thief's jailbreak after The Bookshop Burglar turn-in
Description
burglar.outcome turned-in sets burglar.snig-jailed and the cutting turns hostile. Bible 6.1: a thief can bust Snig out later. Needs the jail/cell system and the loose-stone hint in hale-watch; the jailbreak should reverse goblins.standing and open a Snig reunion.
mw-vauDecide where Old Reed stands: Kestrel Lock scene or Wendmouth towpath post
Description
mw-e40.1 placed npc-reed at a toll board by the towpath arrival in wendmouth-harbour (spawn npc-reed, toll-board) because Kestrel Lock has no scene. Bible 5.14 puts him at Kestrel Lock. When the Kestrel Lock scene is built (mw-e25.14), move the spawn there and drop it from the harbour, or keep a toll post at the towpath gate if the owner prefers.
mw-vqzvPear tree graft and altar lay-down as world interactions (replace Juniper-dialogue and clatter-dialogue facts)
Description
mw-3n0f placed the pear-tree-bloom and choir-altar-psalter spawns (spawnIf bones.pear-bloomed / bones.psalter-given) but the facts are still written by Juniper's and Clatter's dialogue. Needs a graft/Nature-spell interaction on dead-pear-tree and a lay-psalter interaction on choir-altar (a src/game interact verb handler), plus the bloom VFX (E29).
mw-w4zIce slabs as floating physics objects and ice cue events (iceFormed, iceMelted)
Description
Follow-up of mw-e03.7. Out of scope there: (1) broken or melted ice shed as floating slab physics objects that float (density < water), (2) iceFormed / iceMelted sim events for audio and VFX cue sheets, (3) the renderer drawing the ice surface by cell thickness (liquid.ice component).
area:simepic:e03mw-w7rCold air freezes liquid surfaces without a stimulus (frost zones, winter levels)
Description
Follow-up of mw-e03.7. Surface ice cells (liquid.ice) only start from a heat or cold stimulus that reaches the volume. A cold air zone (element field temperature at or below the freeze point) should start ice on its own: scan awake chunks overlapping volumes. Today only the stimulus API starts ice.
area:simepic:e03mw-wifSurface art for the lane, valleys and slice: gravel/dirt/mud/leaves floors and their sound sets
Description
mw-1jm tagged only the inn, store and riverbank, where the greybox can now show a surface patch. Remaining: briar-glen-lane (cobbles, riverbank alley dirt path), valley-01/02/03 (gravel, dirt, leaves, mud by the river) and the slice need real floor art (or at least agreed patches) before they are tagged, and an encounter retune check since surface dB changes what skeletons hear. Also sound sets for carpet, wood-hollow and mud (audio falls back to stone today).
mw-ye1Noise sources missing for ripples: noise-arrow payload does not emit noiseEmitted
Description
From PR #349 (mw-e29.12): the noise ripple assist draws one ring per noiseEmitted, but noise arrows do not emit it (mw-e05 payload work), so no ripple. Player footsteps are covered by open mw-e09.5. The ripple was also hard to see in screenshots even at 0.8 opacity: tune by eye once footsteps emit.
No items match these filters. Clear the search or pick "All" to see everything.
E00Project FoundationA trustworthy engineering foundation: every change lands through a green, coverage-gated, security-scanned PR; the sim core is deterministic and replayable; content is schema-validated; a greybox testbed boots in the chosen engine; and a public backlog.html always shows what is done and what is next.37/39 done · 95%
M0 Foundation 36
mw-e00.1Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest
Description
Why
Every other bead needs a consistent, strict toolchain; rapid iteration (constitution §10) starts with a fast, boring build.
What
Create the project skeleton: pnpm workspace root, Vite app, TypeScript strict, ESLint (flat config) + Prettier, Vitest with v8 coverage, Playwright installed, and the
src/{sim,content,game,render,audio,ui,tools}layout from the backlog contract §2 with a placeholder module + test in each. Scripts:dev,build,preview,lint,format,typecheck,test,test:coverage,e2e.Scope
- In: package.json, pnpm-lock, .nvmrc/.node-version, tsconfig (strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes)
- In: ESLint + Prettier config, EditorConfig
- In: vitest.config.ts with v8 coverage (reporters: text, json-summary, lcov)
- In: playwright.config.ts with one placeholder spec
- In: README quickstart section
- Out: CI workflows (e00-ci-pipeline)
- Out: Per-layer thresholds and ratchet (e00-ci-pipeline)
- Out: Determinism lint rules (e00-determinism-lint)
- Out: Renderer dependency (e00-render-bootstrap)
Notes
- Pin Node LTS via .node-version and
packageManagerfield; use corepack. - Path aliases:
@sim/*,@content/*,@game/*… mirrored in Vite and Vitest.
Acceptance criteria
- AC-1 integration Given a fresh clone with the pinned Node, when
pnpm install && pnpm buildruns, then it exits 0 and producesdist/index.html. - AC-2 integration Given the scaffold, when
pnpm lint && pnpm typecheck && pnpm testruns, then all pass with at least one test persrc/*layer. - AC-3 unit Given a file with an implicit
anyor an unchecked index access, whenpnpm typecheckruns, then it fails. - AC-4 integration Given
pnpm test:coverage, when it completes, thencoverage/coverage-summary.jsonandcoverage/lcov.infoexist. - AC-5 e2e Given
pnpm e2e, when the placeholder Playwright spec opens the built app, then the page loads with zero console errors. - AC-6 integration Given a Node version other than the pinned one, when
pnpm installruns, then it fails with an engine-mismatch message (engine-strict).
Closed
Completed in PR #11 (2026-09-27)
area:infraepic:e00milestone:m0mw-e00.2Lint src/sim for nondeterminism and forbidden layer imports
Description
Why
Deterministic sim (constitution §10) is only real if it is mechanically enforced; replays and save stability depend on it.
What
ESLint overrides scoped to
src/sim/**banningMath.random,Date.now,new Date(),performance.now,crypto.getRandomValues,setTimeout/setInterval/requestAnimationFrame,window/document/navigatorglobals, and imports fromsrc/{game,render,audio,ui,tools}or renderer/physics-rendering packages. Import-boundary rules for all layers:rendermay not importui,simimports onlysim+contenttypes, etc.Scope
- In: eslint no-restricted-globals / no-restricted-properties / no-restricted-syntax rules for src/sim
- In: eslint-plugin-import (or boundaries) layer matrix for all src/* layers
- In: Fixture files under
test/lint-fixtures/proving each rule fires - Out: Runtime determinism checks (e00-replay-harness)
Notes
- Allowed escape:
// eslint-disable-next-linein src/sim is itself banned viaeslint-comments/no-restricted-disablefor these rules. - Iteration order: prefer arrays/Maps (insertion ordered); document that
for…inover numeric-like keys is ordered by spec but discouraged.
Acceptance criteria
- AC-1 unit Given a fixture
src/simfile callingMath.random(),Date.now()orperformance.now(), when ESLint runs, then each call is reported as an error naming the injected RNG/clock alternative. - AC-2 unit Given a
src/simfile importing fromsrc/renderor referencingdocument, when ESLint runs, then an error is reported. - AC-3 unit Given
src/gamecallingperformance.now(), when ESLint runs, then no error is reported (rule scoped to sim only). - AC-4 unit Given a
src/simfile with// eslint-disable-next-line no-restricted-propertiesaboveMath.random(), when ESLint runs, then the disable comment itself is reported as an error. - AC-5 integration Given the lint fixture suite, when
pnpm testruns, then a test programmatically lints every fixture and asserts the expected rule IDs fire.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
Closed
Completed in PR #17 (2026-09-28)
area:infraepic:e00milestone:m0mw-e00.3Add CI with per-layer coverage gates, ratchet and Playwright smoke
Description
Why
The contract goal is releases trusted without reading the code; that requires CI to enforce the §3 coverage policy mechanically, not by reviewer vigilance.
What
GitHub Actions workflow
ci.ymlon pull_request and push to main with jobs:lint(incl. determinism lint),typecheck,unit(Vitest + coverage),coverage-gate,coverage-ratchet,e2e-smoke(Playwright, Chromium),build. Coverage enforcement:- 100% layer:
vitest.config.tscoverage.thresholdsglob entries forsrc/sim/**,src/content/**,src/game/save/**(save/load), and runtime dirs registered incoverage-layers.json(economy, quest, dialogue runtimes,scripts/backlog/**) at 100 lines/branches/functions/statements withperFile: true. - Glue layer:
src/game/**,src/ui/**,src/audio/**,src/tools/**at ≥ 90 lines and branches (per directory). - Excluded layer:
src/render/**, shaders, WASM bindings, bootstrap are excluded only if listed incoverage-exclusions.md(validated by e00-coverage-exclusions). - Ratchet: on push to main CI uploads
coverage-summary.jsonas artifactcoverage-baseline; PR runs download the latest main artifact andscripts/coverage-ratchet.mjsfails if any of the four metrics decreases (tolerance 0.01 pp) globally or for any layer incoverage-layers.json. If no artifact is available it falls back to committedcoverage-baseline.jsonand emits a warning annotation.
Scope
- In: .github/workflows/ci.yml, pnpm store caching, Playwright browser caching
- In: coverage-layers.json (single source for layer → globs → thresholds) consumed by vitest.config.ts and the ratchet script
- In: scripts/coverage-ratchet.mjs with 100% unit coverage
- In: Job summary table of coverage per layer vs baseline
- Out: Secret scanning and CVE audit jobs (e00-secret-scanning, e00-dependency-audit)
- Out: beads validation job (e00-beads-validate)
- Out: Cross-browser matrix (e32-browser-matrix)
- Out: Required-check configuration (e00-branch-protection)
Notes
- Stable job names matter: branch protection references them (
lint,typecheck,unit,coverage-gate,coverage-ratchet,e2e-smoke,build). - Use
concurrencyto cancel superseded PR runs. - Adding a new 100%-layer runtime directory = one line in coverage-layers.json.
Acceptance criteria
- AC-1 integration Given a PR that adds an uncovered branch in
src/sim, when CI runs, thencoverage-gatefails naming the file and metric below 100%. - AC-2 integration Given a PR that drops
src/uiline coverage to 89%, when CI runs, thencoverage-gatefails; at 90% it passes. - AC-3 unit Given a baseline summary and a PR summary where global branches fell from 95.20% to 95.10%, when
coverage-ratchet.mjsruns, then it exits non-zero and prints the metric, layer, old and new values. - AC-4 unit Given no main-branch artifact is available, when the ratchet runs, then it compares against committed
coverage-baseline.jsonand emits a GitHub warning annotation instead of silently passing. - AC-5 unit Given a PR summary equal to baseline within 0.01 pp, when the ratchet runs, then it passes (float tolerance).
- AC-6 integration Given a PR with a failing Playwright smoke test, when CI runs, then
e2e-smokefails and uploads the trace and screenshot as artifacts. - AC-7 integration Given a clean PR, when CI runs with warm caches, then the full pipeline completes in ≤ 10 minutes.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e00.2 Lint src/sim for nondeterminism and forbidden layer imports done
Closed
Completed in PR #18 (2026-09-28)
area:infraepic:e00milestone:m0- 100% layer:
mw-e00.4Enforce coverage-exclusions.md as the only source of exclusions
Description
Why
Exclusions are where coverage policies quietly rot; each one must be visible, justified and owned.
What
Create
coverage-exclusions.mdwith a machine-readable table (path glob | reason | bead | alternative verification).scripts/check-coverage-exclusions.mjsparses it and asserts the Vitestcoverage.excludelist is exactly the set in the file (beyond default test/config ignores), every row has a non-empty reason and amw-bead id, no glob matchessrc/sim/**orsrc/content/**, and glue-layer gaps below 100% are listed with justification. Also bans/* v8 ignore *//istanbul ignorecomments outside excluded paths.Scope
- In: coverage-exclusions.md template + initial rows (src/render/**, bootstrap main.ts)
- In: check script + CI step in the coverage-gate job
- In: Docs section in CONTRIBUTING on how to request an exclusion
- Out: Threshold numbers themselves (e00-ci-pipeline)
Notes
- None.
Acceptance criteria
- AC-1 unit Given vitest excludes
src/render/**but the markdown table does not list it, when the check runs, then it fails naming the unlisted glob. - AC-2 unit Given a row with an empty reason or without a
mw-bead id, when the check runs, then it fails naming the row. - AC-3 unit Given a row excluding
src/sim/rng.ts, when the check runs, then it fails because 100% layers cannot be excluded. - AC-4 unit Given a
/* v8 ignore next */comment insrc/game/foo.ts(non-excluded path), when the check runs, then it fails with file and line. - AC-5 integration Given the committed repo state, when CI runs the check, then it passes and the script has 100% coverage.
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #19 (2026-09-28)
area:infraepic:e00milestone:m0mw-e00.5Block secrets via gitleaks in CI and an optional pre-commit hook
Description
Why
The repo is public; a leaked token is permanent. Secrets must never reach a commit.
What
Add a
secretsCI job running gitleaks over the PR commit range (and full history on push to main), a.gitleaks.tomlwith an allowlist mechanism that requires a comment, enable GitHub secret scanning + push protection in repo settings, and a lefthook/husky pre-commit hook running gitleaks on staged files.Scope
- In: .github/workflows/security.yml
secretsjob - In: .gitleaks.toml
- In: pre-commit hook (opt-in install via
pnpm prepare) - In: Enable GitHub secret scanning + push protection (documented; needs-human if API token lacks admin)
- Out: Dependency CVEs (e00-dependency-audit)
Notes
- Test the job with a fake high-entropy token in a throwaway branch; do not commit real-format tokens into fixtures — use gitleaks test fixtures generated at runtime.
Acceptance criteria
- AC-1 integration Given a PR commit containing a string matching the GitHub PAT pattern, when CI runs, then
secretsfails and reports file and line without echoing the full secret. - AC-2 integration Given a clean PR, when CI runs, then
secretspasses in ≤ 60 s. - AC-3 integration Given a staged file containing an AWS-key-shaped string, when
git commitruns with hooks installed, then the commit is rejected. - AC-4 manual Given repository settings, when inspected, then secret scanning and push protection are enabled.
- AC-5 unit Given an allowlist entry without an explanatory comment, when the config lint step runs, then it fails.
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #20 (2026-09-28)
area:infraepic:e00milestone:m0- In: .github/workflows/security.yml
mw-e00.6Fail CI on CRITICAL dependency CVEs and enable Dependabot
Description
Why
Policy: never ship libraries with known CRITICAL CVEs. A free public game still owes players a safe page.
What
Add an
auditCI job (pnpm audit --audit-level critical --prodplus dev deps reported) that fails on any CRITICAL advisory, runs on PRs, on push to main and nightly on a schedule. Add Dependabot (npm + github-actions, weekly, grouped minor/patch) and dependency-review-action on PRs to block newly-introduced critical/high-licence problems. Temporary waivers live inaudit-waivers.jsonwith advisory id, reason, bead and expiry date.Scope
- In: security.yml
auditjob + nightly schedule - In: .github/dependabot.yml
- In: dependency-review-action with licence denylist (GPL-3.0/AGPL for runtime deps, since assets/code must permit free distribution)
- In: audit-waivers.json + validator (expired waiver fails CI)
- Out: Secret scanning (e00-secret-scanning)
Notes
- None.
Acceptance criteria
- AC-1 integration Given a PR adding a dependency version with a known CRITICAL advisory, when CI runs, then
auditfails listing the advisory id and package path. - AC-2 integration Given a HIGH (not critical) advisory, when CI runs, then
auditpasses but writes a warning to the job summary. - AC-3 unit Given a waiver whose expiry date is in the past, when the waiver validator runs, then it fails naming the advisory.
- AC-4 unit Given a waiver missing a bead id or reason, when the validator runs, then it fails.
- AC-5 integration Given the nightly schedule, when a new CRITICAL advisory affects main, then the workflow fails and opens/updates a GitHub issue titled with the advisory id.
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #22 (2026-09-28)
area:infraepic:e00milestone:m0- In: security.yml
mw-e00.7Validate .beads/issues.jsonl parses and is well-formed in CI
Description
Why
The backlog is a project artefact that drives backlog.html; a malformed line from a bad merge must be caught before it breaks the page.
What
Script
scripts/validate-beads.mjs(100% covered) run in CI jobbeads-validatewhen.beads/**changes (and always on main): every non-empty line parses as JSON; each record hasid,title,status; ids match^mw-; no duplicate ids (last-wins is reported as an error unless bd format allows it); dependency targets exist; epic children reference an existing epic. Emits GitHub annotations with line numbers.Scope
- In: validate script + tests with fixture jsonl files
- In: CI job with path filter plus always-run on main
- Out: Label/AC style linting of bead content (possible follow-up)
Notes
- Confirm bd issues.jsonl semantics (one line per issue vs append log) when implementing; the duplicate rule must match bd behaviour.
Acceptance criteria
- AC-1 unit Given a jsonl with a truncated line 42, when the validator runs, then it exits non-zero with an annotation pointing to line 42.
- AC-2 unit Given a record whose dependency references
mw-e99.7which does not exist, when validated, then it fails naming both ids. - AC-3 unit Given an empty trailing line or CRLF line endings, when validated, then it passes.
- AC-4 integration Given the committed
.beads/issues.jsonl, when CI runsbeads-validate, then it passes. - AC-5 integration Given the script and fixtures, when coverage runs, then
scripts/validate-beads.mjsis at 100% on all metrics.
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #27 (2026-09-28)
area:infraepic:e00milestone:m0mw-e00.8Add PR template with Closes lines and a PR-body lint check
Description
Why
backlog.html and bead closure rely on explicit
Closes mw-…lines; they must be present and well-formed on every PR.What
Add
.github/pull_request_template.md(Summary, Closes lines, Test evidence, DoD checklist from contract §3, CHANGELOG note, coverage exclusions touched). Add apr-lintjob (pull_request: opened/edited/synchronize) that parses the PR body and fails if there is noCloses mw-<id>line (unless labelno-beadis applied by a maintainer for trivial chores) or if a referenced id does not exist in.beads/issues.jsonlon the PR head.Scope
- In: PR template
- In: scripts/pr-body-lint.mjs (shared parser with e00-backlog-pr-status, 100% covered)
- In: pr-lint job
- Out: Closing beads after merge (process, CLAUDE.md)
Notes
- Share the
Closesregex with the backlog generator:^\s*Closes\s+(mw-[a-z0-9.]+)\s*$(case-insensitive, one id per line).
Acceptance criteria
- AC-1 unit Given a body with lines
Closes mw-e00.3andcloses mw-e30.1, when parsed, then both ids are returned. - AC-2 unit Given a body mentioning
mw-e00.3only in prose (fixes part of mw-e00.3), when parsed, then no ids are returned. - AC-3 integration Given a PR without any Closes line and without the
no-beadlabel, whenpr-lintruns, then it fails with a message showing the expected format. - AC-4 integration Given a PR whose Closes line references an id not in issues.jsonl, when
pr-lintruns, then it fails naming the id. - AC-5 integration Given the PR body is edited to add a valid Closes line, when the
editedevent fires, thenpr-lintre-runs and passes.
Waits on
- mw-e00.7 Validate .beads/issues.jsonl parses and is well-formed in CI done
Closed
Completed in PR #30 (2026-09-28)
area:infraepic:e00milestone:m0mw-e00.10Protect main: squash-only, required checks, auto-delete branches
Description
Why
Contract §1: all work via PR, squash merge only, CI green. Protection makes that a rule rather than a habit.
What
Configure (and codify in
scripts/configure-repo.shusinggh api) main branch protection: require PR, require status checkslint,typecheck,unit,coverage-gate,coverage-ratchet,e2e-smoke,build,secrets,audit,beads-validate,pr-lint; require branches up to date; disallow force-push and deletion; squash merge only (merge commits and rebase disabled); auto-delete head branches; linear history. Document the settings indocs/repo-settings.md.Scope
- In: configure-repo.sh (idempotent) + docs
- In: Verification script that reads settings via API and diffs against expected
- Out: Adding new checks later (each check bead updates the script)
Notes
- Solo-maintainer project: do not require approving reviews (would block the only maintainer); rely on required checks. Revisit if contributors join.
Acceptance criteria
- AC-1 manual Given repository settings, when a direct push to main is attempted, then it is rejected.
- AC-2 manual Given a PR with a failing required check, when merge is attempted, then GitHub blocks it.
- AC-3 integration Given the verify script, when run against the repo, then it reports zero drift from expected settings (squash-only, required checks list, auto-delete on).
- AC-4 manual Given a PR merged via the UI, when it completes, then only "Squash and merge" was offered and the head branch was deleted.
- AC-5 integration Given configure-repo.sh run twice, when the second run completes, then it makes no changes (idempotent).
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
- mw-e00.5 Block secrets via gitleaks in CI and an optional pre-commit hook done
- mw-e00.6 Fail CI on CRITICAL dependency CVEs and enable Dependabot done
- mw-e00.8 Add PR template with Closes lines and a PR-body lint check done
Closed
Completed in PR #38. Applied with scripts/configure-repo.sh: verify-repo-settings.sh reports no drift (AC-3), a second configure run changes nothing (AC-5), a direct push to main was rejected with GH006 (AC-1); strict required checks block failing PRs (AC-2) and squash-only + delete-branch are verified settings (AC-4). (2026-09-28)
area:infraepic:e00milestone:m0needs-humanmw-e00.11Generate backlog.html from issues.jsonl and deploy to GitHub Pages
Description
Why
A human-friendly public view of what is done and what is next keeps the project honest and lets playtesters and collaborators follow progress without installing bd.
What
As a developer or follower, I want a single-file backlog.html with Completed and Upcoming / In progress tabs so that I can see progress per epic and milestone at a glance.
scripts/backlog/generate.mjsreads.beads/issues.jsonland writessite/backlog.html(also served asindex.html): one self-contained file (inline CSS/JS, no external requests), two tabs "Completed" and "Upcoming / In progress", grouped by epic then milestone, each bead showing ID, title, priority, status, labels, expandable ACs and description (<details>), and dependencies (linked in-page, with blocker status). Per-epic progress bar (closed/total). Generated-at timestamp and source commit SHA. Workflow.github/workflows/backlog-pages.ymlbuilds and deploys to GitHub Pages on push to main andworkflow_dispatch.Scope
- In: Generator (pure functions: parse → model → render) with 100% coverage (listed in coverage-layers.json)
- In: HTML escaping of all bead text; a safe minimal markdown subset (headings, lists, code, bold) rendered without raw HTML passthrough
- In: Pages workflow using actions/upload-pages-artifact + actions/deploy-pages with
concurrency: pages - In: Light/dark theme via prefers-color-scheme; readable at 375 px width
- Out: GitHub PR overlay (merged/open PR status) and pull_request_target trigger — see e00-backlog-pr-status
- Out: Issue-editing UI (read-only page)
Notes
- Status mapping: closed → Completed; open/in_progress/blocked/deferred → Upcoming tab with status pill; epics sorted by id, milestones m0→post-mvp, then priority, then id.
- Keep render pure and deterministic given (issues, prStatus, now, sha) so snapshot tests are stable.
- Workflow permissions:
contents: read,pages: write,id-token: write.
Acceptance criteria
- AC-1 unit Given a jsonl with one closed and two open beads under
mw-e09, when generated, then the Completed tab lists 1 and the Upcoming tab lists 2 under the E09 heading with a 1/3 progress bar. - AC-2 unit Given a bead title containing
<script>alert(1)</script>, when rendered, then the output contains the escaped text and no executable script element from bead content. - AC-3 unit Given a bead with acceptance criteria, when rendered, then the ACs appear inside a collapsed
<details>element and the bead row shows ID, priority, status, labels and dependency links. - AC-4 unit Given a bead depending on an id not present in the file, when rendered, then the dependency shows as "unknown" rather than throwing.
- AC-5 unit Given an empty issues.jsonl, when generated, then both tabs render an empty-state message and the script exits 0.
- AC-6 integration Given the generated file, when opened in Playwright with network disabled, then it renders fully, tab switching works by click and keyboard, and there are zero console errors.
- AC-7 integration Given a push to main, when backlog-pages.yml runs, then the Pages URL serves the new file within that run;
workflow_dispatchproduces the same result. - AC-8 integration Given coverage runs, when complete, then
scripts/backlog/**is at 100% lines/branches/functions/statements.
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
- mw-e00.7 Validate .beads/issues.jsonl parses and is well-formed in CI done
Closed
Completed in PR #33 (2026-09-28)
area:toolsepic:e00milestone:m0mw-e00.12Reflect merged/open PR Closes lines in backlog.html immediately
Description
Why
Beads are closed locally after merge, so issues.jsonl lags reality; the public page must be accurate the moment a PR merges.
What
As a follower, I want beads referenced by merged PRs to appear as Completed (with a PR link) and beads in open PRs to appear In progress so that the page is correct even before local bead closure is pushed.
The generator gains a GitHub overlay: using
GITHUB_TOKEN, list PRs (paginated, REST or GraphQL), parseCloses mw-…lines with the shared parser from e00-pr-template, and compute: merged PR → bead Completed with link "PR #N" and merge date; open PR → bead In progress with link; closed-unmerged PR → ignored. Bead closed in jsonl wins over open-PR status. The Pages workflow addspull_request_target(types: opened, reopened, edited, closed) so the page updates on PR open/close; under pull_request_target it checks out the default branch only and never checks out or executes PR code.Scope
- In: scripts/backlog/github.mjs with injectable fetch, pagination and rate-limit handling (100% covered, fixture-based tests)
- In: Overlay merge rules + "source" badges (jsonl vs PR)
- In: Workflow trigger changes with
permissions: pull-requests: readand explicitref: ${{ github.event.repository.default_branch }} - In: Offline flag
--no-githubfor local runs - Out: Automatically closing beads in jsonl from CI (process stays local per CLAUDE.md)
Notes
- Security: pull_request_target runs with a write-capable token in base context — never
actions/checkoutwithref: github.event.pull_request.head.sha, never run scripts from the PR; the PR body is treated as untrusted data (only regex-extracted ids are used). - Cache API responses per run; cap to last 500 PRs by default.
Acceptance criteria
- AC-1 unit Given jsonl bead
mw-e09.3open and a merged PR #57 whose body hasCloses mw-e09.3, when generated, thenmw-e09.3is in the Completed tab with a link to PR #57. - AC-2 unit Given an open PR #60 with
Closes mw-e09.4, when generated, thenmw-e09.4shows status "in progress (PR #60)" in the Upcoming tab. - AC-3 unit Given a closed-unmerged PR referencing
mw-e09.5, when generated, thenmw-e09.5keeps its jsonl status. - AC-4 unit Given the GitHub API returns 403 rate-limited or a network error, when generated, then the page is still produced from jsonl alone with a visible "GitHub status unavailable" banner and exit code 0.
- AC-5 unit Given a PR body referencing an id not present in jsonl, when generated, then it is listed in an "Unknown references" footer instead of crashing.
- AC-6 unit Given 3 pages of PR results, when fetched, then all pages are consumed via the Link header and results merged.
- AC-7 integration Given the workflow file, when statically checked by a test, then no step under pull_request_target checks out a PR head ref or runs
pnpm installfrom PR contents. - AC-8 integration Given a PR is merged on GitHub, when the resulting workflow run completes, then the Pages site shows its beads as Completed without any local bd action.
Waits on
- mw-e00.11 Generate backlog.html from issues.jsonl and deploy to GitHub Pages done
- mw-e00.8 Add PR template with Closes lines and a PR-body lint check done
Closed
Completed in PR #39 (2026-09-28)
area:toolsepic:e00milestone:m0mw-e00.13Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001)
Description
Why
The renderer and physics choice shapes every render, animation and physics story; making it on evidence against our hardware baseline avoids a costly mid-project switch.
What
Time-box: 3 days. Question: which of Three.js + Rapier or Babylon.js + (Rapier | Havok) best serves a stylized third-person action RPG in the browser given our baseline? Build the same benchmark scene in
spikes/engine-three/andspikes/engine-babylon/: greybox dungeon room, 1 skinned glTF character with 3 blended animations + 30 skinned NPCs, 200 dynamic rigid bodies (boxes/barrels) with collisions, 8 dynamic point lights + 1 shadowed directional light, 1 particle emitter, post-processing bloom. Measure on the reference machine (High, 1440p-equivalent) and a low-tier machine if available: p50/p95 frame time, gzip bundle size, time-to-first-frame, JS heap. Evaluate physics determinism: Rapier deterministic build (@dimforge/rapier3d-deterministic) snapshot/restore and cross-browser state-hash equality, and whether gameplay physics can live behind thesrc/simboundary. Also score TS ergonomics, glTF/animation retargeting, inspector/debug tooling, WebGPU path, docs/community, licence.Scope
- In: Two spike prototypes (not production code), scripted benchmark harness, results table
- In: docs/adr/0001-engine-and-physics.md with weighted scorecard, decision, consequences, rejected option and revisit triggers
- In: docs/adr/README.md + ADR template (first ADR)
- Out: Production renderer bootstrap (e00-render-bootstrap)
- Out: 3D asset sourcing pipeline (e37-3d-pipeline-decision)
Notes
- Keep spike code out of src/ and out of coverage; delete or archive after the ADR merges.
- Related: e37-3d-pipeline-decision should consume the glTF/animation findings.
Acceptance criteria
- AC-1 manual Given both prototypes, when run on the reference machine with the scripted harness, then a results table (p50/p95 frame time, bundle gz KB, TTFF, heap) for each is recorded in the ADR with browser/OS versions.
- AC-2 integration Given the Rapier deterministic build, when the same 600-step input script runs twice in Chrome and once in Firefox, then the ADR records whether final state hashes match, with the script committed in
spikes/. - AC-3 manual Given the evaluation, when ADR-0001 is written, then it contains a weighted scorecard, the chosen renderer and physics engine, rejected options with reasons, and explicit revisit triggers.
- AC-4 manual Given neither option reaches p95 ≤ 16.7 ms in the benchmark on High, when the ADR is written, then it records the gap, the mitigations assumed (e.g. LOD, instancing, fewer shadow casters) and proposes budget changes rather than silently accepting.
- AC-5 manual Given the ADR, when merged, then the ADR states whether gameplay physics runs inside
src/sim(deterministic) or insrc/gamewith sim-owned outcomes, and the contract's "the renderer" wording remains valid.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
Closed
Completed in PR #72 (2026-09-29)
area:renderepic:e00milestone:m0mw-e00.14Implement seeded RNG streams and injected clock for the sim
Description
Why
Determinism starts with randomness and time being inputs, not ambient globals — required for replays, saves and reproducible bugs.
What
Add
src/sim/rng: a fast, serializable PRNG (e.g. PCG32 or xoshiro128**) with named sub-streams (rng.stream("loot")) so adding a consumer in one system does not perturb others; helpers (int range, float, chance, pick, shuffle, weighted). Addsrc/sim/clock: a sim clock advanced only by the fixed-step loop (tick count, sim time in ms as integer ticks × step).Scope
- In: PRNG with serialize/restore state
- In: Named stream derivation from a root seed (hash of seed + name)
- In: Sim clock type used by all systems
- Out: The loop itself (e00-sim-core)
Notes
- Avoid Math.* transcendental functions in RNG; use 32-bit integer ops (
Math.imul,>>> 0) which are specified exactly.
Acceptance criteria
- AC-1 unit Given seed 12345, when 1,000 values are drawn from stream "loot", then they equal a committed golden sequence.
- AC-2 unit Given a stream whose state is serialized after 500 draws and restored into a new instance, when 500 more draws are made, then they equal the original instance's next 500.
- AC-3 unit Given streams "loot" and "ai", when extra draws are added to "ai", then the "loot" sequence is unchanged.
- AC-4 unit Given
int(5, 5)orpick([]), when called, then it returns 5 and throws a typedEmptyChoiceErrorrespectively. - AC-5 unit Given 1,000,000
float()draws, when bucketed into 10 bins, then each bin holds 10% ± 0.5%.
Waits on
- mw-e00.2 Lint src/sim for nondeterminism and forbidden layer imports done
Closed
Completed in PR #28 (2026-09-28)
area:simepic:e00milestone:m0mw-e00.15Build deterministic fixed-step sim loop, entity store and event bus
Description
Why
Every rule of the game (constitution: systemic world, "Wait… I can do that?") lives in one deterministic, testable simulation core.
What
Add
src/sim/core:Worldwith fixed timestep (default 60 Hz, configurable), ordered system scheduler, entity/component store (typed component definitions, stable integer entity ids, dense storage, queries with deterministic iteration order), deferred entity creation/destruction applied at end of tick, and a typed event bus (events emitted during tick N are delivered in emission order at defined phase boundaries).step(inputs)is the only way time advances; inputs are a per-tick command list.Scope
- In: World, scheduler, component registry, queries, event bus, command/input queue
- In: Snapshot/restore API hook for components (consumed by state hash and saves)
- In: Micro-benchmarks via
vitest bench - Out: Canonical state hashing (e00-sim-state-hash)
- Out: Render interpolation and rAF binding (e00-game-loop-bridge)
- Out: Physics integration (per ADR-0001)
Notes
- Entity ids are never reused within a session (generation counter) to avoid stale-reference bugs.
- Cross-browser float determinism is not guaranteed for Math.sin/cos/exp; document the policy: determinism target is same build + same engine; gameplay-critical trig goes through
src/sim/mathwrappers so a deterministic implementation can be swapped in.
Acceptance criteria
- AC-1 unit Given systems A, B, C registered in that order, when
step()runs, then they execute in order A→B→C every tick. - AC-2 unit Given an entity destroyed by system A during tick N, when system B queries in the same tick, then the entity is still visible, and from tick N+1 it is gone.
- AC-3 unit Given two worlds with the same seed and identical input lists for 1,000 ticks, when compared, then their component snapshots are deep-equal.
- AC-4 unit Given an event emitted by a handler of another event, when the bus flushes, then the nested event is delivered after the current event in FIFO order and a cycle guard throws after 10,000 events in one flush.
- AC-5 unit Given a query for components [Position, Flammable], when entities are added in random order, then iteration order is ascending entity id.
- AC-6 perf Given 5,000 entities with 3 components and 10 trivial systems, when
vitest benchruns in Node on CI, then meanstep()time is ≤ 1 ms.
Waits on
- mw-e00.14 Implement seeded RNG streams and injected clock for the sim done
Closed
Completed in PR #32 (2026-09-28)
area:simepic:e00milestone:m0mw-e00.16Add canonical sim state serialization and hashing
Description
Why
Replays, save verification and desync debugging all need one canonical, order-independent fingerprint of sim state.
What
Add
src/sim/snapshot: canonical serialization of world state (sorted entity ids, component fields in declared order, floats encoded via their IEEE-754 bits, RNG states, clock) and a fast non-cryptographic hash (e.g. xxHash32/FNV-1a 64) over it. ProvidediffSnapshots(a, b)returning the first differing entity/component/field for debugging.Scope
- In: Canonical encoder, hash, diff utility
- Out: Save file format (e30-save-serialization reuses the encoder)
Notes
- None.
Acceptance criteria
- AC-1 unit Given two worlds with identical state built via different entity insertion orders but the same ids, when hashed, then the hashes are equal.
- AC-2 unit Given a single component float differing by 1 ULP, when hashed, then the hashes differ and
diffSnapshotsnames the entity, component and field. - AC-3 unit Given
-0vs+0and two different NaN payloads, when encoded, then the documented canonicalisation rule is applied consistently (NaN canonicalised; ±0 distinct) and tested. - AC-4 perf Given 5,000 entities × 3 components, when hashed in Node, then it completes in ≤ 5 ms.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
Closed
Completed in PR #37 (2026-09-28)
area:simepic:e00milestone:m0mw-e00.17Record inputs and replay them to identical state hashes in tests
Description
Why
Contract §3 determinism policy: recorded input logs must replay to identical sim state hashes — our strongest regression net for emergent systems.
What
Add
src/sim/replay: a replay file format ({formatVersion, buildSha, contentHash, seed, stepHz, inputs: per-tick command lists (run-length encoded), checkpoints: [{tick, hash}]}), a recorder that captures commands fed toWorld.step, and a player that re-runs them asserting checkpoint hashes. Vitest helperexpectReplay("replays/foo.json")and apnpm replay:recorddev hook in the game layer. Golden replays live intest/replays/and run in CI.Scope
- In: Format (zod schema), recorder, player, checkpoint every N ticks (default 60)
- In: Vitest helper + one golden replay against the core sim
- In: Clear failure output using
diffSnapshotsat the first mismatching checkpoint - Out: Bug-report capture UX (e33-replay-bug-capture)
- Out: Game-layer keyboard/mouse capture (lives with e02-input-actions integration)
Notes
- Replays record *sim commands* (post input mapping), not raw DOM events, so they survive key remapping.
- When content or sim changes intentionally alter outcomes,
pnpm replay:reblessregenerates hashes and the diff must be reviewed in the PR.
Acceptance criteria
- AC-1 unit Given a recorded 3,600-tick session, when replayed, then every checkpoint hash matches and the final hash equals the recorded final hash.
- AC-2 unit Given a sim change that alters one component at tick 1,234, when the golden replay runs, then it fails reporting checkpoint tick 1,260 (first after divergence) and the differing entity/field.
- AC-3 unit Given a replay whose
formatVersionis newer than supported, when loaded, then it fails with a typedUnsupportedReplayVersionerror. - AC-4 unit Given a replay whose
contentHashdiffers from current content, when run, then the harness reports "content changed" distinctly from a determinism failure. - AC-5 integration Given
test/replays/*.json, when CI runs unit tests, then all golden replays execute and pass.
Waits on
- mw-e00.16 Add canonical sim state serialization and hashing done
Closed
Completed in PR #43 (2026-09-28)
area:simepic:e00milestone:m0mw-e00.18Load schema-validated content with a per-entry test harness
Description
Why
Spells, items, creatures and puzzles are data, not code (constitution §10); designers need fast, safe iteration with errors caught before runtime.
What
Add
src/content: a registry of content types, each with a zod schema and a folder undercontent/<type>/*.json; a loader (Viteimport.meta.globin the app, fs in Node tests) that validates, reports all errors with file path + JSON path, and freezes results into a typed, id-indexed catalogue. Cross-reference helper (ref("creature", id)) validated after load. Content hash over canonical content for replay/save compatibility. Test harness:describeContent("spell", (entry) => …)generates one test per entry, and a coverage check fails if any content entry has no exercising test.Scope
- In: Registry, loader, error formatting, cross-ref validation, content hash
- In: Example content type
testpropwith 2 entries - In: Per-entry test generator + "every entry exercised" check
- In: JSON Schema export from zod for editor autocompletion (
content/*.schema.json) - Out: Specific schemas (spells e06, creatures e12, items e17, …)
- Out: CLI validator/scaffolder and hot reload (e33-content-validator-cli, e33-content-scaffolder, e33-content-hot-reload)
Notes
- None.
Acceptance criteria
- AC-1 unit Given a content file with a missing required field and a wrong type in two places, when loaded, then all three errors are reported with file path and JSON pointer in one run.
- AC-2 unit Given two entries with the same id in different files, when loaded, then loading fails naming both files.
- AC-3 unit Given an entry referencing
creature:goblin-archerthat does not exist, when cross-refs validate, then it fails naming source entry and missing target. - AC-4 unit Given unchanged content in a different file order, when the content hash is computed, then it is identical.
- AC-5 integration Given a content entry with no test that exercises it, when the content-coverage check runs in CI, then it fails listing the entry id.
- AC-6 content Given the example
testpropentries, when validated, then they pass and the loaded catalogue is deeply frozen.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
Closed
Completed in PR #35 (2026-09-28)
area:contentepic:e00milestone:m0mw-e00.19Bootstrap the chosen renderer and physics with a boot smoke test
Description
Why
The ADR decision must become a running, measurable baseline before any visual gameplay work begins.
What
Install the renderer and physics chosen in ADR-0001; add
src/render/bootstrapcreating canvas, renderer, scene, camera, resize/DPR handling (render scale setting hook) and a clear teardown; lazy-load the physics WASM with a loading state. Playwright smoke: app boots, first frame renders, no console errors, canvas non-blank (pixel sample check), resize works.Scope
- In: Renderer + physics bootstrap, dispose(), DPR cap
- In: Unsupported-browser fallback screen (WebGL2/WASM missing) — minimal version
- In: Playwright boot smoke spec with screenshot artifact
- In: coverage-exclusions.md row for render bootstrap
- Out: Full capability detection matrix (e32-capability-detection)
- Out: Greybox content (e00-greybox-testbed)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given the built app, when loaded in Chromium, then the first frame renders within 3 s locally, the canvas has non-uniform pixels, and there are zero console errors or warnings from our code.
- AC-2 e2e Given the viewport resizes from 1280×720 to 1920×1080, when the next frame renders, then canvas drawing-buffer size matches viewport × min(devicePixelRatio, cap).
- AC-3 e2e Given WebAssembly is disabled via init script, when the app loads, then a readable "unsupported browser" screen appears instead of a blank page or uncaught error.
- AC-4 integration Given
dispose()is called, when the renderer is recreated 5 times, then WebGL context count does not grow and no errors are logged.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #73 (2026-09-29)
area:renderepic:e00milestone:m0mw-e00.20Bind fixed-step sim to rAF rendering with interpolation
Description
Why
Smooth visuals at any refresh rate with a deterministic sim underneath is the backbone of "responsive controls feel excellent" (E02).
What
Add
src/game/loop: an accumulator-based loop on requestAnimationFrame that advancesWorld.stepat the fixed rate, caps catch-up steps (spiral-of-death guard), exposes interpolation alpha to render sync, and pauses the sim when the tab is hidden. Render sync reads sim snapshots (read-only view) and updates scene objects. Input sampling produces per-tick command lists (hook for e02-input-actions).Scope
- In: Loop, accumulator, max-steps-per-frame cap, pause/resume on visibilitychange
- In: Sim→render sync interface (entity ↔ scene object binding registry)
- In: Injected time source for tests
- Out: Action mapping (e02-input-actions)
- Out: Frame pacing analytics (e32-frame-pacing)
Notes
- None.
Acceptance criteria
- AC-1 unit Given a fake clock advancing 33.3 ms per frame at 60 Hz sim, when 60 frames run, then exactly 120 sim steps occur and alpha stays in [0,1).
- AC-2 unit Given a single frame delta of 2,000 ms (debugger pause), when the loop processes it, then at most 5 steps run and the excess time is dropped with a logged warning.
- AC-3 unit Given
visibilitychangeto hidden, when 10 s pass and the tab becomes visible, then 0 sim steps ran while hidden and no catch-up burst occurs. - AC-4 integration Given a 144 Hz and a 60 Hz simulated display, when the same inputs run for 600 sim ticks, then final sim state hashes are identical.
- AC-5 unit Given an entity destroyed in the sim, when render sync runs, then its bound scene object is disposed within the same frame.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.19 Bootstrap the chosen renderer and physics with a boot smoke test done
Closed
Completed in PR #74 (2026-09-29)
area:gameepic:e00milestone:m0mw-e00.21Create greybox testbed scene, level kit, scene loader and debug camera
Description
Why
Gameplay is never blocked by art; every system needs a fast place to be tried in-engine from day one.
What
As a developer, I want a greybox testbed and modular level kit so that I can prototype rooms, corridors and encounters in minutes.
Greybox kit primitives (floor, wall, ramp, stairs, pillar, doorway, platform, crate) with grid snapping and colour-coded materials by purpose (walkable, climbable, interactive, hazard). Scenes are data (
content/scenes/*.json: placements + entity spawns) loaded by a scene loader into both sim and renderer. Free-fly debug camera (toggle), scene picker via?scene=URL param, and a defaulttestbedscene (room → corridor → arena).Scope
- In: Level kit + scene schema (via e00-content-pipeline)
- In: Scene loader/unloader (sim entities + render objects + physics colliders)
- In: Debug fly camera,
?scene=param, on-screen scene name + build SHA - In: Playwright smoke loading each scene in
content/scenes - Out: Player controller and gameplay camera (e02-player-controller, e02-camera)
- Out: Debug console (e33-debug-console)
Notes
- Scenes may be authored by hand in JSON or exported from Blender via glTF extras later; keep the schema simple.
Acceptance criteria
- AC-1 e2e Given
?scene=testbed, when the app loads, then the room→corridor→arena scene renders with zero console errors within 5 s locally. - AC-2 unit Given a scene file, when loaded then unloaded, then the sim world has zero remaining scene entities and the physics world zero colliders.
- AC-3 content Given every file in
content/scenes, when validated, then it passes the scene schema and all referenced kit pieces exist. - AC-4 e2e Given
?scene=does-not-exist, when loaded, then a visible error lists available scenes and the app does not crash. - AC-5 e2e Given the debug camera toggled on, when WASD/mouse input is sent, then the camera moves and toggling off restores the prior camera.
- AC-6 perf Given the testbed scene, when measured on the reference machine (High), then p95 frame time ≤ 8 ms (half budget, leaving headroom for gameplay).
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e00.20 Bind fixed-step sim to rAF rendering with interpolation done
Closed
Completed in PR #76 (2026-09-29)
area:toolsepic:e00milestone:m0mw-e00.32Fix flaky e2e on main: mouse-turn helper and debug-console spec fail on CI
Description
Why
Main CI e2e-smoke failed after PR #164 (testbed-player 'debug console ... hands it back' failed all retries; testbed-player AC-5 needed retries). PR #165 e2e failed world-saves AC-4 three times at e2e/world-saves.spec.ts:152 (expect(turned).toBe(true)) — the mouse-turn helper never converged within 20 s on CI. Red main blocks all merges.
What
Diagnose from CI artifacts/logs (gh run view --log-failed; download playwright artifacts) and make the specs deterministic: e.g., turn via a debug/test hook that sets yaw directly, or a convergent controller that waits for data-player to update between mousemove events. Fix the debug-console spec root cause. No retries-bumping or skipping.
Acceptance criteria
AC-1 e2e The full Chromium e2e suite passes 3 consecutive CI runs with no retries used on the affected specs.
AC-2 e2e world-saves AC-4 and items specs turn the player through a convergent, frame-rate-independent path.
Closed
Completed in PR #168 (2026-10-03)
area:infraarea:qaepic:e00milestone:m0mw-e00.34Shard the e2e-smoke CI job so it stays under the 15-minute limit
Description
Why
e2e-smoke on main takes ~14m43s against its 15-minute timeout; PR #180's first run was cancelled at the limit. Every new spec pushes it over.
What
Run Playwright in 3 shards (matrix) and keep a required aggregator job named e2e-smoke that passes only if every shard passes, so branch protection is unchanged.
Acceptance criteria
AC-1 [ci] e2e runs as 3 parallel shards, each well under 15 minutes.
AC-2 [ci] The required check e2e-smoke fails if any shard fails or is cancelled, and passes when all pass.
Closed
Completed in PR #183 (2026-10-03)
area:infraarea:qaepic:e00milestone:m0mw-e00.9Create CHANGELOG.md and a check for user-facing changes
Description
Why
DoD item 8: players and testers need to know what changed between playtest builds.
What
Add
CHANGELOG.md(Keep a Changelog format,Unreleasedsection). Add a lightweight CI check: if a PR touchessrc/{game,ui,render,audio,sim,content}/**and has neither a CHANGELOG diff nor theno-changeloglabel, fail with guidance.Scope
- In: CHANGELOG.md
- In: changelog check job/script
- Out: Release tagging automation (later, if needed)
Notes
- None.
Acceptance criteria
- AC-1 integration Given a PR modifying
src/sim/foo.tswithout touching CHANGELOG.md or labellingno-changelog, when CI runs, then the changelog check fails. - AC-2 integration Given a PR touching only
docs/**or.github/**, when CI runs, then the check passes without a CHANGELOG entry. - AC-3 unit Given a CHANGELOG with no
## [Unreleased]heading, when the check parses it, then it fails.
Waits on
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #50 (2026-09-28)
area:infraepic:e00milestone:m0mw-e00.22Write CONTRIBUTING.md covering worktrees, beads, DoD and ADRs
Description
Why
Humans and agents both contribute; one short guide prevents process drift (wrong branch, missing Closes lines, unsigned commits).
What
Document: worktree-per-branch flow, bead lifecycle (claim → PR with Closes lines → close after merge), commit sign-off (DCO), squash-merge, DoD, coverage layers and how to request an exclusion, determinism rules for src/sim, ADR process (docs/adr), CHANGELOG convention, how to regenerate backlog.html locally. Add a DCO check job if sign-off is required.
Scope
- In: CONTRIBUTING.md, link from README and AGENTS.md
- In: DCO check (GitHub DCO app or action) as a required check
- Out: Code of conduct / licence choice (separate decision if needed)
Notes
- None.
Acceptance criteria
- AC-1 manual Given a new contributor following only CONTRIBUTING.md, when they make a trivial change, then they produce a PR with a Closes line, signed-off commit and green CI without further guidance.
- AC-2 integration Given a PR with an unsigned commit, when CI runs, then the DCO check fails.
- AC-3 integration Given markdown link checking, when CI runs on docs changes, then CONTRIBUTING.md has no broken relative links.
Waits on
- mw-e00.10 Protect main: squash-only, required checks, auto-delete branches done
- mw-e00.4 Enforce coverage-exclusions.md as the only source of exclusions done
Closed
Completed in PR #388 (dco not yet a required check: owner repo-settings change) (2026-11-10)
area:infraepic:e00milestone:m0mw-e00.28Make simMath transcendentals bit-identical across platforms
Description
Why
Golden replays must pass on developer machines and CI alike. While building the replay harness (mw-e00.17) the first golden, recorded on macOS arm64, diverged in CI on Linux x64 at tick 60 by 1 ULP in a velocity fed by simMath.sin/cos: Node 24's Math.sin/cos are not bit-identical across OS/CPU even on the same Node version. Any sim feature using trig (controller headings, arcs, AI facing) will make its goldens platform-dependent.
What
Replace the Math-backed wrappers in src/sim/math.ts with pure-JS implementations built only from IEEE-exact ops (+ - * / sqrt), e.g. an fdlibm port for sin/cos/tan/atan2/exp/log/pow/hypot, and pin their outputs with golden vectors.
Scope
- In: deterministic simMath functions, golden vectors checked in CI, perf bench
- Out: changing call sites (they already route through simMath)
Notes
The core replay scenario (src/sim/replay/scenarios/core.ts) avoids trig until this lands; once it does, reintroduce trig there and re-bless.
Acceptance criteria
- AC-1 unit Given a table of golden input/output bit patterns for every simMath function, when run on Node in CI (Linux x64) and on macOS arm64, then every output is bit-identical to the table.
- AC-2 unit Given 10,000 random inputs, when compared with Math, then every result is within 1 ULP.
- AC-3 perf Given 1,000,000 sin+cos calls, when benchmarked, then they take no more than 3x the Math.sin/cos time.
Closed
Completed in PR #54 (2026-09-28)
area:infraarea:simepic:e00milestone:m0mw-e00.31Stop replay goldens conflicting on contentHash in every content PR
Description
Why
Every PR that touches content changes the content hash stored in each replay golden and in tests/integration/fixtures/testbed-player.replay.json. With parallel PRs this forces a rebase + rebless on every merge (PRs #139-#146 each rebased 2-3 times just for this), and costs CI minutes.
What
Decouple outcome checking from the content hash: e.g. store the content hash in a single generated file, or have goldens record per-system content fingerprints only for content they use, or warn (not fail) on a hash mismatch when replayed outcomes and final state hashes are identical. Keep detection of real behaviour changes.
Acceptance criteria
- AC-1 integration Given two PRs that each add unrelated content, when the second is rebased onto the first, then no replay golden or fixture file conflicts.
- AC-2 integration Given a content change that alters a replay's outcome, when tests run, then the replay check still fails.
Closed
Completed in PR #147 (2026-10-02)
area:infraepic:e00milestone:m0mw-e00.35Give the alert-machine fuzz test a CPU budget so it stops timing out under coverage
Description
Why
alert.test.ts AC-6 (10k-sequence fuzz) times out at vitest's 5 s default under coverage on busy runners, failing unit on unrelated PRs (#189, #190). Found under mw-e00.33.
What
Explicit 30 s budget with an explanatory comment.
Acceptance criteria
AC-1 unit The fuzz test carries an explicit CPU budget and the unit job passes on PR #189.
Closed
Completed in PR #192 (2026-10-03)
area:infraarea:qaepic:e00milestone:m0mw-e00.37CI: retry the gitleaks download in the secrets job
Description
GitHub release downloads intermittently return 504 from Actions runners (2026-11-08), failing the required secrets job on PRs #317 and #318 before any scan runs. Add curl --retry with backoff to the checksum-verified install step; checksum verification unchanged.
AC-1 [ci] The secrets job installs gitleaks with retries and still verifies the checksum.
Closed
Completed in PR #319 (2026-11-08)
area:infraepic:e00milestone:m0mw-e00.38CI: pin the gitleaks tarball sha256 instead of downloading checksums.txt
Description
Follow-up to mw-e00.37 (PR #319). During the 2026-11-08 GitHub release outage the checksums.txt download kept returning 504 even with retries. Pin the linux_x64 tarball sha256 (from the release's published asset digest) in security.yml; one fewer network dependency and a stricter check.
AC-1 [ci] The secrets job verifies the tarball against the pinned GITLEAKS_SHA256 and no longer downloads checksums.txt.
Closed
Completed in PR #320 (2026-11-08)
area:infraepic:e00milestone:m0mw-e00.39coverage-gate hits its 20-minute CI timeout
Description
pnpm test:coverage now runs 13-20 min on GitHub runners; 2 of the last 4 main runs and every #436 attempt were cancelled at the 20-minute job timeout. Raise timeout-minutes to 30 now; follow-up: shard the coverage run (vitest --shard + merged reports) so it stays well under.
Closed
Completed in PR #438 (2026-11-12)
area:infraepic:e00milestone:m0mw-e00.24Serve the site and backlog from thevesperbell.com
Description
Why
The owner bought thevesperbell.com. The backlog (and later the playable build) should live at the game's own domain instead of davetashner.github.io/thevesperbell.
What
As the owner, I want thevesperbell.com to serve the GitHub Pages site so that the backlog and future builds have a memorable, stable address.
Scope
- In: DNS records at the registrar (owner), GitHub Pages custom domain setting, HTTPS enforcement, updating links in CLAUDE.md and docs
- Out: the game landing page design (later), email on the domain
Notes
Apex needs A/AAAA records to GitHub Pages IPs (or ALIAS/ANAME); www needs a CNAME to davetashner.github.io. Verify the domain in GitHub account settings to prevent takeover.
Acceptance criteria
- AC-1 manual Given DNS records set at the registrar, when GitHub Pages checks the custom domain, then the check passes and HTTPS is enforced.
- AC-2 e2e Given a merge to main, when the Pages workflow finishes, then https://thevesperbell.com/backlog.html serves the latest backlog.
- AC-3 content Given the domain is live, when docs are read, then CLAUDE.md and the backlog contract link to thevesperbell.com.
- AC-4 manual Edge: given the domain is verified in GitHub account settings, when another account tries to claim it for Pages, then GitHub refuses.
Closed
Completed in PR #4 (fixes in #5 and #6) (2026-09-27)
area:infraepic:e00milestone:m0needs-humanmw-e00.25Opt-in main theme player on the landing page
Description
Why
The owner wants the title main theme (music-title-main-theme, Suno take in assets/_incoming/music/) playable on thevesperbell.com.
What
Encode web copies (Opus/Ogg 112 kbps primary, AAC/M4A 160 kbps fallback per audio bible §5.2) into site/audio/, and add a play/pause toggle in the hero. Off by default: no autoplay, preload=none, so nothing downloads until the visitor opts in. Record provenance (prompt file + CREDITS.md) and the owner's approval for website use.
Scope
- In: site/index.html toggle, site/audio/*, assets/prompts/music/music-title-main-theme.md, CREDITS.md row
- Out: in-game music integration (mw-e38.104), adaptive music
Acceptance criteria
- AC-1 manual Given a fresh visit, when the page loads, then no audio plays and no audio file is requested.
- AC-2 manual Given the visitor clicks the theme toggle, then the theme plays (looping) and the toggle shows a pause state; clicking again pauses.
- AC-3 manual Given a browser without Ogg Opus support (Safari), when toggled on, then the M4A fallback plays.
Closed
Completed in PR #12 (2026-09-27)
area:infraepic:e00milestone:m0mw-e00.26Dependabot: ignore @types/node and TypeScript majors
Description
Why
Dependabot proposed @types/node 24 -> 26 (PR #26), but the repo pins Node 24, so its types must stay 24.x. TypeScript 7 majors would also break lint until typescript-eslint supports them.
What
Add ignore rules for major updates of @types/node and typescript to .github/dependabot.yml, each with a comment saying when to lift it.
Scope
- In: .github/dependabot.yml
- Out: upgrading Node or TypeScript
Acceptance criteria
- AC-1 manual Given the dependabot config, when Dependabot runs, then it does not propose @types/node or typescript major updates.
Closed
Completed in PR #29 (2026-09-28)
area:infraepic:e00milestone:m0mw-e00.27Lint direct Math transcendental calls in src/sim (use simMath)
Description
Why
src/sim/math.ts (simMath) is the single place the sim touches Math.sin/cos/tan/atan2/exp/log/pow/hypot, whose results are only guaranteed identical within the same engine build. If a future cross-engine determinism need arises, one file changes. Nothing stops code calling Math.sin directly today.
What
Extend eslint/layers.js so src/sim (except src/sim/math.ts) cannot use Math transcendental functions directly; the message points to simMath. Add lint fixtures proving it.
Scope
- In: eslint rule + fixtures + test update
- Out: a cross-engine deterministic math library
Acceptance criteria
- AC-1 unit Given src/sim code calling Math.sin, when linted, then it errors naming simMath.
- AC-2 unit Given src/sim/math.ts calling Math.sin, when linted, then no error.
- AC-3 unit Given src/game calling Math.sin, when linted, then no error.
Closed
Completed in PR #36 (2026-09-28)
area:infraarea:simepic:e00milestone:m0mw-e00.29Reintroduce trig into the core replay scenario now that simMath is portable
Description
Why
The core golden replay (src/sim/replay/scenarios/core.ts) deliberately avoids simMath transcendentals because Node's Math.sin/cos differed between macOS arm64 and Linux x64. mw-e00.28 made simMath bit-identical on every platform, so the golden can again cover trig-driven motion, the most likely source of future cross-platform drift.
What
Feed at least one moving entity's velocity from simMath.sin/cos (and ideally atan2) in the core scenario, update the scenario's header comment, and re-bless the golden with pnpm replay:rebless.
Scope
- In: core scenario, its golden, its comment
- Out: simMath itself
Acceptance criteria
- AC-1 unit Given the core scenario with a trig-driven velocity, when replayed in CI on Linux x64, then every checkpoint hash matches the golden recorded on macOS arm64.
- AC-2 unit Given the re-blessed golden, when simMath.sin is perturbed by 1 ULP in a test, then the replay reports a divergence.
Closed
Completed in PR #56 (2026-09-28)
area:infraarea:simepic:e00milestone:m0mw-e00.36Clear remaining Dependabot alerts: source-map-js, minimatch, spikes/ vite
Description
Why
GitHub reports open Dependabot alerts on main (2026-11-07): high source-map-js <1.2.2 (transitive, root + spikes lockfiles), medium minimatch <11.0.1 (transitive), and high/medium/low vite <8.3.3 in spikes/pnpm-lock.yaml. happy-dom alerts are fixed by Dependabot PR #261.
What
Bump or pnpm-override the transitive deps in the root lockfile; update spikes/ lockfile (or delete spikes lockfile if spikes are dead).
Acceptance criteria
AC-1 [ci] Given main after merge, when listing Dependabot alerts, then none are open except ones with no patched version.
Closed
Completed in PR #300 (2026-11-08)
area:infraepic:e00milestone:m0
M1 Grey-box slice 1
mw-e00.23Build HUD/menu UI layer, component kit, focus nav and UI test harness
Description
Why
Every group has HUD and menu work (vitals, inventory, containers, death screen, class select, dialogue, spell hotbar, stealth HUD) and nobody owned the layer underneath. Without one kit, each screen reinvents focus handling, input capture and text scaling, and accessibility becomes a retrofit.
What
As a developer, I want one UI layer with a screen stack, reusable components, keyboard/gamepad focus navigation and test helpers so that every HUD element and menu is consistent, accessible and cheap to build.
- Layer & screen stack:
src/uirenders DOM UI over the canvas (unless the engine ADR mandates engine UI). A HUD layer (non-focusable, pointer-through) plus a menu stack: push/pop screens and modals; each screen declarespausesSimandcapturesInput. While a capturing screen is open, gameplay action frames are suppressed and (ifpausesSim) the loop bridge pauses the sim. - Component kit: button, list/grid (virtualised), tabs, toggle, slider, select, meter/bar (with delayed-damage segment), icon slot with count/cooldown, tooltip, toast, modal confirm, key-glyph prompt. Themed only through CSS custom-property tokens that follow the style bible's UI section.
- Focus & navigation: focus manager with spatial navigation, focus trap inside modals, focus restore on pop, visible focus ring. UI navigation reads keyboard and the Gamepad API through a small UI input adapter (
ui.up/down/left/right/confirm/back/tabPrev/tabNext), so it does not depend on gameplay remapping; key glyphs are resolved later from e02-input-actions bindings. - Scaling & comfort hooks:
--ui-text-scale(0.8–2.0) andprefers-reduced-motion/setting hooks consumed by e31-text-scaling and e31-motion-comfort. - HUD binding: HUD widgets read an immutable view model derived from sim snapshots after each tick; they never mutate sim state (commands go through action frames). Dirty-checking avoids DOM writes when nothing changed.
- Test harness: component tests (Vitest + DOM environment) and Playwright helpers:
openScreen,navigate(path), text-overflow check, axe-core scan; a component gallery screen used by e2e tests.
Scope
- In: Screen stack, input capture, component kit, focus manager, UI input adapter, tokens, view-model binding pattern, test helpers, component gallery
- Out: Settings store and options screens — see e31-settings-framework (builds on this)
- Out: Per-screen features (inventory, dialogue, shop…) — owned by their epics
- Out: Full accessibility audit and per-menu ARIA coverage — see e31-menu-navigation, e31-accessibility-audit
- Out: Final UI art — see mw-e37 UI assets
Notes
Overlap resolved: e31-menu-navigation specified a "shared focus manager (spatial navigation)" at m3; the focus manager now lives here at m1, and e31-menu-navigation becomes the audit/ARIA/axe pass over every screen. Must not depend on e02-input-actions or e31-settings-framework (both build on top of it; avoiding a cycle).
Acceptance criteria
- AC-1 unit Given the HUD plus an open inventory modal that captures input, when
ui.back(Esc / gamepad B) is pressed, then the modal pops, focus returns to the element focused before it opened, and gameplay input capture is released on the same frame. - AC-2 integration Given a screen with
pausesSim: trueopen, when gameplay keys are pressed for 60 ticks, then no gameplay action frames are recorded and the sim tick counter does not advance. - AC-3 e2e Given the component gallery, when traversed with arrow keys and with an emulated gamepad D-pad, then every interactive component is reachable, the focus ring is visible with contrast ≥ 3:1, and focus never escapes an open modal.
- AC-4 integration Given
--ui-text-scaleset to 2.0, when the component gallery renders at 1280×720, then the overflow helper finds no clipped text node. - AC-5 unit Given a HUD bar bound to a view model, when the sim snapshot is unchanged between frames, then zero DOM writes occur; when health changes, the bar updates within one rendered frame.
- AC-6 unit Given a gamepad disconnects while a menu item is focused, when input continues from the keyboard, then focus is preserved and no error is thrown (edge).
- AC-7 e2e Given the component gallery, when axe-core runs, then there are zero serious or critical violations.
- AC-8 perf Given a HUD with health, stamina and 4 quick slots updating every tick, when profiled for 30 s, then UI update cost ≤ 0.5 ms per frame p95 on the reference machine (High).
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e00.20 Bind fixed-step sim to rAF rendering with interpolation done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #95 (2026-09-29)
area:uiclass:allepic:e00milestone:m1size:m- Layer & screen stack:
E01Playable Vertical SliceA player can start the game from a title screen, pick a class, spawn into a grey-box room, walk a corridor, fight a skeleton, loot it, save, die, respawn and reload — end to end, in a browser, verified by an automated e2e test. At m2 a grey-box class gauntlet proves every class's core verbs in one course.16/21 done · 76%
M1 Grey-box slice 15
mw-e01.1Write the m1 vertical slice spec and grey-box level plan
Description
Why
M1 is the first proof the game loop works end to end; everyone building toward it needs one shared definition of "done".
What
docs/design/vertical-slice.md: the exact flow (title → class select → spawn room → corridor → skeleton fight room → loot alcove → locked exit → slice complete), a top-down grey-box sketch with dimensions, which systems each beat exercises, the pass criteria for the e2e test, and what is explicitly faked (placeholder art, one class required, no dialogue).
Scope
- In: Slice flow, level sketch, beat → system mapping, pass criteria, placeholder list
- Out: Building the level (e01-slice-greybox-level)
- Out: m2 class gauntlet (e01-class-gauntlet-level)
Notes
Keep the slice under 5 minutes for a first-time player. Related: e35-fun-hypotheses (the slice should be the stimulus for e35-greybox-combat-test).
Acceptance criteria
- AC-1 content Given the spec, when reviewed, then every beat names the owning bead key (e.g. e04-melee-core, e18-containers) of the system it exercises.
- AC-2 content Given the spec, when reviewed, then it defines a single machine-checkable "slice complete" condition (world fact slice.complete = true).
- AC-3 content Given the spec, when reviewed, then it lists the failure paths in scope (player death, reload with no save, leaving the fight room mid-combat) and their expected outcomes.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
Closed
Completed in PR #131 (2026-10-02)
area:designepic:e01milestone:m1mw-e01.2Title screen flows into class select, spawn and continue
Description
Why
The player must be able to "enter the game" (E01 outcome) without developer tools.
What
As a player, I want a title screen with New Game, Continue and Load so that I can start or resume the slice in two clicks.
New Game → e19 class selection → loads the slice scene at its spawn point with the class kit. Continue loads the most recent save. Load opens e30-save-load-ui. Grey-box styling; build SHA shown (per e35-playtest-builds).
Scope
- In: Title screen, New Game/Continue/Load wiring, scene hand-off to the slice spawn point
- Out: Class selection screen (e19-class-selection)
- Out: Save/load menus (e30-save-load-ui)
- Out: Final title art/music (E37/E38)
Notes
The first scene id comes from a data field (game.startScene) so E24 can switch the start to the mountain road at m3 without code.
Acceptance criteria
- AC-1 e2e Given a clean profile, when the player clicks New Game, selects Knight and confirms, then the slice spawn room is playable within 10 s of page load on a 50 Mbps connection.
- AC-2 e2e Given no saves exist, when the title screen shows, then Continue is disabled and has an accessible "no saves yet" tooltip.
- AC-3 integration Given a save exists, when Continue is chosen, then the most recent save loads and the sim state hash equals the saved hash.
- AC-4 unit Given game.startScene references a scene id that does not exist, when the content check runs, then it fails naming the missing id.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e01.1 Write the m1 vertical slice spec and grey-box level plan done
- mw-e01.15 Lock classes that are not playable yet on the class select screen done
- mw-e19.5 Add class selection at new game that applies kit and capabilities done
- mw-e30.11 Build Continue / Load / Save menu screens done
Closed
Completed in PR #185 (2026-10-03)
area:gameepic:e01milestone:m1mw-e01.3Pause the game with resume, settings, save and quit to title
Description
Why
The m1 loop must be playable without dev tools: players need to stop, change settings, save and leave.
What
As a player, I want Esc (or Start) to pause the game and offer Resume, Settings, Save, Load and Quit to Title so that I control my session.
Pausing halts the sim step (and therefore the world clock). Save is disabled with a reason when the autosave "unsafe" predicate vetoes (combat, mid-air, dialogue). Quit to Title asks for confirmation when there is unsaved progress since the last save. Keyboard and gamepad navigable.
Scope
- In: Pause overlay on e00-ui-framework, sim pause/resume, wiring to settings, save and load screens, quit-to-title teardown
- Out: Settings screens (e31-settings-framework)
- Out: Save/load screens (e30-save-load-ui)
Notes
Pause must be deterministic-safe: no sim ticks while paused, so replays are unaffected by pause duration.
Acceptance criteria
- AC-1 integration Given the slice running, when Esc is pressed, then the sim stops stepping within one frame and the state hash is unchanged after 10 real seconds paused.
- AC-2 integration Given the skeleton is in Combat, when the pause menu opens, then Save is disabled with the reason "Can't save during combat" and Resume still works (edge).
- AC-3 e2e Given progress since the last save, when Quit to Title is chosen, then a confirmation appears; on confirm the title screen shows with zero console errors and Continue loads the last save.
- AC-4 integration Given the menu opened with a gamepad, when navigating with the d-pad, then every option is reachable with visible focus.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e01.2 Title screen flows into class select, spawn and continue done
- mw-e30.11 Build Continue / Load / Save menu screens done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #194 (2026-10-03)
area:uiepic:e01milestone:m1mw-e01.4Build the slice grey-box level: room, corridor, arena, loot, exit
Description
Why
The loop needs a place: a small, readable space that exercises movement, a door, a fight and a reward.
What
As a player, I want a short grey-box level so that I can explore, fight and loot in one continuous space.
Scene data (content/scenes/slice.json): spawn room with an interactable door, a 20 m corridor with one side alcove, a 12×12 m fight arena with two pillars for spacing, a loot alcove, and an exit door locked by a key. Checkpoint trigger at the arena entrance. Uses only e00 greybox kit pieces.
Scope
- In: Scene data, navmesh bake, door/lock wiring, checkpoint trigger, slice-complete trigger
- Out: Skeleton spawn (e01-slice-skeleton-encounter)
- Out: Loot (e01-slice-loot)
Notes
Pillars exist so a knight can break line of attack and so the skeleton's lunge can whiff — combat must be fun without rewards (e04-skeleton-fun-validation).
Acceptance criteria
- AC-1 content Given slice.json, when validated, then it passes the scene schema and references only greybox kit pieces.
- AC-2 integration Given the scene loaded headless, when the navmesh is queried, then a path exists from spawn to exit and the arena is fully connected.
- AC-3 integration Given the player walks into the exit trigger without the key, when it fires, then the door stays locked and a "locked" prompt appears (no slice complete).
- AC-4 perf Given the slice scene, when idling at spawn, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e01.1 Write the m1 vertical slice spec and grey-box level plan done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
Closed
Completed in PR #167 (2026-10-03)
area:contentepic:e01milestone:m1mw-e01.5Place the Forgotten skeleton fight in the slice arena
Description
Why
"Fighting a skeleton should be enjoyable even when the player doesn't need XP or loot." The slice's fight is that promise in miniature.
What
As a player, I want a skeleton that wakes when I enter the arena and fights with readable attacks so that the slice has a real fight.
Spawns e13-forgotten-skeleton (miner variant) seated against the far pillar in a Unaware "resting" idle; enters Combat on sight or on noise in the arena. On death it drops its carried key.
Scope
- In: Encounter data (spawn, idle, wake trigger), key carried by the skeleton
- Out: Skeleton data and moves (e13-forgotten-skeleton)
- Out: AI (E11)
Notes
Leaving the arena mid-fight: the skeleton pursues to the corridor then returns to its post (leash 25 m) — failure creates a new situation, not a reset.
Acceptance criteria
- AC-1 integration Given the player enters the arena in view of the skeleton, when 2 s elapse, then the skeleton is in Combat state and has targeted the player.
- AC-2 integration Given the skeleton is killed, when its Died event fires, then the exit key is dropped as a world item within 1 m of its body.
- AC-3 integration Given the player flees beyond the 25 m leash, when 10 s pass, then the skeleton returns to its post in Searching then Unaware state and retains its damage (edge).
- AC-4 manual Given 3 internal testers, when they play the slice, then each can name at least one skeleton attack by its telegraph.
Waits on
- mw-e01.17 Leash the arena skeleton to its post and walk it home after a chase done
- mw-e01.4 Build the slice grey-box level: room, corridor, arena, loot, exit done
- mw-e11.13 Combat behaviour: engage, space, select attacks and share targets done
- mw-e12.4 Spawn data-defined creatures into the sim and grey-box scenes done
- mw-e13.1 Add the Forgotten skeleton with the e04 three-move set done
- mw-e17.7 Pick up, drop and throw items as physical world objects done
Closed
Completed in PR #253 (2026-10-24)
area:contentepic:e01milestone:m1mw-e01.6Add slice loot: skeleton key drop and an alcove chest
Description
Why
The loop is explore → fight → loot; the reward should matter (it opens the exit) and be interesting (item text with voice).
What
As a player, I want to loot the skeleton and a chest so that I get the exit key and a small surprise.
The skeleton carries "Rusted gallery key"; the alcove chest uses a slice loot table (a healing draught, a few coins, one oddity with bible-voice text). The exit door accepts the key via the keyring.
Scope
- In: Item data for the key and oddity, slice loot table, chest placement, door lock id
- Out: Container/loot runtime (E18)
- Out: Keyring (e17-keys-keyring)
Notes
Item text follows bible §7.4 voice (1–3 sentences with a turn of feeling). Placeholder text is fine until e39-item-creature-text.
Acceptance criteria
- AC-1 content Given the slice loot table, when validated, then it passes e18-loot-validator and every entry references an existing item.
- AC-2 integration Given the player holds the gallery key, when interacting with the exit door, then it unlocks and opens without opening the inventory.
- AC-3 integration Given the chest has been looted and the game is saved and reloaded, when the chest is opened again, then it is empty (edge).
Waits on
- mw-e01.4 Build the slice grey-box level: room, corridor, arena, loot, exit done
- mw-e17.5 Auto-use keys from the keyring on matching locks done
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e18.3 Add lootable containers that persist their contents done
- mw-e18.4 Build container window and loot pickup notifications done
Closed
Completed in PR #187 (2026-10-03)
area:contentepic:e01milestone:m1mw-e01.7Save and reload the slice with world state intact
Description
Why
Save/reload is part of the m1 loop; reloading must restore the world, not just the player.
What
As a player, I want to save anywhere safe and reload into exactly the same situation so that I trust the game with my time.
Wire the slice to autosave on the arena checkpoint and after slice completion; manual save from the pause menu (e01-pause-menu). Persisted: player vitals/inventory, skeleton alive/dead, chest looted, door state.
Scope
- In: Checkpoint autosave trigger, fact registrations for slice state, pause-menu save
- Out: Save format, storage and UI (E30)
- Out: World-state store (e27-world-state-store)
Notes
Autosave must defer while the skeleton is in Combat (e30-autosave safe predicate).
Acceptance criteria
- AC-1 integration Given the skeleton is dead and the chest looted, when the game is saved and reloaded, then the skeleton does not respawn, the chest is empty and the sim state hash matches.
- AC-2 integration Given the player enters the checkpoint trigger while the skeleton is in Combat, when the trigger fires, then the autosave is deferred until combat ends (edge).
- AC-3 e2e Given a manual save in the corridor, when the page is reloaded and Continue chosen, then play resumes in the corridor within 3 s warm.
Waits on
- mw-e01.4 Build the slice grey-box level: room, corridor, arena, loot, exit done
- mw-e12.14 Persist creature runtime state across save and load done
- mw-e27.2 Declare world facts in a content registry validated at load done
- mw-e27.4 Serialize world facts and entity deltas through the save system done
- mw-e30.4 Support manual save slots with metadata and thumbnails done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
Closed
Completed in PR #191 (2026-10-03)
area:gameepic:e01milestone:m1mw-e01.8Run the player death and respawn loop
Description
Why
E01 owns dying: death must be a quick, clear beat that invites another attempt ("failure creates stories").
What
As a player, I want dying to play a short death beat and then offer respawn or reload so that I am back in play within seconds.
On the player's Died event (e04-damage-model): freeze input, 1.5 s death beat (camera pull-back, fade), then hand off to the e30-death-reload screen. Adds a data-driven respawn-rule resolver (content/world/respawn-rules.json): rules of {region, conditions, destination, mode: reload|wake-in-place, factsToSet}. m1 ships a single rule: slice → reload last save. Emits player.died and player.respawned telemetry-ready events.
Scope
- In: Died handling for the player entity, death beat, respawn-rule schema + resolver, m1 rule, events
- Out: Death screen and reload mechanics (e30-death-reload)
- Out: Contextual wake-up rules such as the Fold (e01-contextual-respawn)
Notes
Coordinate with foundation: e30-death-reload owns the screen and reload; this item owns everything between Died and that screen plus the rule table. Related: e39-failure-narratives (texts), e34-gameplay-events (death telemetry).
Acceptance criteria
- AC-1 unit Given the player entity emits Died, when the next tick runs, then player input is ignored and exactly one player.died event is emitted with position and killer id.
- AC-2 e2e Given the player dies in the slice arena, when "Load last save" is chosen, then the game is playable within 3 s warm and the skeleton's state matches the save.
- AC-3 unit Given two respawn rules whose conditions both match, when resolved, then the rule with the higher priority wins and ties break by rule id deterministically.
- AC-4 unit Given no respawn rule matches the current region, when the player dies, then the resolver falls back to reload-last-save and logs a content warning (edge).
- AC-5 content Given respawn-rules.json, when validated, then every destination references an existing scene spawn point.
Waits on
- mw-e01.4 Build the slice grey-box level: room, corridor, arena, loot, exit done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e30.7 On death, offer reload of latest save or checkpoint done
Closed
Completed in PR #174 (2026-10-03)
area:gameepic:e01milestone:m1mw-e01.9Prove the slice plays start-to-finish in a CI e2e test
Description
Why
Releases must be trusted without reading the code; the m1 loop is the most important thing to never break.
What
A Playwright e2e test that boots the build, starts a new game as Knight, drives the slice via a recorded input log (e00-replay-harness) through fight → loot → save → deliberate death → reload → exit, and asserts slice.complete. Also asserts perf and load budgets on the reference CI runner profile.
Scope
- In: Recorded input log, Playwright spec, CI job wiring, perf/load assertions
- Out: Per-class gauntlet e2e (e01-class-gauntlet-e2e)
Notes
Replays must be regenerated when controller tuning changes; provide a pnpm script to re-record.
Acceptance criteria
- AC-1 e2e Given the CI build, when the slice e2e runs, then it reaches slice.complete = true with zero console errors or unhandled rejections.
- AC-2 e2e Given the scripted death step, when the reload completes, then the replay continues and the final sim state hash equals the committed golden hash.
- AC-3 perf Given the slice run, when measured, then initial load to playable <= 10 s at 50 Mbps throttling, warm reload <= 3 s and JS heap <= 1.5 GB.
- AC-4 e2e Given the build is served with IndexedDB unavailable, when the slice runs, then saving shows the documented fallback message and the run still reaches the exit (edge).
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
- mw-e01.2 Title screen flows into class select, spawn and continue done
- mw-e01.3 Pause the game with resume, settings, save and quit to title done
- mw-e01.5 Place the Forgotten skeleton fight in the slice arena done
- mw-e01.6 Add slice loot: skeleton key drop and an alcove chest done
- mw-e01.7 Save and reload the slice with world state intact done
- mw-e01.8 Run the player death and respawn loop done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
Closed
Completed in PR #253 (2026-10-24)
area:qaepic:e01milestone:m1mw-e01.15Lock classes that are not playable yet on the class select screen
Description
Why
The owner decided (2026-10-02) that the m1 slice supports the Knight only. Class choice is the game's core promise (CONSTITUTION §1–2), so the screen should still show all four classes while making it impossible to start a run the slice cannot support.
What
As a player, I want to see all four classes on the class select screen, with the ones not yet playable shown as locked and a reason, so that I know what is coming and cannot start a run the build does not support.
A data field
game.playableClasses(next togame.startScene, e01-title-new-game-flow) lists the selectable class ids; m1 ships["knight"]. Cards for other classes stay visible with their name, pitch and signature verbs, greyed, with a lock badge and the reason "Not playable in this build yet". They stay focusable (so screen readers announce the reason) but Confirm is disabled while one is focused. The Knight card is focused by default. Debug and test builds can override the list (e.g. the dev content layer) so e19-class-selection's per-class tests still run.Scope
- In:
game.playableClassesfield and validator, locked-card state on the e19-class-selection screen, default focus, debug override - Out: The selection screen and apply-class (e19-class-selection, mw-e19.5)
- Out: Class data (e19-class-definitions, mw-e19.4)
Notes
Spec: docs/design/vertical-slice.md §3 (beat B1). m2 unlocks classes by editing the list, with no code change.
Acceptance criteria
- AC-1 integration Given game.playableClasses = ["knight"], when the class select screen opens, then four cards show, the Knight card is focused and the other three show a lock badge and the reason "Not playable in this build yet".
- AC-2 integration Given focus on a locked card, when Confirm is pressed (keyboard, mouse or gamepad), then nothing starts and Confirm reports disabled with the same reason (edge).
- AC-3 content Given game.playableClasses lists a class id with no class data file, when content is validated, then validation fails naming the id.
- AC-4 unit Given game.playableClasses is empty, when validated, then validation fails (a build must offer at least one class) (edge).
- AC-5 e2e Given a production build, when the player picks New Game, then only the Knight can be confirmed and the slice spawns with player.class = knight.
Waits on
- mw-e19.5 Add class selection at new game that applies kit and capabilities done
Closed
Completed in PR #166 (2026-10-03)
area:gamearea:uiclass:allepic:e01milestone:m1- In:
mw-e01.17Leash the arena skeleton to its post and walk it home after a chase
Description
Why
Leaving the fight room mid-combat must create a new situation, not a reset (CONSTITUTION §2, "failure creates stories"). e01-slice-skeleton-encounter requires a 25 m leash, but no AI item owns a leash: combat behaviour (e11-combat-behaviour) pursues indefinitely and return-to-routine (e11-return-to-routine) is m2.
What
As a knight, I want the arena skeleton to chase me only so far from its post and then walk back, keeping its wounds, so that I can retreat, recover and come back to a fight I have already started.
A creature placement may carry
leash: { radius, post }(post defaults to the spawn point). In Combat, when the creature's own distance from its post exceeds the radius it drops the target and enters Searching at the leash edge; when Searching ends it paths back to its post (e11-patrol-routines resume) and settles to Unaware in its post idle. It keeps its health and poise damage. A target that steps back inside the radius during Searching can be re-acquired by normal perception.Scope
- In: Leash data on creature spawns, leash check in the combat loop, walk-home to the post, slice skeleton leash of 25 m
- Out: Post-alert changes such as relighting torches (e11-return-to-routine, mw-e11.15)
- Out: Search patterns (e11-search-behaviour, mw-e11.11)
Notes
Spec: docs/design/vertical-slice.md §6 (failure F5). With the skeleton's post at (2.5, 32.5) the leash edge crosses the corridor near z = 7.5, so the spawn room is out of reach.
Acceptance criteria
- AC-1 unit Given a creature in Combat with leash radius 25 m, when its distance from its post exceeds 25 m, then within one tick it drops the target and enters Searching.
- AC-2 unit Given the creature's Searching timer expires, when simulated, then it paths to its post, arrives and is Unaware in its post idle within 10 s on an open path.
- AC-3 unit Given the creature took 40 damage before leaving the leash, when it returns to its post, then its health is still 40 below max (edge).
- AC-4 unit Given a creature with no leash, when chasing beyond 25 m, then behaviour is unchanged (backwards compatible).
- AC-5 integration Given the slice arena and a player who flees down the corridor into the spawn room, when 15 s pass, then the skeleton never enters the spawn room and is back at its post.
Waits on
- mw-e11.13 Combat behaviour: engage, space, select attacks and share targets done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e11.9 Patrol routes, guard posts and idle routines authored as data done
Closed
Completed in PR #189 (2026-10-03)
area:gamearea:simepic:e01milestone:m1mw-e01.16Restart the slice from its start when the player dies with no save
Description
Why
"Failure creates stories" (CONSTITUTION §2), and failure must never strand the player. A death before the slice's first checkpoint (or with no save at all) leaves no save to reload; e30-death-reload then offers "Restart area", and in the slice that button needs a defined result.
What
As a player, I want "Restart area" on the death screen to put me back at the slice's start with my class kit so that dying before any save never leaves me stuck.
Restart area tears the world down and runs the same hand-off New Game uses (e01-title-new-game-flow): load
game.startSceneat its player-start spawn and apply the class chosen this session (e19-class-selection), with fresh world facts. It writes no save itself; the slice's first checkpoint then autosaves as normal.Scope
- In: Restart-area action in the slice, re-use of the New Game hand-off, class kept from the session
- Out: Death screen and option ordering (e30-death-reload, mw-e30.7)
- Out: Respawn-rule resolver (e01-death-respawn-loop, mw-e01.8)
Notes
Spec: docs/design/vertical-slice.md §6 (failure F2). In normal play the slice's first checkpoint (CP-1, the spawn-room door) autosaves within seconds, so this path is mostly reached with the debug console's kill command or a fresh profile where storage failed.
Acceptance criteria
- AC-1 integration Given no saves exist and the knight dies in the spawn room, when "Restart area" is chosen, then the slice reloads at player-start, player.class = knight, the knight kit is equipped and every slice fact has its default.
- AC-2 integration Given Restart area completes, when the sim state hash is taken, then it equals the hash of a New Game as knight at tick 0 (same seed).
- AC-3 e2e Given a clean profile and a death before CP-1, when "Restart area" is chosen, then the slice is playable within 3 s warm with zero console errors.
- AC-4 unit Given the session has no chosen class (state lost or corrupted), when Restart area runs, then it returns to the title screen instead of throwing and logs a warning (edge).
Waits on
- mw-e01.2 Title screen flows into class select, spawn and continue done
- mw-e01.8 Run the player death and respawn loop done
- mw-e30.7 On death, offer reload of latest save or checkpoint done
Closed
Completed in PR #211 (2026-10-04)
area:gameepic:e01milestone:m1mw-e01.18Show a slice-complete card and return to the title
Description
Why
The slice must end on a clear beat. Without one a player who walks through the exit wonders whether they finished, and the e01-slice-playtest measure "reached slice complete" needs a moment testers can see.
What
As a player, I want a short "Slice complete" card when I walk through the exit so that I know I finished and can go back to the title.
When the world fact
slice.completebecomes true, input is released to the UI and a grey-box card shows "Slice complete", the run time and the class, with "Return to title" focused. The sim keeps no further state. The completion autosave (e01-slice-save-points) happens before the card shows, so Continue from the title loads the completed slice.Scope
- In: Card on the e00-ui-framework kit, run timer readout, return to title, keyboard/gamepad focus
- Out: Ending screens, credits and music (E39/E38)
- Out: The slice.complete trigger and fact (e01-slice-greybox-level, mw-e01.4)
Notes
Spec: docs/design/vertical-slice.md §3 (beat B8). The e2e pass condition is the fact, not this card.
Acceptance criteria
- AC-1 integration Given slice.complete flips to true, when the next frame renders, then the card shows with "Return to title" focused and player input no longer moves the knight.
- AC-2 unit Given a run from New Game at tick 0 to completion at tick 9000 (60 Hz), when the card renders, then it shows 2:30.
- AC-3 e2e Given the card, when "Return to title" is chosen, then the title shows with zero console errors and Continue loads the completion save.
- AC-4 integration Given the completion autosave is vetoed or fails, when the card shows, then it states that the run was not saved instead of claiming it was (edge).
Waits on
- mw-e01.4 Build the slice grey-box level: room, corridor, arena, loot, exit done
- mw-e01.7 Save and reload the slice with world state intact done
Closed
Completed in PR #210 (2026-10-04)
area:gamearea:uiepic:e01milestone:m1mw-e01.19Open a hinged door the player is standing against without it blocking
Description
Why
The slice's first interaction is opening the spawn-room door. A player who walks up against a
hinged door and presses Interact sees it stop "blocked": the leaf's sweep test treats obstacles as
bounding spheres (src/sim/mechanisms/geometry.ts), and the player's sphere overlaps the leaf's
swept sector even though the door swings away from them. It resumes once they step back, but it
reads as broken. Found while building the slice level (mw-e01.4); its e2e steps back to work round it.
What
As a player, I want a door I open while standing against it to swing open, so that the first door of
the slice never looks stuck.
Scope
- In: obstacle test for hinged leaves (e.g. the actor's capsule footprint rather than a bounding
sphere, or ignoring obstacles on the side the leaf swings away from), with tests.
- Out: changing door profiles or the slice layout.
Notes
Repro: ?scene=slice, hold W into the wooden door until stopped (z ≈ 4.6), press E: data-mechanisms
shows spawn-door "blocked".
Acceptance criteria
AC-1 unit Given a closed hinged door swinging forward and an actor capsule touching its back face, when the door is opened, then it reaches open without blocking.
AC-2 unit Given an actor standing in the leaf's swept sector, when the door is opened, then it still stops blocked at the contact.
AC-3 e2e Given ?scene=slice, when the player walks into the spawn door and presses Interact, then the door opens without stepping back.
Closed
Completed in PR #169 (2026-10-03)
area:gamearea:simepic:e01milestone:m1mw-e01.21Slice playthrough replay hash varies run to run locally since liftable props (#306)
Description
Why
The slice golden replay (e2e/slice-playthrough.spec.ts AC-1/2/3) must be deterministic. Since PR #306 put physics props (bottle, barrel, clay pot) in the slice, a locally re-recorded golden fails replay 3/3 under heavy machine load: same final tick (manual-2 save at 2063) but a different saved-game hash each run (b00fcd3a, a8b9d7ad, c2efca02...). manual-1 save and load always match. Main at 2763905 (before #306) passed 3/3; adding only a new world property to older main also failed. CI on main (a094b04) passed with #306's committed golden, so CI is stable so far.
What
Find the source of run-to-run divergence (suspects: Rapier contact/sleep state of the new props depending on frame cadence or catch-up step limits; something reading render-time state into the sim; carry/impact-noise or breakables reacting to physics events in non-deterministic order). Dump the full saved-game JSON at manual-2 from two failing runs and diff it to see which entity/component differs.
Notes
Investigation evidence from the mw-e06.8 worker, 2026-11-08.
Acceptance criteria
AC-1 e2e Given a fresh slice:record, when the playthrough project runs 5 times locally under load (e.g. alongside the chromium project), then AC-1/2/3 passes every time.
Closed
Completed in PR #309 (2026-11-08)
area:gameepic:e01milestone:m1
M2 Class fantasies 1
mw-e01.11Connect scenes with area transitions and named spawn points
Description
Why
The MVP world is a network (town, forest, tower, mine, priory, labyrinth); players must move between areas seamlessly and saves must survive it.
What
As a player, I want to walk through a doorway, gate or tunnel into the next area so that the world feels continuous.
Transition volumes reference {targetScene, targetSpawn}; the loader unloads the current scene, persists entity deltas, loads the target, places the player at the spawn with camera facing, and triggers autosave. Loading screen with area name only if load exceeds 500 ms.
Scope
- In: Transition volume component, spawn point markers, load/unload orchestration, autosave hook, loading overlay
- Out: Asset streaming/compression (e32-asset-loading)
- Out: World connectivity design (e24-world-map-connectivity)
Notes
Companions/followers (Horn, Snig) transfer with the player if flagged follow=true.
Acceptance criteria
- AC-1 integration Given a transition from scene A to B, when the player enters the volume, then B loads, the player stands at the named spawn and A's entity deltas are persisted.
- AC-2 integration Given a creature in Combat pursuing the player, when the player transitions, then the creature does not follow unless flagged pursuer, and returns to Searching in A (edge).
- AC-3 unit Given a transition referencing a missing spawn id, when content is validated, then validation fails naming the scene and spawn.
- AC-4 perf Given a warm cache, when transitioning between two grey-box scenes, then time from trigger to playable <= 3 s on the reference machine.
- AC-5 e2e Given 50 back-and-forth transitions, when run in the memory leak harness, then heap growth after GC is <= 5% (ties to e32-memory-leak-test).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e27.3 Persist entity changes as deltas from authored level baselines done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
Closed
Completed in PR #242 (reload-per-crossing; in-place loader mw-ju8.28, leak run mw-ju8.27) (2026-10-05)
area:gameepic:e01milestone:m2
E02Core Character ControllerMovement, camera, jumping, climbing, stance, interaction, targeting and animation hooks feel excellent and responsive on keyboard + mouse (gamepad second). All movement rules live in src/sim as a deterministic kinematic controller driven by abstract input actions, so replays, AI and tests share the exact same rules the player feels.29/39 done · 74%
M1 Grey-box slice 21
mw-e02.1Map remappable KB/M input to deterministic per-tick action frames
Description
Why
Keyboard + mouse comes first (Technical constitution). Everything downstream — controller, combat, spells, replays, AI-driven test bots — must consume abstract actions, not raw keys, so the sim stays deterministic and remapping is free.
What
As a player, I want every game action bound to remappable keys and mouse buttons so that I can play comfortably and the game reacts identically every time.
An action map (Move, Look, Jump, Sprint, Crouch, Interact, LockOn, CycleTarget, PrimaryAttack, SecondaryAttack, Ability1–4, Inventory, Pause, …) turns DOM input into an immutable ActionFrame sampled once per fixed sim tick (digital buttons with pressed/held/released edges, analog move vector, look delta). The sim only ever sees ActionFrames.
Scope
- In: Action registry (data-defined), default KB/M bindings, runtime rebinding API with conflict detection
- In: ActionFrame sampling aligned to e00-sim-core ticks; pointer lock handling
- In: Bindings persisted through the settings store
- Out: Gamepad — see e02-input-gamepad
- Out: Remapping UI screen — see mw-e31 (uses this API)
Notes
ActionFrame is plain data so e00-replay-harness can record/replay it verbatim. Keep raw DOM handling in src/game; the frame type lives in src/sim. Related: mw-e31 accessibility remapping UI.
Acceptance criteria
- AC-1 unit Given the default binding set, when W and Space are pressed in the same tick, then the ActionFrame contains move=(0,1) and jump.pressed=true, and on the next tick jump.held=true, jump.pressed=false.
- AC-2 unit Given a rebind of Jump to F, when F is pressed, then Jump fires and Space no longer does.
- AC-3 unit Given a rebind that collides with an existing binding in the same context, when applied, then the API returns a conflict listing both actions and leaves bindings unchanged.
- AC-4 unit Given a key pressed and released within a single tick (between samples), when the frame is sampled, then pressed=true and released=true are both reported so taps are never lost.
- AC-5 integration Given pointer lock is lost (Esc / window blur), when the next frame is sampled, then all held actions are released and look delta is zero (no stuck keys).
- AC-6 integration Given bindings saved via e31-settings-framework, when the page reloads, then custom bindings are restored.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
Closed
Completed in PR #77 (2026-09-29)
area:gameclass:allepic:e02milestone:m1size:mmw-e02.2Move the player with a deterministic third-person kinematic controller
Description
Why
Movement is the verb the player uses every second; if it feels mushy nothing else matters. Keeping it in src/sim means replays, AI and tests share the exact rules the player feels.
What
As a player, I want tight, weighty walk/run/sprint, jump and crouch movement so that exploring the world feels excellent.
A kinematic capsule controller in src/sim consumes ActionFrames and the camera yaw, resolves collisions against the physics world, handles slopes, steps, ground snapping, jump arcs, coyote time and jump buffering, and exposes a movement state (grounded/airborne/crouched/sprinting) plus the capsule shape (crouch lowers it). Climb/traverse extension points are defined here and filled in by later items.
Scope
- In: Walk/run/sprint with accel/decel curves, air control, gravity, jump, coyote time (≤ 120 ms), jump buffer (≤ 150 ms)
- In: Slope limit, step-up, ground snapping, moving-platform carry
- In: Crouch stance with reduced capsule and uncrouch obstruction check
- In: Traversal hook interface (climb, mantle, swim) for later items
- Out: Ledges/climbing — see e02-ledge-mantle, e02-climb-surfaces
- Out: Stance stealth modifiers — see e02-stance-profiles
- Out: Dodge roll — see e04-melee-core
Notes
Starting numbers (all in content data, tunable): run 5.0 m/s, sprint 7.5 m/s, crouch 2.2 m/s, reach max speed in 0.15 s, stop in 0.10 s, jump apex 1.2 m, step height 0.35 m, slope limit 45°. Physics queries go through a thin collision-world interface so the sim is unit-testable with a fake world; the Rapier implementation follows e00-engine-decision.
Acceptance criteria
- AC-1 unit Given a grounded player at rest on flat ground, when move=(0,1) is held, then speed reaches 5.0 m/s ± 0.05 within 0.15 s and releasing stops the player within 0.10 s.
- AC-2 unit Given a grounded player, when jump is pressed, then apex height is 1.2 m ± 0.02 and the player lands on the same tick count on every run.
- AC-3 unit Given the player walked off a ledge 100 ms ago, when jump is pressed, then the jump executes (coyote time); at 130 ms it does not.
- AC-4 unit Given a 0.30 m step, when walking into it, then the player steps up without losing more than 10% speed; given a 0.40 m step, then the player is blocked.
- AC-5 unit Given a 50° slope, when walking up it, then the player slides back down and cannot ascend; a 40° slope is climbable at ≥ 80% speed.
- AC-6 unit Given a crouched player under a 1.2 m ceiling, when crouch is released, then the player stays crouched until the obstruction clears (edge).
- AC-7 integration Given the greybox traversal course and a recorded input log, when replayed twice, then final sim state hashes are identical.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
Closed
Completed in PR #75 (input taken as the MovementActions/CharacterInput subset, since mw-e02.1 is still open) (2026-09-29)
area:simclass:allepic:e02milestone:m1size:lmw-e02.3Move controller tuning values into schema-validated content data
Description
Why
Feel is found by iteration (rapid iteration is a technical pillar). Designers must tune speeds, jump arcs and camera feel live without code changes.
What
Controller and camera parameters live in a zod-validated content file (content/controller/*.json) with per-class overrides (e.g. thief crouch faster, knight in heavy armor slower). The debug console can live-edit values and dump the modified file.
Scope
- In: Schema + default file + per-class override merge
- In: Debug console commands: ctl.get, ctl.set, ctl.dump
- In: Hot reload in dev
- Out: Armor weight effects on movement — see mw-e17 (supplies modifiers)
Notes
Values documented with units in the schema description fields.
Acceptance criteria
- AC-1 content Given the controller data file, when validated, then every numeric field has declared units and range and the default file passes.
- AC-2 unit Given a class override that sets only crouchSpeed, when merged, then all other values come from the base profile.
- AC-3 unit Given an out-of-range value (negative gravity), when loaded, then validation fails with the field path in the error.
- AC-4 integration Given the debug console, when ctl.set runSpeed 6 is executed, then the next tick uses 6.0 m/s and ctl.dump outputs a valid file.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #135 (2026-10-02)
area:contentarea:toolsepic:e02milestone:m1size:smw-e02.4Follow the player with a colliding, orbiting third-person camera
Description
Why
A camera that clips into walls or loses the player breaks exploration and combat. Camera options are also an accessibility concern (E31).
What
As a player, I want a smooth third-person camera I control with the mouse so that I can always see my character and surroundings.
Orbit camera with shoulder offset, pitch limits, sensitivity/invert options, zoom, collision pull-in against level geometry (never inside walls), and smooth recovery. Camera yaw feeds the controller as data so movement is camera-relative but still deterministic.
Scope
- In: Orbit + shoulder offset, pitch clamp (-70°..+60°), zoom 2–6 m, sensitivity + invert Y
- In: Sphere-cast collision pull-in, recovery easing, transparency fade of occluders near the camera
- In: Camera yaw injected into ActionFrame for camera-relative movement
- Out: Lock-on framing — see e02-lock-on
- Out: Bow aim camera — see e05-bow-core
- Out: Motion sickness options UI — see mw-e31
Notes
Camera lives in src/game/src/render (presentation), but the yaw consumed by sim is quantised into the ActionFrame so replays are exact. Default FOV 70°.
Acceptance criteria
- AC-1 unit Given pitch input beyond the clamp, when applied, then pitch stays within -70°..+60°.
- AC-2 unit Given a wall between the ideal camera position and the player, when resolved, then the camera is placed ≥ 0.2 m in front of the wall on the same frame (no clipping frame).
- AC-3 unit Given the occluder disappears, when recovering, then the camera returns to the ideal distance over 0.3 s ± 0.05 without overshoot.
- AC-4 unit Given the camera yaw is 90°, when move=(0,1), then the player moves along the camera forward vector (camera-relative).
- AC-5 e2e Given the greybox testbed, when the Playwright bot walks the player along a narrow corridor with pillars, then no rendered frame has the near plane inside geometry (depth probe) and there are no console errors.
- AC-6 manual Given a playtest, when 3 testers orbit and move for 5 minutes, then none report nausea or losing sight of the character.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.23 Put a controllable player capsule into the greybox testbed done
Closed
Completed in PR #82 (2026-09-29)
area:gamearea:renderclass:allepic:e02milestone:m1size:mmw-e02.5Focus and interact with world objects via contextual prompts
Description
Why
The world rewards curiosity: bookshelves, levers, chests, bodies, statues must all respond to one consistent interact verb.
What
As a player, I want to look at an object and press Interact to use it so that exploring feels natural and I always know what I can do.
Objects expose interaction affordances (open, pull, pick up, read, search, hide, climb, talk…) declared as components. A focus selector in src/sim scores candidates by distance, view angle and line of sight and picks one focus target; the UI shows a contextual prompt. Affordances may be gated by class/capability or item (locked → "Pick lock" for thief, "Unlock" with key).
Scope
- In: Interactable component with affordance list, range, required capability/item predicate
- In: Deterministic focus scoring + LOS check, hysteresis to prevent flicker
- In: Interaction event dispatch to owning systems; contextual prompt model for UI (text + binding glyph)
- In: Hold-to-interact durations
- Out: Prompt visuals — see mw-e31/ui work
- Out: Specific affordances (doors, containers) — see e03-doors-locks-switches, e03-hiding-containers
Notes
Scoring: score = w_d*(1 - d/range) + w_a*cos(angle); range default 2.5 m, max angle 45°. Ties broken by entity id for determinism. Unavailable affordances are still shown greyed with the reason (e.g. "Locked — needs Iron Key") to hint at alternate routes.
Acceptance criteria
- AC-1 unit Given two interactables at 1.0 m (30° off-axis) and 2.0 m (straight ahead), when focus is computed, then the selection matches the documented scoring and is identical across runs.
- AC-2 unit Given an interactable behind a wall within range, when focus is computed, then it is not selected (LOS).
- AC-3 unit Given focus on A and a new candidate B scoring within 10% of A, when computed, then focus stays on A (hysteresis).
- AC-4 unit Given a locked door and a player without the key or lockpicking capability, when focused, then the prompt shows the affordance as unavailable with the reason and Interact dispatches nothing.
- AC-5 unit Given a hold-to-interact affordance (1.0 s), when Interact is released at 0.8 s, then the interaction is cancelled and no event fires.
- AC-6 e2e Given the greybox testbed, when the bot faces a lever and presses Interact, then the lever event fires and the prompt DOM element displays the bound key.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
Closed
Completed in PR #100 (2026-09-29)
area:simclass:allepic:e02milestone:m1size:mmw-e02.6Drive character animation from sim locomotion state and events
Description
Why
Animation must reflect the sim exactly (no sliding feet, no "floaty" jumps) without leaking renderer concerns into the sim.
What
As a developer, I want the sim to publish a locomotion state snapshot and discrete animation events so that any renderer/animation set can play matching clips.
The sim emits: locomotion state (idle/walk/run/sprint/crouch/airborne/landing/climb/mantle/swim), normalised speed, turn rate, and events (jumpStart, land{impactSpeed}, footstep{foot}, mantleStart, ledgeGrab). src/game maps these to an animation state machine on grey-box/Mixamo rigs.
Scope
- In: LocomotionSnapshot type + event stream in sim
- In: Blend-tree driver in render using speed/turn rate; root motion NOT authoritative
- In: Grey-box capsule-man placeholder rig
- Out: Combat animation layers — see e04-melee-core
- Out: Final character rigs — see e37-3d-pipeline-decision
Notes
Footstep events are produced from sim stride distance, not from animation curves, so they stay deterministic (consumed by e02-footstep-events).
Acceptance criteria
- AC-1 unit Given a player accelerating from idle to run, when snapshots are sampled, then state transitions idle→walk→run occur at documented speed thresholds (0.2, 2.5 m/s).
- AC-2 unit Given a fall of 3 m, when landing, then exactly one land event fires with impactSpeed within ±2% of the analytic value.
- AC-3 unit Given the player is pushed sideways while idle (external impulse), when sampled, then state is not "walk" unless input is present (no false locomotion).
- AC-4 e2e Given the greybox testbed, when the bot runs and jumps, then the placeholder rig plays run and jump clips (animation state probe) and there are no console errors.
- AC-5 perf Given 20 animated characters in the greybox scene, when running for 30 s, then animation update cost ≤ 1.0 ms per frame p95 on the reference machine (High).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.20 Build layered animation state machine synced to sim timelines done
Closed
Completed in PR #104 (2026-09-30)
area:gamearea:renderepic:e02milestone:m1size:mmw-e02.7Lock controller determinism with golden replay tests in CI
Description
Why
Determinism is a constitution-level technical goal; controller regressions are silent and must be caught automatically.
What
Record golden input logs on the greybox traversal course (run, jump, crouch, slopes, steps, later mantle/climb/swim) and assert final and periodic state hashes in CI. Provide a script to re-bless goldens intentionally with a diff report.
Scope
- In: 3 golden logs (basic, slopes/steps, stress with rapid direction changes)
- In: Periodic hash checkpoints every 60 ticks to localise divergence
- In: Re-bless script with human-readable diff
- Out: Replay harness itself — see e00-replay-harness
Notes
Extend goldens as traversal items land (each traversal item adds a clip).
Acceptance criteria
- AC-1 integration Given each golden log, when replayed in CI, then every checkpoint hash matches the committed golden.
- AC-2 integration Given a deliberate 1% change to runSpeed, when replayed, then the test fails and reports the first divergent checkpoint tick.
- AC-3 integration Given a log recorded at a different frame rate (30 vs 144 Hz render), when replayed, then sim hashes are identical (render rate independence).
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
Closed
Completed in PR #78 (2026-09-29)
area:qaepic:e02milestone:m1size:smw-e02.9Support gamepad input with deadzones and remappable bindings
Description
Why
Gamepad is second after KB/M (Technical constitution). Action RPG combat is commonly played on a pad.
What
As a player, I want to plug in a standard gamepad and play with sensible defaults so that I can play from the couch.
Gamepad API polling mapped into the same ActionFrame: radial deadzone, response curve, right-stick look with acceleration, default standard-mapping bindings, rebinding, hot-plug, and last-used-device prompt glyph switching.
Scope
- In: Standard mapping (Xbox/PS layouts) defaults, radial deadzone 0.15 configurable, look curve
- In: Hot-plug connect/disconnect, device-switch detection for prompt glyphs
- Out: Aim assist — see e05-bow-core / e02-lock-on
- Out: Haptics — P3 later
Notes
Browser Gamepad API differs across Chrome/Firefox/Safari; test standard mapping only.
Acceptance criteria
- AC-1 unit Given stick input magnitude 0.10, when processed with deadzone 0.15, then move vector is (0,0); at magnitude 1.0 it is length 1.0.
- AC-2 unit Given the stick at magnitude 0.575 (midway between deadzone and full), when processed, then output magnitude is 0.5 ± 0.01 (rescaled radial deadzone).
- AC-3 unit Given both KB and gamepad produce move input in one tick, when merged, then the larger-magnitude vector wins and length never exceeds 1.0.
- AC-4 integration Given a gamepad disconnects mid-sprint, when the next frame is sampled, then all gamepad-held actions release and the game pauses per settings.
- AC-5 integration Given the last input came from the gamepad, when the interact prompt renders, then it shows the gamepad glyph.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
Closed
Completed in PR #83 (2026-09-29)
area:gameclass:allepic:e02milestone:m1size:mmw-e02.10Expose stance and movement profiles for stealth noise and visibility
Description
Why
Stealth cannot be crouching + an eye icon: stance and speed must feed visibility and noise as numbers (Thief constitution).
What
As a thief, I want my stance and speed to meaningfully change how visible and loud I am so that careful movement is rewarded.
The controller publishes a MovementProfile each tick: stance (standing/crouched), gait (still/walk/run/sprint), silhouette height, and normalised noise and visibility multipliers from data. A "slow walk" modifier (hold key / light stick) gives the quietest gait.
Scope
- In: MovementProfile type and data table per stance × gait
- In: Slow-walk modifier action
- In: Armor/encumbrance multiplier hook (value supplied by inventory)
- Out: Visibility scoring — see e09-visibility-model
- Out: Noise propagation — see e09-sound-propagation
Notes
Default multipliers: stand-sprint noise 1.0, stand-run 0.6, stand-walk 0.3, crouch-walk 0.15, slow-walk 0.08, still 0.0. Related: e09 consumes; do not implement scoring here.
Integration note: Promoted to P0/m1 — visibility and noise at m1 need crouch/walk/run profiles.
Acceptance criteria
- AC-1 unit Given a crouched player moving at crouch speed, when the profile is read, then noise multiplier is 0.15 and silhouette height matches the crouch capsule.
- AC-2 unit Given sprint pressed while crouched, when resolved, then the player stands (if space allows) and profile becomes stand-sprint; if a ceiling blocks standing, sprint is ignored (edge).
- AC-3 unit Given an encumbrance multiplier 1.5 from armor, when walking, then noise multiplier is 0.45 and clamped to ≤ 1.0 overall.
- AC-4 content Given the stance profile table, when validated, then every stance × gait combination is defined.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.3 Move controller tuning values into schema-validated content data done
Closed
Completed in PR #145 (2026-10-02)
area:simclass:thiefepic:e02milestone:m1size:smw-e02.20Build layered animation state machine synced to sim timelines
Description
Why
Combat is intrinsically fun only if what the player sees lines up with what the sim decides: a parry flash on the frame the sword connects, a skeleton's windup that honestly telegraphs its attack. Every character — player, NPC, creature — needs one shared animation runtime that never becomes a second source of truth.
What
As a developer, I want one data-driven animation runtime for every rig so that the player, NPCs and creatures animate from sim state and events without animation ever deciding gameplay.
- State machine + blending: per-rig state graphs in content data (zod-validated): states, blend trees (1D/2D by sim-published parameters such as speed and turn rate), transitions with crossfade durations and conditions on sim state. Layers: base locomotion, upper-body/action (masked), additive hit-react. Humanoid and non-humanoid rigs use the same runtime with different graphs.
- Root motion policy: the sim is authoritative for position. Clips are played in place and the renderer never moves an entity's transform from animation. Root translation found in source clips is extracted as a velocity curve for tuning reference only (matches e37-animation-pipeline AC-5).
- Animation events: gameplay-relevant timing (footsteps from stride distance, hit/active frames from e04 move data, release/cast frames) is emitted by the sim. Clip markers (
footstep,hit,release,cast) are for alignment only: the runtime time-warps action clips so thehitmarker lands on the sim's first active tick, and the content validator reports drift between markers and move data. Render-side hooks (VFX/SFX flourishes) may subscribe to marker callbacks, but sim hooks subscribe only to sim events. - Time: animation follows each entity's sim local time-scale (hit-stop, slow-motion, pause). Distant characters update at a lower rate (LOD).
Scope
- In: Runtime (src/render/animation), state-graph schema (src/content), sim-parameter binding, crossfades, layers/masks, marker-to-sim alignment and drift validator, animation LOD, dev state probe for tests
- In: One humanoid and one non-humanoid grey-box placeholder rig with idle/move/attack/hit-react clips
- Out: Player locomotion parameter mapping — see e02-animation-hooks (consumes this runtime)
- Out: Clip sourcing, retargeting and manifests — see e37-animation-pipeline
- Out: Move frame data — see e04-move-data-schema
- Out: IK (foot placement, look-at) — post-MVP polish
Notes
Resolves an inconsistency across groups: e28-footsteps mentions "foot plant events from animation" while e02-animation-hooks produces footsteps from sim stride distance. Per this item the sim event is authoritative; audio may use the clip marker only to fine-align the one-shot within the same rendered frame. Engine-agnostic API; the implementation targets whatever e00-engine-decision picks. Related: creature users e04-creature-attack-hooks, e04-melee-core, e10-takedowns (paired placeholder snaps).
Acceptance criteria
- AC-1 content Given a state graph with a transition to an unknown state or a clip id missing from the clip manifest, when validated, then validation fails naming the graph file and path (edge).
- AC-2 unit Given a 0.2 s crossfade from idle to run, when the blend is sampled at 0.1 s, then weights are 0.5/0.5 ± 0.01 and weights across all layers sum to 1.0 on every sample.
- AC-3 integration Given a move whose first active tick is 12 and a clip whose
hitmarker is at 0.40 s, when played at the 60 Hz sim rate, then the clip is time-warped so the marker coincides with tick 12 ± 1 tick; a marker drifting > 1 tick from move data is reported by the content validator. - AC-4 integration Given an entity whose sim local time-scale is 0 for 4 ticks (hit-stop), when rendered, then its pose holds for those ticks and resumes with no pop (max bone rotation delta on resume ≤ 5°).
- AC-5 unit Given a clip containing root translation loaded under the in-place policy, when played for 2 s, then the entity transform equals the interpolated sim position on every frame (animation never moves the entity).
- AC-6 e2e Given the greybox testbed with one humanoid and one non-humanoid placeholder rig, when each cycles idle → move → attack → hit-react via sim state, then the animation state probe reports each state in order and no console errors occur.
- AC-7 perf Given 24 animated characters (those beyond 30 m updated at reduced rate), when running for 30 s, then animation update cost ≤ 1.5 ms per frame p95 on the reference machine (High).
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e00.19 Bootstrap the chosen renderer and physics with a boot smoke test done
- mw-e00.20 Bind fixed-step sim to rAF rendering with interpolation done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
Closed
Completed in PR #93 (2026-09-29)
area:renderclass:allepic:e02milestone:m1size:mmw-e02.21Implement CollisionWorld on Rapier for the character controller
Description
Why
mw-e02.2 moves the player through a Rapier-free CollisionWorld interface (src/sim/character/collision-world.ts) tested against a fake. The real game needs it backed by the sim-owned Rapier physics port from mw-e03.10.
What
An adapter implementing CollisionWorld (capsule shape-cast, raycast, overlap) on the deterministic Rapier world, plus contract tests shared with the fake.
Scope
- In: adapter, shared contract test suite run against fake and Rapier
- Out: controller logic (mw-e02.2), physics port and stepping (mw-e03.10)
Acceptance criteria
- AC-1 integration Given the shared CollisionWorld contract suite, when run against the Rapier adapter and the fake, then both pass.
- AC-2 integration Given the greybox traversal course and a recorded input log, when replayed twice on the Rapier adapter, then final sim state hashes are identical.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.35 Host a sim-owned deterministic Rapier world with static colliders done
Closed
Completed in PR #79 (2026-09-29)
area:gamearea:simepic:e02milestone:m1mw-e02.23Put a controllable player capsule into the greybox testbed
Description
Why
The controller (mw-e02.2), input (mw-e02.1), loop (mw-e00.20) and testbed (mw-e00.21) all exist, but nothing connects them, so there's still nothing to walk around.
What
In ?scene=testbed, spawn a player character, feed sampled ActionFrames and camera yaw into createFrameLoop and characterControllerSystem, resolve collisions against the scene's greybox colliders, and render an interpolated placeholder capsule. Use the Rapier CollisionWorld (mw-e02.21) if it has merged; otherwise, a fake CollisionWorld built from the scene's GreyboxShapes behind the same interface, swapped out later.
Scope
- In: spawn point from scene data, input to loop to controller wiring, placeholder capsule mesh, simple fixed follow camera until mw-e02.4
- Out: orbit camera (mw-e02.4), animation (mw-e02.6)
Acceptance criteria
- AC-1 e2e Given ?scene=testbed, when the Playwright bot holds W for 1 s, then the player capsule moves at least 4 m forward and there are no console errors.
- AC-2 e2e Given the testbed wall, when the bot walks into it, then the capsule stops in front of it and does not pass through.
- AC-3 e2e Given Space pressed, when grounded, then the capsule leaves the ground and lands again within 1 s.
- AC-4 integration Given the same recorded input log, when replayed through the testbed wiring twice, then the final sim hashes match.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
Closed
Completed in PR #80 (2026-09-29)
area:gamearea:simepic:e02milestone:m1mw-e02.24Step up onto steps whose edges are rounded, as on the Rapier CollisionWorld
Description
Why
On the in-memory fake, box edges are square (the capsule's Minkowski sum is sharp), so the controller's step-up landing probe hits a step's top face and sees a walkable normal. On the real Rapier CollisionWorld (mw-e02.21) edges are rounded, as the CollisionWorld contract allows ('only face contacts are pinned'). The landing probe, advanced only one tick's travel (~0.08 m at run speed) past the wall, meets the step's top edge with a normal of about 50° (y ≈ 0.64 < cos 45°), so the step is refused and the player stops dead at a 0.30 m step. Found by the greybox traversal course on Rapier (tests/integration/character-course-rapier.test.ts, currently marked it.fails).
What
Make stepUp robust to rounded edges, e.g. probe the landing at least one capsule radius past the wall (then place the feet at the real advance), or accept an edge contact whose witness lies on a walkable top face. Keep the fake and Rapier runs of the course behaving the same.
Scope
- In: controller step-up (src/sim/character/controller.ts), its tests, flipping the Rapier course test from it.fails to it, re-recording the course replays if hashes change
- Out: CollisionWorld contract changes (edges stay unpinned)
Acceptance criteria
- AC-1 unit Given a 0.30 m step and a CollisionWorld with rounded edges (Rapier), when the player runs at it, then it steps up losing no more than 10% speed (mw-e02.2 AC-4 on Rapier).
- AC-2 integration Given the greybox traversal course on the Rapier adapter, when the recorded log runs, then every course feature passes as on the fake.
Closed
Completed in PR #81 (2026-09-29)
area:gamearea:simepic:e02milestone:m1mw-e02.8Enforce input latency and controller cost budgets in the perf suite
Description
Why
"Responsive" must be a number. The contract requires numeric feel targets and perf budgets on the reference machine.
What
Add Playwright perf tests measuring input-to-motion latency (synthetic key event timestamp → first rendered frame where the player transform changed) and controller + camera CPU cost per tick; wire into e32 budgets.
Scope
- In: Latency probe hook in dev builds only
- In: Budgets: input-to-motion ≤ 100 ms p95 (target ≤ 50 ms), controller sim ≤ 0.3 ms/tick p95, camera ≤ 0.2 ms/frame p95
- Out: General frame-time budgets — see e32-perf-budgets
Notes
Latency probe must be stripped from production bundles (check bundle for the probe symbol).
Acceptance criteria
- AC-1 perf Given the greybox testbed on the reference machine (High), when 50 synthetic move key presses are measured, then input-to-motion latency is ≤ 100 ms at p95.
- AC-2 perf Given 60 s of scripted traversal, when profiled, then controller sim cost ≤ 0.3 ms per tick p95 and camera ≤ 0.2 ms per frame p95.
- AC-3 integration Given a production build, when the bundle is scanned, then the latency probe symbol is absent.
- AC-4 perf Given a simulated 30 fps render (low preset worst case), when measured, then input-to-motion latency is ≤ 133 ms p95 (edge).
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
Closed
Completed in PR #370 (2026-11-09)
area:perfepic:e02milestone:m1size:smw-e02.22Persist custom key bindings through the settings store
Description
Why
Split from mw-e02.1: AC-6 there needs the settings framework (mw-e31.1), which depends on the UI layer (mw-e00.23). Input mapping ships first with an in-memory bindings API.
What
Save and restore mw-e02.1's bindings via the mw-e31.1 settings store.
Scope
- In: bindings serialisation, migration of unknown actions, wiring to settings store
- Out: rebind UI (options menu)
Acceptance criteria
- AC-1 integration Given bindings saved via e31-settings-framework, when the page reloads, then custom bindings are restored.
- AC-2 unit Given stored bindings that reference an unknown action or key code, when loaded, then those entries are dropped and defaults fill the gap.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #218 (2026-10-04)
area:gamearea:simepic:e02milestone:m1mw-e02.26Let interaction reach ignore physics objects' own colliders in the game
Description
Why
Property-derived affordances (liftable → pick up, pushable → push) must work on physics objects, but a crate's own Rapier collider blocks the reach ray to its centre, so it could never take focus.
What
As a player, I want to focus and pick up a loose crate so that physics props are interactable like any other object.
When the game installs physics objects (mw-e03.10 wiring), pass the sim's
physicsBodiesOfas the interactionbodiesOf(setupTestbedPlayerinteraction.bodiesOf) so an object's own body and bound collider never block reach to it.Scope
- In: wiring
physicsBodiesOfin src/main.ts and the testbed replay world once physics-object components are registered there - Out: pick-up/carry behaviour itself (mw-e02.17)
Notes
physicsBodiesOflives in src/sim/interaction/system.ts (mw-e02.5); ColliderHandle and the sight world's BodyId share one sequence.Acceptance criteria
- AC-1 integration Given a liftable physics crate in the testbed in reach and view, when focus is computed, then the crate is focused (its own collider does not block reach).
- AC-2 integration Given the same crate behind a wall, when focus is computed, then it is not focused.
Closed
Completed in PR #100 (2026-09-29)
area:gamearea:simepic:e02milestone:m1size:s- In: wiring
mw-e02.31Feed the lock-on target into the knight's attack facing
Description
Why
mw-e04.6 lets the knight turn at up to 360°/s toward a lock-on target during attack startup, via PlayerMeleeOptions.target (faceTarget). Lock-on (mw-e02.16) is not merged yet, so the testbed passes no target and the knight tracks its look direction instead.
What
When lock-on lands, pass its current target to installPlayer's combat.melee.target.
Scope
- In: wiring the lock-on target into the facing rule
Notes
Follow-up from mw-e04.6.
Acceptance criteria
- AC-1 unit Given a locked-on target 90° to the side, when a light attack starts, then the knight turns 6° per startup tick toward it and is locked from the first active tick.
Closed
Completed in PR #121 (2026-09-30)
area:gamearea:simclass:knightepic:e02milestone:m1mw-e02.36Make R3 crouch a toggle with movement exits
Description
Make the default gamepad crouch action latch on the first R3/RS click and unlatch on the next click, jump, sprint, or controller disconnect. Preserve low-ceiling safety in the character controller and existing keyboard behavior. Update the controls hint/docs and automated tests.
Acceptance criteria
R3 click enters crouch without being held; a second R3 click, jump, or sprint exits; disconnect clears the latch; low ceilings still prevent standing; focused tests, typecheck, lint, and build pass.
Closed
Implemented R3/RS crouch latching in the merged input sampler; second R3 click, jump, sprint, or disconnect releases it while the controller retains low-ceiling safety. Updated HUD/docs and passed 65 focused/controller/integration tests, typecheck, lint, and build. (2026-10-03)
area:gamearea:simepic:e02milestone:m1mw-e02.32Make the room training dummy a lock-on target
Description
Why
mw-e04.6 added a combat training dummy (with Health) in the testbed room while mw-e02.16 added lock-on with three separate targetable dummies in the arena. Locking on to the one you can actually hit is the knight's core loop.
What
As a knight, I want to lock on to the room training dummy so that I can practise strafing and attack timing on it.
Scope
- In: give the
training-dummyspawn atargetableprofile, locate it by its combat transform (it may be knocked back), pass zeroHealth so its death hands the lock on. - Out: lock-on rules (src/sim/targeting).
Notes
src/game/combat (TRAINING_DUMMY, readDummyTransform); TestbedLockOn.locate in src/game/player/testbed-player.ts.
Acceptance criteria
- AC-1 integration Given the testbed, when the player presses lock on facing the room dummy, then the lock targets it and the marker sits on it.
- AC-2 integration Given a lock on the room dummy, when its health reaches 0, then the lock releases (no other target within 10 m).
Closed
Completed in PR #121 (2026-09-30)
area:gamearea:simepic:e02milestone:m1milestone:m2- In: give the
mw-e02.33Block dodges, attacks and blocks while mantling or hanging from a ledge
Description
Why
A dodge, swing or raised shield started while the player mantles or hangs (mw-e02.12) plays its action timeline (i-frames, hitboxes, stamina) while the ledge hook holds the character in place: a free i-frame exploit and a pose that makes no sense.
What
As a player, I want combat buttons to do nothing while my hands are on a ledge, so that traversal and combat never overlap.
Scope
- In: dodge input, light attack and block are refused while CharacterState.traversal is mantle or hang (and later climb)
- Out: attacks from a hang (a future thief move)
Acceptance criteria
- AC-1 unit Given a player hanging from a ledge, when dodge, attack or block is pressed, then no move starts and stamina is unchanged.
- AC-2 unit Given a player mid-mantle, when dodge is pressed, then no dodge starts; once the mantle ends a new press dodges as usual.
Closed
Completed in PR #129 (2026-10-02)
area:gamearea:simclass:allepic:e02milestone:m1mw-e02.38Place liftable and throwable objects in the slice and valley areas
Description
Why
Carry/throw (mw-e02.17, PR #301) works in the sim but no area has a liftable object, so players can't use it.
What
Mark suitable props liftable with weights (bottles, pots, small crates, barrels) in the testbed, slice rooms and valley scenes; add at least one fragile throwable near a guard for the distraction fantasy; re-record slice goldens if world state changes.
Acceptance criteria
AC-1 e2e Given the testbed, when the player picks up a bottle and throws it at a wall, then it breaks and a noise event is recorded.
Closed
Completed in PR #306 (2026-11-08)
area:gamearea:simepic:e02milestone:m1
M2 Class fantasies 8
mw-e02.11Emit footstep events tagged with surface material and gait
Description
Why
Surfaces matter to stealth (Thief constitution) and footsteps are a key audio feedback channel (E28).
What
As a thief, I want gravel to be louder than carpet and wet stone to sound different so that I choose my path deliberately.
The sim emits footstep events at stride intervals carrying position, foot, surface material (from world properties of the ground collider), wetness/frozen state, gait and loudness = profile noise × material loudness.
Scope
- In: Stride-distance based footstep cadence per gait
- In: Ground material lookup via e03-world-properties (including wet, frozen, burning states)
- In: Event consumed by audio and stealth (via event bus)
- Out: Sound propagation/perception — see e09-sound-propagation
- Out: Footstep SFX assets — see mw-e38
Notes
Material loudness examples (data): carpet 0.3, wood 0.7, stone 0.8, gravel 1.2, shallow water 1.3, ice 0.9. Loudness is data, not code.
Acceptance criteria
- AC-1 unit Given a player walking 10 m at walk speed, when footsteps are counted, then the count equals floor(10 / strideLength) ± 1.
- AC-2 unit Given the ground collider has material gravel, when a step lands, then event.material = gravel and loudness = profileNoise × 1.2.
- AC-3 unit Given a stone floor with wet=0.8, when stepping, then the event carries wet=true and loudness uses the wet-stone modifier.
- AC-4 unit Given the player is airborne or standing still, when ticks elapse, then no footstep events are emitted (edge).
Waits on
- mw-e02.10 Expose stance and movement profiles for stealth noise and visibility done
- mw-e02.6 Drive character animation from sim locomotion state and events done
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #354 (2026-11-09)
area:simclass:allepic:e02milestone:m2size:smw-e02.12Mantle onto and grab ledges detected from world geometry
Description
Why
Classes change possibilities: "the thief might climb through a window". Verticality is also a stealth factor. Every class needs baseline mantling; thieves go further.
What
As a player, I want to jump at a ledge and pull myself up so that I can reach windows, crates and balconies.
Ledge detection uses forward + down shape casts against surfaces with the ledge/climbable markup, validates headroom and standing space, and performs: auto-mantle (≤ 1.0 m), mantle (≤ 1.6 m, all classes), ledge grab/hang with shimmy and pull-up (≤ 2.2 m reach, capability-gated), and drop-down to hang from an edge.
Scope
- In: Ledge query + validation (headroom, surface angle ≤ 20°, clearance)
- In: Mantle, ledge hang, shimmy, pull-up, drop, jump-back states
- In: Capability gates (e.g. ledge hang may require climbing capability per class data)
- Out: Free climbing walls — see e02-climb-surfaces
- Out: Surface markup authoring — see e03-climbable-surfaces
Notes
Mantle is deterministic and uses sim-driven motion curves, not root motion. Heavy encumbrance may block pull-up (data).
Acceptance criteria
- AC-1 unit Given a 1.4 m crate edge ahead and jump pressed while moving forward, when resolved, then the player mantles onto the top in 0.6 s ± 0.05 and ends grounded on it.
- AC-2 unit Given a ledge with 1.0 m headroom above it, when mantle is attempted, then it is rejected unless crouched mantle fits (capsule check) (edge).
- AC-3 unit Given a player hanging from a ledge, when moving sideways, then they shimmy at 1.0 m/s and stop at ledge ends or gaps > 0.3 m.
- AC-4 unit Given a class without ledge-hang capability, when jumping at a 2.0 m ledge, then no grab occurs and the player falls normally.
- AC-5 unit Given a ledge surface that becomes frozen or burning (via e03 properties), when hanging, then the player is forced to drop after the configured grace time (1.0 s).
- AC-6 integration Given the greybox traversal course, when the golden mantle replay runs, then state hashes match.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.22 Mark climbable surfaces and ledges from properties and geometry done
Closed
Completed in PR #123 (2026-09-30)
area:simclass:allepic:e02milestone:m2size:mmw-e02.13Climb ladders, ivy, ropes and rough walls marked climbable
Description
Why
Climbing is a core thief verb and an alternate-route generator; burning the ivy or freezing the wall should change it (systemic).
What
As a thief, I want to climb any surface the world marks as climbable so that I can find routes others cannot.
Climb state attaches the player to a climbable surface (ladder, rope, ivy, rough stone), moves in surface space, transitions to ledge mantle at the top, and detaches on jump-off. Climbability comes only from world properties (climbable grade + capability requirement), so fire removing ivy or ice making walls slippery works automatically.
Scope
- In: Climb attach/detach, surface-space movement (0.8 m/s default), corner transitions ≤ 45°
- In: Ropes (vertical, swaying anchor) and ladders (all classes), grade-gated walls (thief capability)
- In: Stamina hook (value from progression) and fall on stamina exhaustion
- Out: Climbing claws/tools — see mw-e10
- Out: Rope arrows — see mw-e05 (they spawn a climbable rope entity)
Notes
Rope arrows, vines spell, and a knight propping a ladder all work by spawning entities with the climbable property — no special cases.
Acceptance criteria
- AC-1 unit Given a surface with climbable=ladder, when any class presses toward it, then they attach and climb at 1.2 m/s.
- AC-2 unit Given a surface with climbable=rough and requiresCapability=climb.rough, when a class without it approaches, then no attach occurs; with it, attach succeeds.
- AC-3 unit Given a climbing player whose ivy surface loses the climbable property (e.g. burned away), when the next tick runs, then the player detaches and falls (edge).
- AC-4 unit Given a runtime-spawned rope entity with climbable=rope, when approached, then the player can climb it exactly like an authored rope.
- AC-5 unit Given stamina reaches 0 while climbing, when the tick runs, then the player slips and falls.
- AC-6 e2e Given the greybox climbing wall, when the bot climbs to the top and mantles, then it reaches the upper platform with no console errors.
Waits on
- mw-e02.12 Mantle onto and grab ledges detected from world geometry done
- mw-e03.22 Mark climbable surfaces and ledges from properties and geometry done
Closed
Completed in PR #129 (2026-10-02)
area:simclass:thiefepic:e02milestone:m2size:mmw-e02.14Wade and swim in water and slide on frozen surfaces
Description
Why
"Magic can freeze water … open traversal opportunities." Freezing a lake to walk across it must emerge from properties, not a scripted bridge.
What
As a sorcerer, I want to freeze a river and walk across it so that magic opens new routes.
Water volumes (from e03) drive wading (depth 0.5–1.2 m, speed reduction), swimming (> 1.2 m; no weapons, limited breath), and diving. Frozen water becomes a walkable solid with low friction (ice sliding, reduced accel). Melting ice drops the player back into water.
Scope
- In: Wade/swim/dive states, breath meter (20 s default), drowning damage hook
- In: Low-friction movement on any surface with frozen state or friction < 0.2
- In: Transitions when water freezes/melts under the player
- Out: Water/freezing simulation — see e03-water-wetness, e03-freeze-melt
- Out: Drowning damage numbers — see e04-damage-model
Notes
Swimming makes the player wet (sets wet property on the actor), which matters for fire and electricity.
Acceptance criteria
- AC-1 unit Given water depth 0.9 m, when moving, then the player wades at 60% run speed and footstep events report material water.
- AC-2 unit Given depth 1.5 m, when entered, then the player swims, breath depletes over 20 s underwater, and damage events fire each 1 s after depletion.
- AC-3 unit Given a water volume transitions to frozen while the player swims on the surface, when resolved, then the player is placed on top of the ice rather than embedded in it (edge).
- AC-4 unit Given a frozen surface, when the player stops input at run speed, then they slide ≥ 2.0 m before stopping (vs ≤ 0.5 m on stone).
- AC-5 unit Given the player stands on ice that melts, when the tick runs, then the player drops into the water volume and state becomes wading/swimming.
- AC-6 unit Given the player exits a water volume, when checked, then the actor wet property is set to 1.0 and decays per e03 drying rules.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
Closed
Completed in PR #246 (AC-3/4/5 ice parts: mw-ju8.30; swim clips mw-ju8.31; mantle/drop-armour mw-ju8.32) (2026-10-05)
area:simclass:allepic:e02milestone:m2size:mmw-e02.15Apply knockback, launches and fall damage to the player controller
Description
Why
Gust, Thunderclap, explosions, trolls and collapsing floors must move the player through one impulse API; failure (being blasted off a ledge) creates stories.
What
As a player, I want explosions and heavy blows to physically throw me so that the world feels powerful and dangerous.
The controller accepts impulses from any source (force stimulus from e03, attacks from e04), transitions to a ragdoll-free "launched" airborne state with reduced air control, and computes fall damage from impact speed via data thresholds. Self-cast impulses (a sorcerer using Gust on themselves) are allowed — "too useful" is permitted.
Scope
- In: Impulse API (vector, source, stagger flag), launched state, recovery
- In: Fall damage curve (safe ≤ 8 m/s impact, lethal ≥ 20 m/s) as data
- In: Landing into water or soft materials (hay, snow) reduces damage via material property
- Out: Stagger/poise — see e04-damage-model
Notes
Soft landing uses a material property (impactAbsorb) so hay carts and snowdrifts work automatically.
Acceptance criteria
- AC-1 unit Given an impulse of 10 m/s horizontal + 5 m/s up, when applied to a grounded player, then the player enters launched state and follows the ballistic arc within 1% error.
- AC-2 unit Given an impact speed of 7 m/s, when landing, then no damage event fires; at 14 m/s a damage event matching the data curve fires.
- AC-3 unit Given a landing surface with impactAbsorb=0.8 (hay), when landing at 14 m/s, then damage is reduced by 80%.
- AC-4 unit Given two impulses in the same tick, when resolved, then they sum and the result is order-independent (deterministic).
- AC-5 unit Given a launched player hits a wall mid-flight, when resolved, then horizontal velocity is removed and wall-impact damage uses the same curve (edge).
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
Closed
Completed in PR #107 (2026-09-30)
area:simclass:allepic:e02milestone:m2size:smw-e02.16Lock on to and cycle between targets with camera framing
Description
Why
Knight and archer combat need reliable targeting; poor lock-on makes intrinsically fun combat frustrating.
What
As a knight, I want to lock onto an enemy and circle-strafe around it so that I can focus on timing and positioning.
Target selection in sim (targetable component, distance ≤ 20 m, within 60° of camera forward, LOS), cycling left/right by screen angle, strafing movement relative to target, lock break on distance > 25 m or LOS lost for > 1.0 s, and camera framing of player + target.
Scope
- In: Targetable component with lock point(s) and priority
- In: Selection, cycle, break rules; strafe mode in controller
- In: Camera framing lerp that keeps both in view
- Out: Weak-point targeting on bows — see e05-bow-core
- Out: Soft aim assist for spells — see e06-spell-pipeline
Notes
Targetables can be creatures, but also shootable mechanisms and interactables flagged lockable (lets archers lock onto a distant bell).
Acceptance criteria
- AC-1 unit Given three targetables within the cone, when lock is pressed, then the one nearest the camera forward axis (weighted by distance) is selected, deterministically.
- AC-2 unit Given a lock on target A, when cycle-right is pressed, then the next target clockwise in screen angle is selected; with no other targets, lock stays on A.
- AC-3 unit Given a locked target moves behind a wall, when 1.0 s elapses without LOS, then the lock breaks; if LOS returns at 0.8 s, the lock persists (edge).
- AC-4 unit Given a locked target dies, when resolved, then the lock auto-switches to the next valid target within 10 m or releases.
- AC-5 unit Given a lock-on, when move=(1,0), then the player strafes around the target maintaining distance within 5% per second.
- AC-6 e2e Given the greybox arena with 3 dummies, when the bot locks and cycles, then the HUD lock marker moves across all three and no console errors occur.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
Closed
Completed in PR #102 (2026-09-30)
area:gamearea:simclass:allepic:e02milestone:m2size:mmw-e02.17Pick up, carry, drop and throw liftable objects
Description
Why
Throwing a bottle to distract a guard, stacking crates to reach a window, or carrying a body out of sight are core systemic verbs.
What
As a thief, I want to pick up a bottle and throw it so that the noise draws the guard away.
Any entity with liftable and weight ≤ the actor carry limit can be picked up via interaction, held in front (collides), dropped, placed precisely, or thrown with a charge (0.2–1.0 s → 4–14 m/s). Thrown objects are physics objects: they deal impact via e03, break if fragile, and emit noise events. Bodies (unconscious/dead actors) are carriable with slow movement.
Scope
- In: Carry limit per class (data), carry pose, drop/place/throw
- In: Throw arc preview line (UI hint)
- In: Carrying bodies (heavy carry: 50% speed, no jump)
- Out: Telekinesis lifting — see e03-liftable-levitation (magic uses the same object side)
- Out: Distraction AI response — see mw-e11
Notes
A thrown burning object remains burning; a thrown oil flask shatters and spills oil (fragile + contents). No special cases — all via properties.
Acceptance criteria
- AC-1 unit Given a liftable object weighing 8 kg and a carry limit of 25 kg, when Interact is used, then the object is held; given 40 kg, then the affordance is unavailable with reason "Too heavy".
- AC-2 unit Given a held object and throw charged for 1.0 s, when released, then it leaves at 14 m/s ± 0.1 along the aim direction.
- AC-3 unit Given a thrown fragile object, when it impacts above its break threshold, then it breaks via e03-breakables and a noise event with the break loudness is emitted.
- AC-4 unit Given a held object is set on fire (burning property), when still held after 2.0 s, then the actor takes heat exposure and auto-drops it (edge).
- AC-5 unit Given a held object would clip into a wall, when carried, then it is pushed back and never penetrates geometry by > 2 cm.
- AC-6 unit Given the player carries a body, when moving, then speed is 50% and jump is disabled.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e17.7 Pick up, drop and throw items as physical world objects done
Closed
Completed in PR #301 (2026-11-08)
area:simclass:allepic:e02milestone:m2size:mmw-e02.18Push and pull heavy objects such as crates and statues
Description
Why
Manipulating statues and moving objects are explicit puzzle examples (Puzzle constitution); knights should shove what others can only drag.
What
As a player, I want to grab a heavy crate and push or pull it so that I can reach high ledges or weigh down pressure plates.
Objects with pushable=true enter a grab state; movement speed scales with weight vs. actor strength (data), restricted to the object axes if the object declares them (grid statues, rails). Pushing across frozen surfaces is easier (friction from properties).
Scope
- In: Grab/release, push/pull speed curve by weight and surface friction
- In: Axis/rail-constrained pushables and grid-snapped pushables (for puzzles)
- In: Rotate-in-place affordance for rotatable statues/mirrors
- Out: Puzzle-specific prefabs — see e15-prefab-pieces
Notes
Speed = base × clamp(strength / (weight × friction), 0, 1); knight strength higher. Friction from world properties (ice ~0.05).
Acceptance criteria
- AC-1 unit Given a 120 kg crate on stone and a strength profile of 150, when pushed, then movement speed equals the documented formula within 1%.
- AC-2 unit Given the same crate on a frozen surface, when pushed, then speed is higher than on stone and the crate continues sliding 1.0 m ± 0.2 after release.
- AC-3 unit Given a grid-snapped statue, when a push is released mid-cell, then it completes to the next cell centre.
- AC-4 unit Given a pushable blocked by a wall or another object, when pushed, then neither the player nor the object moves and no penetration occurs (edge).
- AC-5 unit Given a rotatable mirror, when rotate is used, then it turns by its configured step (15°) and emits a rotation-changed event.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
Closed
Completed in PR #313 (2026-11-08)
area:simclass:allepic:e02milestone:m2size:s
E03Systemic Interaction EngineDoors, switches, movable objects, breakables, fire, water, ice, electricity, gas, light, physics and triggers form one reusable, property-based world simulation. Nothing is scripted pairwise: objects declare properties (flammable, wet, frozen, conductive, weight, fragile, climbable, liftable, hideable, reflective…) and elements propagate by rules over those properties, so a spell, an arrow, a torch, a creature and a puzzle all touch the same systems. This is the architectural heart of "Wait… I can do that?".37/58 done · 64%
M1 Grey-box slice 21
mw-e03.1Define typed world-property components for systemic interactions
Description
Why
The single most important architectural principle: interactions come from properties (flammable=true), never "Fireball + Rope" scripts. Every system in the game reads and writes these.
What
As a designer, I want to give any object a set of physical/elemental properties so that it automatically reacts to fire, water, cold, electricity, force and light.
A typed property component set in src/sim with schema in src/content: material, temperature (°C), ignitionPoint, fuel (burn seconds), burning, wetness (0..1), frozen, freezePoint, conductive, charge, weight (kg), fragile (breakThreshold J), hp/structural, buoyancy/density, climbable (grade), liftable, pushable, hideable, reflective, transparent, opaque, lightEmitter, soundDamping, friction, impactAbsorb, ownership tag. Derived queries (isFlammableNow = flammable ∧ ¬wet>0.5 ∧ ¬frozen).
Scope
- In: Property component definitions + zod schemas + defaults
- In: Query helpers and change events (propertyChanged{entity, key, old, new})
- In: Deterministic ordered iteration over entities with a property
- Out: Material defaults — see e03-material-presets
- Out: Propagation behaviour — see e03-element-propagation and siblings
Notes
Keep the property set closed and versioned (adding a property is a schema bump tracked by e30 migrations). A CI guard (see epic exit criteria) forbids type-pair checks in interaction rules. Actors (player, creatures) use the same components — see e03-actor-properties.
Acceptance criteria
- AC-1 unit Given an entity with flammable, wetness=0.8, when isFlammableNow is queried, then it returns false; at wetness=0.2 it returns true.
- AC-2 unit Given a property write that changes a value, when committed, then exactly one propertyChanged event fires with old/new values; writing the same value fires none.
- AC-3 content Given an entity data file with an unknown property key or out-of-range value (wetness 1.4), when validated, then validation fails naming the path.
- AC-4 unit Given 1,000 entities with the flammable property created in random order, when iterated, then iteration order is by entity id and stable across runs.
- AC-5 unit Given a frozen=true entity with temperature above freezePoint, when invariants are checked, then the validator reports an inconsistent state (edge).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
Closed
Completed in PR #41 (2026-09-28)
area:contentarea:simclass:allepic:e03milestone:m1size:mmw-e03.2Provide data-defined material presets for common world objects
Description
Why
Fast content creation: a designer should mark a crate "wood" and get sensible flammability, weight density, sound and fragility for free.
What
As a designer, I want to pick a material preset (wood, dry-wood, stone, iron, copper, cloth, rope, paper, oil, water, ice, glass, straw/hay, flesh, bone, ivy, earth) so that objects get consistent properties with per-object overrides.
Scope
- In: content/materials/*.json presets with property defaults + footstep loudness + impact sound id
- In: Override merge rules (object value > preset > global default)
- In: Material docs table generated from data
- Out: Visual materials/shaders — see mw-e37 / mw-e29
Notes
Example values: wood ignition 300°C, fuel 60 s/kg-ish scalar; iron conductive, non-flammable; oil ignition 210°C, spreads as liquid; glass fragile threshold low; straw impactAbsorb 0.8, very flammable.
Acceptance criteria
- AC-1 content Given every material preset, when validated, then all fields pass schema and physically inconsistent combos (flammable + ignitionPoint missing) are rejected.
- AC-2 unit Given an object with material=wood and override weight=3, when resolved, then weight is 3 and all other properties come from the wood preset.
- AC-3 unit Given an object referencing an unknown material id, when loaded, then load fails with the id and file path (edge).
- AC-4 content Given the presets, when the docs generator runs, then docs/design/materials.md lists every material and property value.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #46 (2026-09-28)
area:contentepic:e03milestone:m1size:smw-e03.3Inject heat, cold, water, charge, force and gas via one stimulus API
Description
Why
Spells, arrows, torches, lightning, creatures and traps must all act on the world through the same door, or the combinatorial workload kills the project.
What
As a developer, I want a single applyStimulus(shape, element, intensity, duration, source) API so that any ability produces world effects without knowing what it hits.
Stimulus shapes: point, sphere, cone, capsule/beam, box, contact (on hit). Elements: heat, cold, water, charge, force (impulse), gas (type), light, blunt/slash/pierce (for breakables). Each stimulus is recorded with source for attribution (quests, crime, telemetry). Stimuli resolve in a fixed phase of the tick.
Scope
- In: Stimulus types + shapes, ordering rules, attribution
- In: Overlap resolution against entities with properties and against the element field
- In: Stimulus log available to debug overlay and tests
- Out: Specific spells/arrows — see e06-spell-pipeline, e05-bow-core
- Out: Propagation after injection — see e03-element-propagation
Notes
A Fire Arrow = contact(pierce) + sphere(heat, r=0.5). A torch swing = capsule(heat, low). Thunderclap = sphere(force) + sphere(blunt). Designers compose these in data.
Acceptance criteria
- AC-1 unit Given a sphere heat stimulus of radius 2 m, when applied, then every entity with temperature within 2 m receives heat scaled by the documented falloff and entities outside receive none.
- AC-2 unit Given two stimuli applied in the same tick in either order, when resolved, then resulting state is identical (order-independent via sorted resolution).
- AC-3 unit Given a force stimulus on a pushable 10 kg object, when applied, then an impulse proportional to intensity/weight is delivered to the physics object.
- AC-4 unit Given a stimulus with a source entity, when it causes a propertyChanged, then the event carries the source id (attribution).
- AC-5 unit Given a stimulus with intensity 0 or a zero-radius shape, when applied, then it is a no-op and does not throw (edge).
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #49 (2026-09-28)
area:simclass:allepic:e03milestone:m1size:mmw-e03.4Build a sparse spatial grid for temperature, moisture, gas and charge
Description
Why
Fire spreading across a floor, gas filling a room, or water pooling need a medium between objects; per-object rules alone cannot express "the room is full of gas".
What
A sparse, chunked voxel field (default 0.5 m cells, 8×8×8 chunks) storing temperature, moisture, gas concentration per gas type, and charge. Supports diffusion steps with per-cell conductivity derived from occupying materials, walls blocking flow, sleeping chunks when stable, and entity↔cell exchange.
Scope
- In: Chunked sparse storage, activation/sleep, fixed-point or deterministic float math
- In: Diffusion kernel with blockers from static geometry
- In: Entity↔cell coupling interface used by propagation rules
- Out: Element-specific rules — see e03-element-propagation, e03-water-wetness, e03-gas-clouds, e03-electricity
Notes
Use integer/fixed-point for cross-browser determinism. Budget target: 2,000 active cells ≤ 2.0 ms/tick (verified by e03-sim-perf-budget).
Acceptance criteria
- AC-1 unit Given a single hot cell in an open field, when diffusion runs for N ticks, then total heat is conserved within 0.1% (minus configured ambient loss).
- AC-2 unit Given a wall between two cells, when diffusion runs, then no quantity crosses the wall.
- AC-3 unit Given a chunk whose values are within epsilon of ambient for 60 ticks, when checked, then it sleeps and costs zero update time until touched.
- AC-4 unit Given the same initial field on two runs, when stepped 1,000 ticks, then field hashes are identical.
- AC-5 unit Given a stimulus far outside any existing chunk (1 km away), when applied, then a new chunk is allocated without affecting others and memory stays bounded by the chunk cap (edge).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #58 (2026-09-28)
area:simepic:e03milestone:m1size:mmw-e03.5Ignite, burn, spread and extinguish fire from heat and properties
Description
Why
Fire is the canonical systemic element: burning ropes, oil slicks, torched webs, scared creatures. "If ropes have flammable=true and fire spells produce heat, you have built the foundation."
What
As a player, I want anything flammable to catch fire when hot enough and fire to spread to nearby flammables so that I can improvise with torches and spells.
Heat/fire rules over properties and the element field: entities heat up from stimuli and neighbouring cells; at ignitionPoint and not wet/frozen they ignite; burning entities consume fuel, emit heat into the field, emit light (via e03-light-field) and smoke gas, dry out neighbours, and reduce structural hp; at fuel 0 they become burnt (material → charred) or are destroyed. Water/cold/smothering extinguish. This item also establishes the propagation rule framework (rule registry, phases) that other elements plug into.
Scope
- In: Rule framework: ordered rule phases, each rule reads/writes only properties + field
- In: Heating, ignition, burning, fuel consumption, burnout/destruction, spread via field
- In: Extinguish by wetness/cold/smother; wet objects dry by heat before igniting
- Out: Water, ice, electricity, gas specifics — see sibling e03 items
- Out: Fire visuals/audio — see e03-state-feedback-bridge
Notes
Everything here is expressed as property rules. A lint rule forbids referencing entity archetype ids in src/sim/elements/**. Emergent examples to cover in e03-emergence-scenarios: oil puddle ignition chain, burning a rope support, torching a spider web wall.
Acceptance criteria
- AC-1 unit Given any entity with flammable, wetness < 0.5 and not frozen, when its temperature reaches ignitionPoint, then burning=true on that tick.
- AC-2 unit Given a burning entity and a flammable neighbour within 1 cell, when ticks elapse, then the neighbour ignites within the time predicted by field heat transfer (± 1 tick) and a non-flammable neighbour never ignites.
- AC-3 unit Given a burning entity with fuel F, when it burns uninterrupted, then it burns out after F seconds and transitions to its burnt state (material charred or destroyed per data).
- AC-4 unit Given a wet flammable entity (wetness 1.0) exposed to heat, when heated, then wetness decreases first and it ignites only after wetness < 0.5.
- AC-5 unit Given a burning entity receives a water stimulus making wetness ≥ 0.5, when resolved, then burning=false and a steam gas puff is emitted into the field.
- AC-6 unit Given a fire in a closed room with limited fuel, when simulated 10 minutes, then the fire terminates (no infinite burn loop) and the field returns to ambient (edge).
- AC-7 integration Given a greybox scene with a line of 10 wooden crates, when the first is ignited, then all 10 burn in order and a replay reproduces identical state hashes.
Waits on
- mw-e03.2 Provide data-defined material presets for common world objects done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e03.4 Build a sparse spatial grid for temperature, moisture, gas and charge done
Closed
Completed in PR #68 (2026-09-28)
area:simclass:allepic:e03milestone:m1size:lmw-e03.10Simulate movable, throwable and stackable physics objects in sim
Description
Why
Crates, barrels, bottles, statues and debris are the raw material of systemic play and puzzles; they must behave consistently and deterministically.
What
As a player, I want objects to tumble, stack and roll believably so that I can build, knock over and improvise.
A physics-object layer binding e03 properties (weight → mass, friction, restitution, buoyancy) to rigid bodies stepped inside the sim fixed timestep, with sleeping, impact events (impulse, relative speed, materials), and sim-side authority (renderer interpolates only). Impact events feed breakables, noise and damage.
Scope
- In: Rigid body lifecycle from properties; fixed-step integration inside sim tick
- In: Impact events with energy (J) and material pair for sound/noise
- In: Stacking stability, sleeping, max-body budget, render interpolation contract
- Out: Breaking — see e03-breakables
- Out: Player carry/push — see e02-carry-throw, e02-push-pull
- Out: Levitation — see e03-liftable-levitation
Notes
Physics engine per e00-engine-decision (Rapier WASM with enhanced determinism expected). Physics is wrapped behind an interface so unit tests can run with a deterministic reference integrator.
Acceptance criteria
- AC-1 unit Given a 10 kg crate dropped from 2 m onto stone, when simulated, then it comes to rest within 2 s and emits one impact event with energy 196 J ± 5%.
- AC-2 unit Given a stack of 5 crates, when simulated for 10 s with no disturbance, then no crate moves more than 1 cm (stable stacking) and all bodies sleep.
- AC-3 unit Given a weight property change on a body (e.g. soaked cloth heavier), when committed, then the rigid body mass updates on the next tick.
- AC-4 integration Given the same scene and inputs, when simulated 3,000 ticks on two runs, then physics state hashes are identical.
- AC-5 unit Given the active body count exceeds the budget (default 300), when a new body wakes, then the oldest distant resting bodies are forced to sleep and a budget warning is logged (edge).
- AC-6 perf Given 200 active bodies in the greybox scene, when simulated, then physics step ≤ 2.0 ms per tick p95 on the reference machine.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.35 Host a sim-owned deterministic Rapier world with static colliders done
Closed
Completed in PR #94 (2026-09-29)
area:simclass:allepic:e03milestone:m1size:lmw-e03.11Break fragile objects and smash weakened walls into debris
Description
Why
"The knight might discover that an old wall can simply be smashed apart." Breaking must come from damage type vs. structural properties, so arrows, spells and thrown barrels break things too.
What
As a knight, I want to smash cracked walls, barricades and pottery so that brute force opens new routes and reveals secrets.
Breakable component: structural hp, resistances per damage type (blunt, slash, pierce, force, fire, cold-shatter), fragile impact threshold, break result (debris pieces, contents spilled, nav/collision change, revealed passage). Weak walls are visually telegraphed (crack decal hook) and marked with a discoverable hint property.
Scope
- In: Damage accumulation per type, impact-energy breaks for fragile objects, frozen-shatter multiplier
- In: Break results: spawn debris bodies (budgeted), drop contents, remove collider, update nav hooks
- In: Break events with source attribution and noise loudness
- Out: Weapon damage numbers — see e04-damage-model
- Out: Persisting broken state — see e27-entity-deltas
Notes
Barricades of wood: slash-weak and flammable (burn through them). Old walls: blunt/force-weak only — so knight, Thunderclap, a rolling boulder or an explosive gas pocket all work. No per-class checks.
Integration note: Promoted to P0/m1 — e27-entity-deltas persists broken state; a breakable crate is part of the grey-box slice.
Acceptance criteria
- AC-1 unit Given a breakable with blunt resistance 0.2 and hp 100, when it receives blunt damage totalling 125, then it breaks; the same slash damage with resistance 0.9 does not break it.
- AC-2 unit Given a fragile object, when an impact event exceeds its break threshold (from any source: thrown, fallen, hit), then it breaks and spills its contents.
- AC-3 unit Given a frozen breakable, when struck, then its effective threshold is divided by the frozen fragility multiplier.
- AC-4 unit Given a break, when resolved, then the collider is removed, a revealed-passage event fires if configured, and a noise event with the material break loudness is emitted.
- AC-5 unit Given many breaks in one tick exceeding the debris budget, when resolved, then extra debris is culled deterministically (oldest first) and breaks still complete (edge).
- AC-6 e2e Given the greybox weak-wall room, when the bot uses a heavy attack placeholder on the wall, then a passage opens and the bot walks through.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
Closed
Completed in PR #138 (2026-10-02)
area:simclass:knightepic:e03milestone:m1size:mmw-e03.15Query light level at any position from all light sources in sim
Description
Why
Stealth needs light and darkness as numbers; puzzles need "navigating darkness" and light receivers. The renderer's lighting is not queryable or deterministic, so the sim needs its own light model.
What
As a thief, I want shadows to genuinely hide me so that extinguishing a torch is a meaningful choice.
A sim-side light model: light emitters (point/spot/directional/ambient zones) with intensity, radius, falloff, cone; baked static occlusion (per-emitter visibility probes or cell occlusion) plus dynamic occluders (doors, large objects). lightLevel(pos) returns 0..1 with per-emitter contributions; fires and spells register emitters automatically. Includes a render-parity check.
Scope
- In: Emitter component, ambient zones (moonlight, indoor dark), sampling API with contributions
- In: Occlusion: static bake from greybox geometry + dynamic door/large-object occluders
- In: Burning entities and light stimuli auto-register emitters
- Out: Visibility scoring — see e09-visibility-model
- Out: Light sources gameplay (extinguish/ignite) — see e03-light-sources
- Out: Beams/mirrors — see e03-light-beams
Notes
Cache sampling in a coarse light grid (1 m) with dirty-region invalidation when emitters change. Render parity: sample positions compared against renderer luminance in Playwright to keep sim and visuals roughly aligned.
Integration note: Promoted to P0/m1 — e09-visibility-model (m1 AI fight) reads light level from it.
Acceptance criteria
- AC-1 unit Given a point light of intensity 1.0 and radius 8 m, when sampled at 0, 4 and 8 m with no occluders, then values follow the documented falloff (1.0, 0.25±0.01, 0.0).
- AC-2 unit Given a wall between emitter and sample point, when sampled, then that emitter contributes 0.
- AC-3 unit Given a door that closes between an emitter and a room, when the door state changes, then samples in the room update on the next tick.
- AC-4 unit Given an entity starts burning, when sampled nearby, then its light contribution appears without any manual registration; when it stops burning it disappears.
- AC-5 unit Given a sample position outside all baked regions, when queried, then the ambient zone default is returned rather than an error (edge).
- AC-6 perf Given 64 dynamic emitters and 500 samples per tick, when running, then light sampling ≤ 0.5 ms per tick p95 on the reference machine.
- AC-7 e2e Given the greybox lighting room, when the Playwright parity probe samples 20 points, then sim light level and rendered luminance rank-correlate ≥ 0.8.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
Closed
Completed in PR #89 (2026-09-29)
area:simclass:thiefepic:e03milestone:m1size:mmw-e03.18Build doors, locks, levers and buttons as data-wired mechanisms
Description
Why
The grey-box vertical slice needs room → corridor progression; the locked-tower example needs locks that thieves pick, knights break, sorcerers bypass and keys open.
What
As a player, I want doors that open, lock, jam, break and burn so that there is always more than one way through.
Mechanism components: door (hinged/sliding/portcullis/trapdoor, open/closed/locked/jammed/broken, speed, blocks light/gas/sound when closed), lock (tier, key id, pickable tier, magical seal flag), lever/button/crank/wheel (toggle, momentary, multi-position). All are signal emitters/receivers wired via e03-triggers. Doors are also breakables/flammables via properties (a wooden door burns; an iron door does not).
Scope
- In: Door, lock, lever, button, crank components and state machines
- In: Lock resolution: key in inventory, lockpick capability hook, break, magical unlock hook
- In: Door as field/light/sound blocker; blocked-by-object handling
- In: Greybox prefabs for each
- Out: Lockpicking minigame — see mw-e10
- Out: Pressure plates — see e03-pressure-plates
- Out: Shootable targets — see e03-shootable-mechanisms
Notes
Key check reads e17-inventory-core. Doors opening/closing emit noise events and update light/gas occlusion. Portcullis falling onto a crate is stopped (object weight vs. crush force) — a creative way to wedge it open.
Acceptance criteria
- AC-1 unit Given a locked door and a player holding the matching key id, when Unlock is used, then state becomes unlocked and an unlocked event with the player as source fires.
- AC-2 unit Given a hinged door opening into a crate, when the door opens, then it stops at the obstruction without penetrating and emits blocked; removing the crate lets it finish.
- AC-3 unit Given a lever wired to a portcullis, when the lever toggles, then the portcullis receives the signal and moves at its configured speed; toggling mid-motion reverses it.
- AC-4 unit Given a wooden door (flammable) that burns out, when resolved, then the door becomes broken/open and no longer blocks light, gas or sound.
- AC-5 unit Given a portcullis closing onto an object with crush resistance above the portcullis force, when resolved, then the portcullis stops above the object (wedged) (edge).
- AC-6 unit Given a frozen lever, when interacted with, then it does not move and emits jammed until thawed.
- AC-7 e2e Given the greybox slice room, when the bot pulls a lever then walks through the opened door, then it reaches the corridor with no console errors.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #152 (2026-10-03)
area:simclass:allepic:e03milestone:m1size:lmw-e03.21Wire trigger volumes and mechanisms with data-defined signal graphs
Description
Why
Designers must connect levers to doors and volumes to events without writing code (fast content creation, reusable interaction systems).
What
As a designer, I want to connect a trigger volume or lever to any receiver in data so that I can build encounters and mechanisms without scripts.
Signal graph: emitters (trigger volumes with filters — actor tag, property predicate, weight, element presence; levers; plates; receivers) → wires → receivers (doors, lights, spawners, audio cues, world-fact setters). Logic nodes: AND, OR, NOT, XOR, timer/delay, pulse, latch, counter, sequence (ordered inputs), random (seeded). Evaluated deterministically once per tick in topological order with cycle detection.
Scope
- In: Trigger volume component with enter/exit/stay and property-predicate filters
- In: Signal graph schema (zod), runtime evaluation, logic node library
- In: Validator: missing targets, cycles without delay, unused outputs
- Out: Puzzle goal evaluation — see e15-puzzle-kit
- Out: World-fact persistence — see e27-world-state-store (receiver provided there)
Notes
Filters are property predicates ("any entity with burning=true enters"), never type ids. Cycles are allowed only through delay/latch nodes to avoid same-tick oscillation.
Acceptance criteria
- AC-1 unit Given a trigger volume filtered to entities with burning=true, when a burning object enters, then enter fires; a non-burning player entering does not fire it.
- AC-2 unit Given an AND node with two lever inputs, when only one lever is on, then output is off; when both are on, output is on on the same tick.
- AC-3 unit Given a sequence node expecting inputs A,B,C, when inputs arrive A,C,B, then it resets and emits a failed pulse; A,B,C emits success.
- AC-4 content Given a signal graph with a zero-delay cycle, when validated, then validation fails naming the cycle nodes (edge).
- AC-5 content Given a wire referencing a missing entity id, when validated, then validation fails with the file path and id.
- AC-6 unit Given the same graph and inputs, when evaluated on two runs, then emitted signal order is identical.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #62 (2026-09-28)
area:simclass:allepic:e03milestone:m1size:mmw-e03.31Consolidate world-property vocabulary assumed by other epics
Description
Why
Interactions come from properties, never pairings — which only works if every group uses the same property names. Magic, combat, stealth and systems each assumed properties that e03-world-properties does not define, some with conflicting spellings (
soft_anchorvs camelCase keys).What
As a designer, I want every property other systems rely on defined once, with type, range, default and meaning, so that spells, arrows, tools and AI all read the same vocabulary.
Add to the closed, versioned property set (camelCase, matching e03-world-properties):
flammable(explicit boolean alongside ignitionPoint),flammableGas(gas type ignites/explodes),extinguishable(fire/light source that water, force or interaction can put out)reflective(confirm semantics for beams),waterSurface(liquid top surface; freezable into walkable ice)unstable(collapses/topples when force or charge exceeds threshold),suspended(hangs from a support entity; falls when it breaks/burns/is cut)breakable+toughnessper damage type (blunt/slash/pierce/force, joules) alongsidefragile;bashable(shield bash/kick shove/break),cuttable(slash/pierce severs),shootable(projectile hit emits a signal)softAnchor(rope arrows embed),surfaceHardness(soft/medium/hard: footstep loudness and arrow sticking)chargeActivated/lightActivated(mechanism inputs with thresholds)hidden(not perceivable/targetable until revealed),trapped(links a trap definition; triggers on interaction unless disarmed),container(marker linking to the e18 container component),remains(skeletal/corpse remains usable by summoning)- Actor-derived
noiseMultiplier(product of equipped items' noise multipliers, read by stealth)
Plus query helpers (
breaksUnder(type, energy),isRevealed,supportOf) and a generated docs table.Scope
- In: Schema additions, defaults, invariants, query helpers, material preset updates, alias rejection, docs table, save-schema bump with migration fixture
- Out: Behaviour using the properties — owned by consumers (e03-breakables, e03-ropes-supports, e05-arrow-schema, e10-trap-disarm, e08 spells…)
- Out: Container inventory logic — see e18-containers
Notes
Sources: magic (reflective, unstable, remains, container, water-surface, flammable-gas, charge-activated, light-activated, hidden, trapped), combat (breakable+toughness, shootable, cuttable, bashable, soft_anchor, surface hardness, armor noiseMultiplier), core/stealth (extinguishable, suspended, fragile). e18-containers (P0/m1) must not wait on this item: it keeps its own container component and this item only adds the marker/alias.
Acceptance criteria
- AC-1 content Given the property schema, when validated, then every property listed in this bead exists with type, range, default and doc string, and the generated docs table lists each one.
- AC-2 unit Given an entity with breakable toughness {blunt: 200 J, slash: 800 J}, when breaksUnder is queried for a 300 J blunt hit and a 300 J slash hit, then only the blunt hit returns true.
- AC-3 unit Given an entity with hidden=true, when perception and interaction-targeting queries run, then it is excluded until a reveal clears the flag, and the reveal emits exactly one propertyChanged event.
- AC-4 content Given content using a non-canonical alias such as
soft_anchororwater-surface, when validated, then validation fails with a message naming the canonical key (edge). - AC-5 unit Given invalid combinations (suspended without a support reference, trapped without a trap definition, waterSurface on a non-liquid material), when invariants run, then each is reported with the entity id.
- AC-6 content Given the material presets, when resolved, then every preset declares surfaceHardness and softAnchor (wood and earth true; stone, iron and glass false).
- AC-7 unit Given armour items with noiseMultiplier 1.4 and 1.2 equipped, when actor properties are derived, then the actor noiseMultiplier is 1.68, clamped to 0.2–3.0.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.2 Provide data-defined material presets for common world objects done
- mw-e30.3 Gate save-schema changes on committed migration fixtures in CI done
Closed
Completed in PR #84 (2026-09-29)
area:simclass:allepic:e03milestone:m1size:mmw-e03.35Host a sim-owned deterministic Rapier world with static colliders
Description
Why
mw-e03.10 (size:l) bundles the physics foundation with dynamic-object behaviour. Walking the greybox testbed only needs the foundation, and mw-e02.21 (the CollisionWorld adapter) needs it too. Split out per contract §4.
What
A sim-owned physics port (ADR-0001): the @dimforge/rapier3d-deterministic world created and stepped inside the sim fixed tick, owned by World and not by the renderer. It implements StaticColliderSink (src/sim/physics/static-colliders.ts) with fixed colliders from GreyboxShape and replaces InMemoryColliderSink in src/main.ts. Physics state is included in the sim state hash and snapshot/restore. Vitest can load the WASM (the non-compat package has no main/exports; see the mw-e03.10 notes).
Scope
- In: port interface plus Rapier implementation, lazy WASM init via the physics loader, fixed-step stepping, static colliders, hash and snapshot integration, test WASM loading
- Out: dynamic bodies, properties→mass, impacts, sleeping budget (mw-e03.10); CollisionWorld adapter (mw-e02.21)
Acceptance criteria
- AC-1 unit Given a greybox scene loaded into the Rapier sink, when unloaded, then the physics world has zero colliders.
- AC-2 integration Given the same scene, when the sim runs 600 ticks twice, then state hashes including physics are identical.
- AC-3 unit Given a physics snapshot taken at tick 300, when restored into a new World and run to tick 600, then the hash equals the uninterrupted run.
- AC-4 e2e Given ?scene=testbed, when the app loads, then static colliders are registered in the Rapier world (debug attribute count > 0) with zero console errors.
- AC-5 unit Given physics WASM fails to load, when the sim starts, then a clear error state is reported rather than an uncaught exception (edge).
Closed
Completed in PR #79 (2026-09-29)
area:simepic:e03milestone:m1size:mmw-e03.29Show properties, element fields and light levels in a debug overlay
Description
Why
Designers and developers cannot tune invisible fields. Developer tooling is a technical pillar.
What
Debug overlay (via e33 debug console) toggling: property inspector on the focused entity (with live edit), temperature/moisture/gas/charge heatmaps, light level grid, signal-graph wire view with live states, stimulus log timeline. Console commands to spawn stimuli (stim heat 500 r=2 at cursor).
Scope
- In: Overlay layers + inspector panel + console commands
- In: Read-only in production builds (stripped)
- Out: Puzzle inspector — see e15-puzzle-inspector
Notes
Overlay reads sim state through the public query API only.
Acceptance criteria
- AC-1 integration Given the overlay toggled to temperature, when a heat stimulus is spawned, then the heatmap shows the hot region within 1 frame.
- AC-2 integration Given the inspector on an entity, when wetness is edited to 1.0, then the sim reflects it next tick and propertyChanged fires.
- AC-3 integration Given a production build, when scanned, then overlay code is absent (edge).
- AC-4 e2e Given the greybox testbed, when the overlay is toggled through all layers, then no console errors occur.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e03.4 Build a sparse spatial grid for temperature, moisture, gas and charge done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #368 (2026-11-09)
area:toolsepic:e03milestone:m1size:smw-e03.37Render sim light sources and check sim/render light parity
Description
Why
The sim's light model (mw-e03.15) decides whether a thief is seen; players judge that from what they see on screen. If sim light and rendered light disagree, shadows lie to the player and stealth feels unfair.
What
As a thief, I want the shadows I see to be the shadows guards use so that hiding in a dark corner works when it looks like it should.
Render the sim's light sources in the greybox renderer and prove they agree: a greybox lighting room (scene with torches, a burning prop, a moonlit window), renderer point/spot lights driven from the sim's
LightFieldemitters (intensity and radius mapped from the same falloff), the level's static geometry handed toLightField.staticsalongside physics at scene load, and a Playwright parity probe.Scope
- In: greybox lighting-room scene; scene-file light environment (ambient zones, directional lights) loaded into
LightField.setEnvironment; game wiring (installLightField,lightFieldSystem, a sink that feeds both physics and light statics); renderer lights from sim emitters; Playwright parity probe - Out: the light model itself (done in mw-e03.15); visibility scoring (mw-e09.2); light debug overlay (mw-e33.10)
Notes
Split out of mw-e03.15 (its AC-7): the renderer had no point lights and scenes had no light data, so parity needed render and content work beyond the sim PR. Probe idea: project each sample point to screen, read floor luminance from a canvas screenshot, compare with
LightField.levelAtvia Spearman rank correlation.Acceptance criteria
- AC-1 e2e Given the greybox lighting room, when the Playwright parity probe samples 20 points, then sim light level and rendered luminance rank-correlate ≥ 0.8.
- AC-2 unit Given a scene with light environment data, when it loads, then its ambient zones and directional lights are in the LightField and its static geometry blocks light.
- AC-3 e2e Given a torch entity in the lighting room, when it stops burning, then its rendered light goes out on the same tick the sim level drops.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
Closed
Completed in PR #127 (2026-10-02)
area:renderarea:simclass:thiefepic:e03milestone:m1size:m- In: greybox lighting-room scene; scene-file light environment (ambient zones, directional lights) loaded into
mw-e03.39Spawn scene props as physics objects in the game and render them from their sim pose
Description
mw-e03.10 added the sim physics-object layer (installPhysicsObjects, addPhysicsObject, physics.object pose component, bindCollider) but nothing in the game uses it yet. Wire it in src/main.ts / src/game: install physics objects on the game world, spawn scene spawns whose prop is a movable object (crates, barrels) as physics objects with their material properties, bind level colliders to stone entities so impacts name a material, bind render objects that read physics.object position/rotation through RenderSync, and log physicsBudgetExceeded warnings to the console/debug overlay. ACs to be written per the backlog contract.
Acceptance criteria
AC-1: Loading a scene with physics spawns every spawn whose prop has a body (content testprop.body: box size + material) as a sim physics object standing on its spawn, with the prop's mass as weight and its material's world properties; markers and props without a body stay static.
AC-2: Every solid level-kit piece becomes a stone entity (level material configurable) owning its static colliders via bindCollider, so an impact on the level names the piece entity and ['wood','stone'].
AC-3: Movable props are drawn from their sim pose (physics.object position/rotation) through RenderSync, interpolated between the last two ticks; drawing never changes sim state.
AC-4: physicsBudgetExceeded warnings are logged (console.warn + data-physics-budget debug attribute); the budget focus follows the player.
AC-5: Unloading a scene leaves no bodies or colliders behind; loading and stepping is deterministic (testbed player replay re-recorded).
AC-6: In the browser the testbed spawns its two movable props as physics objects (data-physics-objects=2) with zero console errors.
Closed
Completed in PR #99 (2026-09-29)
area:gamearea:simepic:e03milestone:m1mw-e03.40Play impact sounds from physicsImpact events via cue sheets
Description
mw-e03.10 emits physicsImpact { entity, other, materials, energy, impulse, speed, normal, position } but it is not in the cue-sheet event table (src/content/cue-events.ts CUE_EVENTS) nor bound in src/game/cues/events.ts, so no sound plays for tumbling crates. Add the event with material facts (like HIT_FACTS) and energy as a volume-scaling number, and a default cue rule using each material's impactSound. ACs to be written per the backlog contract.
Acceptance criteria
- AC-1 unit Given a physicsImpact payload, when its cue binding reads it, then it offers the anchors entity, other and at, and the facts entity/other (impact class of each side's material), entityMaterial/otherMaterial (material ids) and energy, impulse, speed (numbers).
- AC-2 unit Given the physics cue sheet, when a wood crate lands on stone through the world event bus, then the cue bridge plays sfx-impact-wood at the crate, and a harder impact (more energy) plays louder than a soft one.
- AC-3 unit Given a cue rule for physicsImpact, when content loads, then it may match the material facts and scale volume by energy, and matching an unknown fact or a number fact is rejected naming the rule.
Closed
Completed in PR #109 (2026-09-30)
area:audioarea:simepic:e03milestone:m1mw-e03.41Remove physics body automatically when a physics-object entity is destroyed
Description
From PR #94: removePhysicsObject must be called manually before destroying a physics-object entity or its Rapier body leaks in the world. Hook entity destruction so the body is removed automatically; test that destroying the entity leaves no body behind and state hash matches.
Closed
Completed in PR #99 (2026-09-29)
area:simepic:e03milestone:m1mw-e03.49Overhead and enclosed lights must not burn passers-by
Description
Owner playtest 2026-11-08: 'it's very easy to get burned by the torches everywhere.' Cause (likely): the environment hazard rule (src/content/data/environment-damage/default.json, src/sim/combat/environment/environment.ts hazardSystem) burns any creature whose capsule touches a sphere of placement.radius + 0.75 m around anything burning. Street lanterns hang at 2.5 m in the middle of the lanes (e.g. briar-glen-lane street-lantern-1..4 at x 0) and wall torches/sconces at head height, so walking under or past them sets the player on fire.
Fix systemically (properties, not per-scene hacks):
- light sources (src/sim/light/sources.ts, mw-e03.16) declare their flame: enclosed sources (lantern, sconce with glass) are not contact hazards; open flames (torch, candle, brazier, fireplace) hurt only within a small flame radius around the flame point, not placement.radius + 0.75;
- legacy lit spawns that only set burning/flammable (lanterns tagged lantern) get the enclosed treatment;
- a loose burning object (a kicked brazier, a burning crate) still burns as today.
AC-1 unit Walking under a lantern hung at 2.5 m deals no fire damage.
AC-2 unit Brushing past a wall torch at 2 m height at 0.5 m distance deals no damage; standing in a brazier's flame does.
AC-3 unit A burning crate on the floor still burns a creature touching it.
AC-4 e2e Walking the full length of briar-glen-lane down the centre line takes no fire damage.
Closed
Completed in PR #333 (2026-11-08)
area:simepic:e03milestone:m1mw-e03.42Remove a level piece's static colliders when fire or damage destroys it
Description
Why
Fire burnout destroys an entity whose material burns away (ivy, straw, cloth). When that entity is a scene kit piece (e.g. a placement with material ivy, now possible via scene placement properties from mw-e03.22), its bound static colliders stay in the physics world (bindCollider: 'the collider stays when the entity goes; its owner removes it'), leaving invisible solid geometry. Climb/ledge queries already treat the piece as gone.
What
When a piece entity with PhysicsColliderComponent is destroyed during play, remove its colliders from the physics sink (and keep unloadScene from double-removing them).
Notes
Discovered while implementing mw-e03.22.
Acceptance criteria
- AC-1 unit Given a scene piece of material ivy that burns out, when the burnout applies, then its colliders are removed from the physics world by the end of the tick.
- AC-2 unit Given such a scene is then unloaded, when unloadScene runs, then no collider is removed twice and no error is thrown.
- AC-3 unit Given the piece is destroyed, then line-of-sight, light occluder statics and ledge queries no longer see it.
Closed
Completed in PR #122 (2026-09-30)
area:simepic:e03milestone:m1milestone:m2size:smw-e03.47Wire Mage Hand tether to the remote hold constraint
Description
Follow-up from mw-e03.12 (PR #311). Mage Hand's tether (src/sim/magic/tether.ts) still teleports its object; switch it to holdRemotely/setHoldPoint/releaseHold in src/sim/magic/remote-hold.ts so held objects obey strength vs weight, obstruction break and release velocity. Also gate tether use / interactRemotely on remoteActuatable once scene levers carry the flag, and use world gravity instead of DEFAULT_GRAVITY in remote-hold.
AC-1 unit Mage Hand holding a liftable object moves it via the spring-damper hold, not teleport.
AC-2 unit Mage Hand on an overweight object fails with ForceResisted reason overweight.
AC-3 unit Remote lever use without remoteActuatable is refused; scene levers meant to be remotely usable carry the flag.
Closed
Completed in PR #330 (2026-11-08)
area:simepic:e03milestone:m1mw-e03.52Sim step cost with ~40 burning entities exceeds the 16.7 ms frame budget
Description
mw-e03.28 AC-4 (e2e/element-perf.spec.ts, VESPER_PERF=1 headed, M1 Pro): 40 burning crates + smoke measured frame p50 1.6 ms, p95 about 140-200 ms with sim ticks as the slow frames; the same scenario with the element bridge disabled gave p95 about 131 ms and the bridge itself costs about 0.2 ms p95, so the cost is in the sim (fire, element field and smoke diffusion, physics) or the existing VFX/render path. Profile and bring it under budget.
Closed
Completed in PR #343 (2026-11-09)
area:simepic:e03milestone:m1
M2 Class fantasies 16
mw-e03.6Simulate water volumes, splashes, wetness and drying
Description
Why
Water extinguishes fire, conducts electricity, freezes into bridges and makes creatures vulnerable — it is the most combinatorial element after fire.
What
As a player, I want water to soak things and put out flames so that dousing a torch or soaking a goblin before a lightning spell just works.
Water volumes (lakes, basins, puddles) with level and drain/fill rates; water stimuli (splash, rain, spell) raise entity wetness and cell moisture; wetness decays by heat and time; entities submerged in a volume become fully wet; puddles spawn from spilled liquid; liquid types (water, oil) share the volume model with different properties (oil floats, flammable).
Scope
- In: Water volume entity (level, fill/drain), submersion checks, puddle spawning from spills
- In: Wetness gain/decay rules; rain zone stimulus
- In: Liquid types: water, oil (flammable, slippery)
- Out: Freezing — see e03-freeze-melt
- Out: Fluid dynamics / flowing rivers simulation — out of scope (volumes are level-based)
Notes
Basins with drains/valves are puzzle pieces (see e15-prefab-pieces). Oil on water floats and can burn on the surface — emergent, not scripted.
Acceptance criteria
- AC-1 unit Given any entity fully inside a water volume, when a tick runs, then its wetness becomes 1.0.
- AC-2 unit Given a wet entity (1.0) at 20°C ambient and no heat source, when time passes, then wetness decays to ≤ 0.1 within the documented drying time; near a burning entity it dries at least 3× faster.
- AC-3 unit Given a basin with a drain opened, when ticks run, then level decreases at the drain rate and entities previously submerged emerge when level falls below them.
- AC-4 unit Given an oil liquid spilled onto a water volume, when resolved, then an oil layer sits on the surface and igniting it burns the surface layer only.
- AC-5 unit Given a water stimulus onto a non-absorbent material (iron), when resolved, then wetness rises but caps at the material absorbency max (edge).
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
Closed
Completed in PR #338 (2026-11-08)
area:simclass:allepic:e03milestone:m2size:mmw-e03.7Freeze water and wet objects into ice and melt them with heat
Description
Why
"Magic can freeze water." Frozen rivers as bridges, frozen doors that shatter, ice surfaces for sliding enemies are signature "Wait… I can do that?" moments.
What
As a sorcerer, I want cold to freeze water and wet things so that I can make bridges, jam mechanisms and shatter frozen objects.
Cold stimuli lower temperature; water volume cells and wet entities below freezePoint become frozen: water volume surface becomes a walkable ice solid (thickness grows with exposure), wet entities gain frozen (immobilised mechanisms, brittle → fragile multiplier), creatures slowed/frozen via actor properties. Heat melts; ice slabs are physics objects that float and can be broken.
Scope
- In: Freezing/melting rules with latent time, ice thickness per cell
- In: Frozen water surface colliders generated/removed deterministically
- In: Frozen modifiers: mechanisms jam, fragility ×3, friction → ice
- Out: Player sliding/walking on ice — see e02-water-ice-traversal
- Out: Frost spells — see mw-e08
Notes
Jammed mechanisms: any mechanism entity with frozen=true refuses to move (e.g. freeze a portcullis winch to stop it closing). Ice collider changes must notify physics to wake bodies resting on it.
Acceptance criteria
- AC-1 unit Given a water volume cell at the surface receiving cold until temperature ≤ freezePoint, when resolved, then an ice collider with thickness ≥ 0.1 m exists at that cell and thickness grows with continued cold up to 0.5 m.
- AC-2 unit Given any wet entity (wetness ≥ 0.3) cooled below freezePoint, when resolved, then frozen=true and its fragility threshold is divided by 3.
- AC-3 unit Given any mechanism entity with frozen=true, when a signal tries to move it, then it does not move and emits a jammed event until thawed.
- AC-4 unit Given an ice surface with an object resting on it, when heat melts it, then the collider is removed and the object falls/floats into the water volume on the same tick.
- AC-5 unit Given a dry entity (wetness 0) cooled far below freezing, when resolved, then it becomes cold but not frozen (edge: freezing requires water).
- AC-6 integration Given the greybox river, when a cold stimulus is swept across it, then the bot can walk across the resulting ice and a replay reproduces the same hashes.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
Closed
Completed in PR #340 (2026-11-08)
area:simclass:sorcererepic:e03milestone:m2size:mmw-e03.8Conduct electrical charge through conductive and wet objects
Description
Why
Storm magic becomes spectacular when lightning arcs through a puddle into three goblins standing in it, or powers an old mechanism — pure property combos.
What
As a sorcerer, I want lightning to spread through water and metal so that I can electrify puddles, armored knights or arcane conduits.
Charge stimuli add charge to entities/cells; charge flows along connected conductive networks (conductive property or wetness ≥ 0.5, or water volumes) with per-hop falloff within one tick; charge above thresholds deals shock (actors), ignites flammables at high intensity, and powers "charge receiver" mechanisms (arcane conduits, old dwarven lifts).
Scope
- In: Connectivity graph build from contacts/overlaps, cached and invalidated on movement/wetness change
- In: Charge distribution with falloff, discharge, shock event, ignition at threshold
- In: Charge receiver component feeding the signal system
- Out: Storm spells — see mw-e08
- Out: Damage numbers — see e04-damage-model
Notes
Network flood fill capped (e.g. 256 nodes) with deterministic BFS by entity id. A knight in metal armor is conductive via actor properties (e03-actor-properties).
Acceptance criteria
- AC-1 unit Given a water volume containing three actors, when a charge stimulus hits the water, then all three receive a shock event with intensity reduced by the documented falloff per hop.
- AC-2 unit Given a chain of conductive entities touching, when charge enters one end, then charge reaches the far end in the same tick; with a non-conductive gap, it does not cross.
- AC-3 unit Given a dry wooden entity next to a charged metal entity, when resolved, then the wood receives no charge; once wetness ≥ 0.5 it conducts.
- AC-4 unit Given a charge receiver in a charged network, when charge ≥ its activation threshold, then it emits an activated signal; below threshold it does not.
- AC-5 unit Given a conductive network larger than the node cap, when charge floods, then propagation stops at the cap deterministically and logs a warning (edge).
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
Closed
Completed in PR #341 (2026-11-08)
area:simclass:sorcererepic:e03milestone:m2size:mmw-e03.12Let external forces hold, levitate and fling liftable objects
Description
Why
Mage Hand, Telekinesis, Gravity Well and "throwing creatures / catching projectiles" all need one world-side API for being held and moved at a distance.
What
As a sorcerer, I want to lift a crate onto a balcony, pull a lever from across a chasm or yank a shield from a goblin so that magic manipulates the world itself.
A holdable-at-distance API: external holders attach a spring-damper constraint to any body with liftable=true (or actors with a liftable tier), limited by holder strength vs. weight, with gravity scale overrides (float), point attractors (Gravity Well), and release with velocity. Interactables (levers) can be actuated remotely if flagged remoteActuatable.
Scope
- In: Remote hold constraint, strength vs weight check, break-hold on obstruction or overload
- In: Gravity scale override zones and point attractors
- In: Remote actuation of interactables with remoteActuatable
- Out: The spells themselves — see e06-spell-pipeline / mw-e08
- Out: Player hand-carry — see e02-carry-throw
Notes
Standing on a levitated platform should work (it is just a moving body; controller handles moving-platform carry). Catching projectiles: arrows are liftable light bodies.
Acceptance criteria
- AC-1 unit Given a liftable 30 kg object and a holder strength of 50, when held, then it tracks the target point with settling time ≤ 0.5 s; given 80 kg, the hold fails with reason overweight.
- AC-2 unit Given a held object blocked by a wall for > 1.0 s with the target far behind it, when resolved, then the hold breaks and the object drops (edge).
- AC-3 unit Given a point attractor of strength S, when loose liftable objects are within radius, then each accelerates toward the point proportional to S/mass and non-liftable objects are unaffected.
- AC-4 unit Given a remoteActuatable lever within the holder range, when remote actuation is applied, then the lever toggles exactly as if interacted with; a lever without the flag does not.
- AC-5 unit Given a held object released with velocity v, when released, then its initial velocity equals v within 1%.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
Closed
Completed in PR #311 (2026-11-08)
area:simclass:sorcererepic:e03milestone:m2size:mmw-e03.13Support hanging objects on ropes that burn, cut or snap
Description
Why
Shooting the rope that holds a chandelier onto enemies, burning a rope bridge behind you, or cutting a hanging cage free are classic emergent moments.
What
As an archer, I want to shoot or burn a rope so that whatever it holds falls — chandeliers, cages, drawbridges, counterweights, bridges.
Rope/chain support entities connect an anchor to a supported body; they have material properties (rope: flammable, slash/pierce-weak; chain: not). When hp reaches 0 or burnt, the support releases, the supported object becomes dynamic (falling damage via impact) and any linked mechanism receives a signal. Rope bridges are chains of planks supported by ropes.
Scope
- In: Support constraint entity with hp/resistances and load
- In: Release → dynamic body + signal output
- In: Rope bridge prefab (planks + two support ropes)
- Out: Climbable ropes — see e02-climb-surfaces (a rope can be both climbable and a support)
Notes
Overload: a support snaps if load weight exceeds its rating — putting a heavy crate on a rotten rope bridge snaps it (failure creates stories).
Acceptance criteria
- AC-1 unit Given any support with flammable material, when it burns out, then it releases and the supported body becomes dynamic on that tick.
- AC-2 unit Given a rope support with pierce resistance 0.3, when hit by pierce damage ≥ its hp/0.7, then it releases; a chain support with pierce immunity does not.
- AC-3 unit Given a released support linked to a mechanism, when released, then the mechanism receives a signal exactly once.
- AC-4 unit Given a rope bridge rated for 150 kg, when total load exceeds 150 kg, then the weakest rope snaps and planks fall (edge/failure).
- AC-5 integration Given the greybox chandelier room, when a pierce stimulus hits the rope, then the chandelier falls and impact damage events fire for actors beneath it.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
Closed
Completed in PR #350. Creature damage from falling objects tracked by mw-aps; armour and carried load by mw-mlf. (2026-11-09)
area:simclass:archerepic:e03milestone:m2size:mmw-e03.16Extinguish and relight torches, candles and braziers systemically
Description
Why
"Extinguishing lights" is a named thief verb; relighting a brazier is a named puzzle pattern. Both must work via properties so a water arrow, a gust, a hand pinch or rain all work.
What
As a thief, I want to snuff torches by hand, with a water arrow, or with a gust so that I can carve a path through darkness.
Light source prefabs (candle, torch, wall sconce, brazier, lantern, fireplace) = flammable fuel + light emitter tied to burning. Extinguish via wetness, force above windResistance, smother interaction (hand, requires reach), or fuel exhaustion. Relight via heat. Enemies noticing lights out is AI's job but a lightChanged event with attribution is emitted.
Scope
- In: Light source prefabs, snuff interaction (silent, 1.0 s), wind/force extinguish thresholds
- In: lightChanged event with source attribution
- In: Unlit-at-start and infinite-fuel flags for designers
- Out: Guard relighting behaviour — see mw-e11
- Out: Water arrows — see mw-e05
Notes
Lanterns carried by guards are the same prefab attached to an actor — freeze or soak one and the guard walks in darkness.
Acceptance criteria
- AC-1 unit Given any lit light source, when its wetness rises ≥ 0.5 from any water stimulus, then it extinguishes and the local light level drops accordingly.
- AC-2 unit Given a candle (windResistance low) and a brazier (high), when the same force stimulus hits both, then the candle extinguishes and the brazier does not.
- AC-3 unit Given a player uses snuff on a torch, when completed, then it extinguishes and emits a noise event of loudness ≤ 0.05.
- AC-4 unit Given an unlit torch, when any heat stimulus exceeds its ignition point, then it relights and emits lightChanged with the heat source as attribution.
- AC-5 unit Given a soaked torch (wetness 1.0), when heat is applied, then it does not relight until dried below 0.5 (edge).
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
Closed
Completed in PR #329 (2026-11-08)
area:simclass:thiefepic:e03milestone:m2size:smw-e03.17Redirect light beams with mirrors and lenses onto receivers
Description
Why
"Redirecting light" is the first puzzle example in the constitution; beams should also interact with properties (burn through webs, blind creatures, melt ice).
What
As a player, I want to angle mirrors so that a sunbeam reaches a sealed door's sensor.
Beam emitters (sunlight shaft, crystal, spell) cast deterministic segmented rays in sim; surfaces with reflective bounce, transparent pass (with tint), opaque stop; lens focus multiplies intensity; beams apply a continuous light stimulus (and heat when focused) where they end; light receivers emit signals when intensity ≥ threshold for a dwell time.
Scope
- In: Beam tracing with max bounces (default 8), reflection, transmission, stop
- In: Light receiver component with threshold + dwell + optional colour filter
- In: Focused beams apply heat stimulus
- Out: Rotating mirrors — see e02-push-pull (rotate affordance)
- Out: Beam VFX — see e03-state-feedback-bridge / e29-vfx-framework
Notes
Frozen water/ice is transparent; a player or creature standing in the beam blocks it (actors are opaque) — enabling "stand here to break the beam" solutions.
Acceptance criteria
- AC-1 unit Given a beam hitting a reflective surface at 30°, when traced, then it reflects at 30° about the surface normal within 0.01°.
- AC-2 unit Given a beam reaching a receiver with threshold 0.5 for dwell 1.0 s, when intensity is 0.6 for 1.0 s, then it emits activated; interrupted at 0.9 s, it does not.
- AC-3 unit Given a beam passing through a lens, when it hits a flammable entity, then heat stimulus is applied and the entity ignites per fire rules.
- AC-4 unit Given two mirrors facing each other creating a loop, when traced, then tracing stops at max bounces without hanging (edge).
- AC-5 unit Given an actor steps into the beam path, when traced, then the beam terminates at the actor and downstream receivers deactivate.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
Closed
Completed in PR #331 (2026-11-08)
area:simclass:allepic:e03milestone:m2size:mmw-e03.19Activate pressure plates by total weight of anything resting on them
Description
Why
"Weighing pressure plates" is a named puzzle pattern. Plates must count any weight — crates, bodies, ice blocks, summoned skeletons — so solutions multiply.
What
As a player, I want a plate to trigger when enough weight rests on it so that I can use a crate, a body, a frozen block or myself.
Plate component: min/max weight window, hysteresis, momentary vs latching, arming delay; weight = sum of weight properties of bodies and actors in contact (including carried items).
Scope
- In: Plate weight summation from contacts incl. actors and their carried/equipped weight
- In: Windowed thresholds (too heavy also fails), hysteresis, latching, delay
- In: Trap variant: hidden plate outputs to trap signals
- Out: Trap effects — see mw-e10
Notes
Weight window enables "exactly 40 kg" scale puzzles. Levitated objects (gravity scale 0) weigh nothing on a plate — emergent sorcerer trick.
Acceptance criteria
- AC-1 unit Given a plate with min 40 kg, when a 25 kg crate and a 20 kg barrel rest on it, then it activates; removing the barrel deactivates it after hysteresis.
- AC-2 unit Given a plate with window 40–60 kg, when 70 kg rests on it, then it does not activate (too heavy).
- AC-3 unit Given an actor carrying a 10 kg object stands on a plate, when summed, then weight includes actor + carried weight.
- AC-4 unit Given an object under gravity-scale 0 (levitated) hovers touching the plate, when summed, then it contributes 0 kg (edge).
- AC-5 unit Given a latching plate, when weight is removed after activation, then it stays active until reset by a signal.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
Closed
Completed in PR #318 (2026-11-08)
area:simclass:allepic:e03milestone:m2size:smw-e03.20Trigger distant mechanisms with projectiles, impacts and force
Description
Why
"The archer might shoot a distant mechanism." Bullseye switches, bells, hanging counterweights and crystal switches must respond to any impact source, so thrown rocks and spells work too.
What
As an archer, I want to shoot a target on a far wall to lower a bridge so that precision opens routes others must work around.
Impact-switch component: activates when an impact/stimulus meets its filter (min energy, damage type set, element set, hit zone). Variants: bullseye (pierce, small hit zone), bell (any impact, emits loud noise), crystal (charge/light), counterweight (release via rope support).
Scope
- In: Impact switch with filters and hit-zone geometry
- In: Variants as prefabs; bell as noise-emitting distraction tool
- In: Outputs to signal system
- Out: Arrow projectile — see e05-bow-core
Notes
A sorcerer's Mage Hand pressing the button, a thief's thrown dagger, a knight's thrown shield — all valid if the filter permits. Designers restrict filters only deliberately.
Acceptance criteria
- AC-1 unit Given a bullseye filtered to pierce with min energy 20 J, when a 25 J pierce impact hits inside the hit zone, then it activates; the same impact 5 cm outside the zone does not.
- AC-2 unit Given a bell switch filtered to any impact, when a thrown object at ≥ 5 J hits it, then it activates and emits a noise event with loudness 1.0.
- AC-3 unit Given a crystal switch filtered to charge, when a heat stimulus hits it, then it does not activate; a charge stimulus does.
- AC-4 unit Given multiple hits in one tick on a momentary switch, when resolved, then exactly one activation event fires (edge).
Waits on
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
Closed
Completed in PR #322 (2026-11-08)
area:simclass:archerepic:e03milestone:m2size:smw-e03.22Mark climbable surfaces and ledges from properties and geometry
Description
Why
Climbing routes must be authorable quickly and change systemically (burn ivy, freeze a wall, prop a ladder).
What
As a designer, I want to tag surfaces as climbable and have ledges auto-detected so that I can create alternate routes quickly.
Climbable property on colliders with grade (ladder, rope, ivy, rough, sheer=no), plus an offline/at-load ledge extraction pass over greybox geometry producing ledge segments (with overrides to disable/enable). Property changes at runtime (ivy burned, surface frozen → slippery) update climbability.
Scope
- In: Climbable grade property + capability requirement data
- In: Ledge extraction pass with per-edge override markup, ledge query API
- In: Runtime invalidation on property change
- Out: Player climbing movement — see e02-climb-surfaces, e02-ledge-mantle
Notes
Ivy uses material ivy (flammable) — burning removes the climbable property via burnt-state data. Frozen surfaces set a slippery flag that blocks climbing unless the actor has ice tools (future).
Acceptance criteria
- AC-1 unit Given a greybox box of height 1.5 m, when ledge extraction runs, then ledge segments are produced along its top edges and none along vertical edges.
- AC-2 unit Given an edge override disable, when extraction runs, then no ledge is produced there.
- AC-3 unit Given an ivy surface that burns out, when the burnt state applies, then its climbable grade becomes none and ledge/climb queries reflect it on the next tick.
- AC-4 unit Given a surface becomes frozen, when queried, then climbable returns slippery and climb attach is refused (edge).
- AC-5 content Given a level file with a climbable grade not in the enum, when validated, then validation fails.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #113 (2026-09-30)
area:contentarea:simclass:thiefepic:e03milestone:m2size:smw-e03.23Hide in wardrobes, barrels and haystacks and search containers
Description
Why
Hiding places are a named stealth factor; containers hold loot, bodies and secrets. They must also be systemic (a burning haystack evicts you; a searched wardrobe is found).
What
As a thief, I want to slip into a wardrobe or haystack and stash bodies in chests so that I can break line of sight and cover my tracks.
Hideable component (capacity, concealment 0..1, exit points, noise on enter/exit, peek allowed) and container component (inventory, lockable via e03 lock, capacity by weight, holds bodies if large). Actors inside are removed from sight queries except by search; containers that burn/break eject contents and occupants.
Scope
- In: Enter/exit hide verbs via interaction, peek camera hook, occupancy
- In: Container inventory binding to e17, open/search/loot affordances
- In: Break/burn/submerge ejects occupants and spills contents
- Out: AI searching hiding spots — see mw-e11 (uses the occupancy query)
- Out: Loot tables — see e18-loot-tables
Notes
Hay is flammable; hiding in a haystack next to a torch-wielding guard is a story. Occupancy query: isOccupied(entity) for AI search, respecting "last seen near" logic on their side.
Acceptance criteria
- AC-1 unit Given a hideable with capacity 1, when the player enters, then the player is flagged hidden with the container concealment value and a second actor cannot enter.
- AC-2 unit Given a hidden player, when the hideable ignites or breaks, then the player is ejected to the nearest free exit point on that tick and takes heat exposure if burning.
- AC-3 unit Given a container holding items, when it breaks, then all items spawn as world pickups around it.
- AC-4 unit Given all exit points are blocked, when the player tries to exit, then exit is refused and the prompt shows "Blocked" (edge).
- AC-5 unit Given a container with capacity 60 kg, when a 70 kg body is stashed, then it is refused.
- AC-6 integration Given the save system, when saving with an item and a body inside a chest, then after reload both are still inside (via e27).
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #359. Test scene hiding-room; follow-ups in mw-nm7; AI search in mw-e11.11. (2026-11-09)
area:simclass:thiefepic:e03milestone:m2size:mmw-e03.24Apply world properties and elements to creatures and the player
Description
Why
Enemies are part of the same world: a soaked goblin conducts lightning, a burning spider flees, a knight in iron armor is conductive, a frozen skeleton shatters. No per-creature element scripts.
What
As a sorcerer, I want creatures to be wet, burning, frozen or charged by the same rules as objects so that combining spells on enemies is intuitive.
Actors carry the same property components (with actor-specific resistances from e12 creature data and equipment): wetness, temperature, burning, frozen (slow → immobilised), charge, conductive (from armor), weight (for plates/carry). Element rules emit actor-facing events (onFire, frozenSolid, shocked, soaked) that combat/AI respond to. Actors spread fire to what they touch.
Scope
- In: Property components on actors with resistance modifiers from creature/equipment data
- In: Status thresholds and events; movement speed modifier hooks
- In: Burning actors ignite flammables they touch; wet actors extinguish
- Out: Damage values and poise — see e04-damage-model
- Out: AI panic/flee — see mw-e11
- Out: Creature data schema — see e12-creature-schema
Notes
Frozen-solid actors become temporary physics objects with fragile=true (the shatter combo) — pushing one off a ledge should work.
Acceptance criteria
- AC-1 unit Given any actor with wetness ≥ 0.5, when a charge stimulus hits it, then shock intensity is multiplied by the conductive-wet factor from data.
- AC-2 unit Given a burning actor moving through flammable grass cells, when ticks run, then grass cells along its path ignite.
- AC-3 unit Given an actor accumulating cold beyond its frozen threshold, when resolved, then it becomes frozen solid (immobile, fragile) for the data duration and emits frozenSolid.
- AC-4 unit Given an actor with fire immunity (resistance 1.0), when exposed to heat, then burning never becomes true but it can still ignite things it touches if its own burning is data-forced (e.g. fire elemental) (edge).
- AC-5 unit Given a player equips iron armor, when conductive is queried, then it returns true; unequipping restores the base value.
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #353. Frozen actors as physics objects tracked by mw-7ns. (2026-11-09)
area:simclass:allepic:e03milestone:m2size:mmw-e03.25Emit noise events from world interactions with material loudness
Description
Why
Breaking glass, slamming doors, falling crates, bells and splashes must be audible to AI and audio through one consistent event, not per-system hacks.
What
World systems (impacts, breaks, doors, mechanisms, splashes, fire crackle, explosions) emit NoiseEvent{pos, loudness, kind, source} via the event bus, with loudness computed from impact energy × material loudness (data).
Scope
- In: NoiseEvent type + emission from e03 systems
- In: Material loudness data, energy→loudness curve
- Out: Propagation, occlusion and perception — see e09-sound-propagation, e11-perception
- Out: Audio playback — see e28-audio-engine
Notes
Related: e09-sound-propagation consumes these; keep the event schema stable and documented in docs/design/noise-events.md.
Acceptance criteria
- AC-1 unit Given a crate impact of 200 J on stone, when resolved, then a NoiseEvent is emitted with loudness from the documented curve and the impact source.
- AC-2 unit Given a door slam vs a slow close, when compared, then slam loudness ≥ 3× slow close loudness.
- AC-3 unit Given 50 micro-impacts per second from a settling pile below the min energy threshold, when resolved, then no noise events are emitted (edge: spam suppression).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
Closed
Completed in PR #352. AC-2 read as sound pressure: a kicked door is +12 dB. Rope, ice, charge and fire sounds in mw-4vr; noise arrows in mw-ye1. (2026-11-09)
area:simclass:thiefepic:e03milestone:m2size:smw-e03.26Property-test all element × property combinations in a matrix harness
Description
Why
Emergence is only a feature if it is safe: combinations nobody designed must not crash, loop forever or break invariants. Property-based testing is how we cover a combinatorial space we cannot enumerate by hand.
What
As a developer, I want a generator-driven harness that throws random entities (random property sets) and random stimulus sequences into a sandbox world so that invariants hold for every element × property combination.
Uses fast-check to generate property bags and stimulus programs; runs N ticks headless; checks invariants: determinism (double-run hash), termination (fields return to ambient after sources stop), conservation (heat/gas/water within tolerance), no NaN/Infinity, state consistency (frozen ⇒ not burning; burnt ⇒ fuel 0), no type-pair logic reachable. Emits a matrix report (element × property → observed transitions) committed as docs/design/interaction-matrix.md.
Scope
- In: fast-check arbitraries for property bags, materials and stimulus programs
- In: Invariant library and shrinking-friendly failure output (seed + minimal case)
- In: Matrix report generator; CI job with fixed seed budget ≤ 60 s + nightly long run
- Out: Handcrafted scenario replays — see e03-emergence-scenarios
Notes
Every failing case found becomes a named regression test. Matrix report diff in PRs makes emergent behaviour changes visible to designers ("this PR makes wet wood no longer conduct").
Acceptance criteria
- AC-1 unit Given 500 generated worlds × 300 ticks with a fixed seed, when run in CI, then all invariants hold and the job completes in ≤ 60 s.
- AC-2 unit Given a deliberately injected bug (burning allowed while frozen), when the harness runs, then it fails and prints a shrunk minimal reproduction with seed.
- AC-3 unit Given every pair of (element ∈ heat,cold,water,charge,force,gas,light) × (property ∈ schema), when the matrix report is generated, then every cell is populated with observed transitions or "no effect".
- AC-4 integration Given a PR that changes an element rule, when CI runs, then the regenerated matrix report is diffed and the diff is attached to the job summary.
- AC-5 unit Given extreme values (temperature 1e6, weight 0, radius 0), when generated, then no NaN/Infinity appears in any property or field cell (edge).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e03.8 Conduct electrical charge through conductive and wet objects done
Closed
Completed in PR #365 (2026-11-09)
area:qaepic:e03milestone:m2size:mmw-e03.28Show burning, wet, frozen and charged states through VFX and audio
Description
Why
Systems the player cannot read are systems the player cannot exploit. Wet, frozen, charged and burning must be legible at a glance.
What
As a player, I want to see and hear that something is soaked, frozen or crackling with charge so that I can plan combos.
A presentation bridge subscribing to propertyChanged and element events: drives material parameters (wet darkening, frost overlay, char), VFX hooks (fire, steam, sparks, smoke volumes, beams) via e29 and audio cues (crackle loops, sizzle, freeze crack, zap) via e28. Placeholder grey-box visuals are acceptable; final art via separate beads.
Scope
- In: Event → material/VFX/audio mapping table in data
- In: Pooling and LOD for element VFX; field-visualisation for gas/smoke
- In: Playwright smoke + perf checks
- Out: Final VFX art — see mw-e29 asset beads
- Out: Final SFX — see mw-e38 asset beads
Notes
Mapping is data-driven by property/state, not entity type: any burning entity gets fire VFX sized to its bounds.
Acceptance criteria
- AC-1 unit Given the mapping table, when an entity gains burning=true, then exactly one fire VFX handle is requested sized to its bounds and released when burning=false.
- AC-2 unit Given 200 entities ignite in one tick, when bridged, then VFX requests beyond the pool cap (64) are LOD-culled by distance deterministically (edge).
- AC-3 e2e Given the chaos room, when the fire and freeze scenarios run in the browser, then VFX nodes appear (scene probe) and there are no console errors.
- AC-4 perf Given 40 burning entities and a smoke cloud in view, when rendering on the reference machine (High), then frame time p95 ≤ 16.7 ms.
- AC-5 manual Given a playtest, when testers are shown 6 objects in different states, then ≥ 80% correctly identify wet, frozen, burning and charged without UI text.
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
area:gamearea:renderclass:allepic:e03milestone:m2size:mmw-e03.30Budget element, physics and light simulation cost per tick
Description
Why
Systemic worlds die by a thousand cuts in the browser. The element sim must stay within a fixed slice of the frame on the reference and low-preset machines.
What
Stress scenes (large fire spread, flooded room freezing, gas explosion, 200 physics bodies) with perf assertions wired into e32. Add adaptive degradation: when budget exceeded, reduce far-chunk update rate deterministically (sim-rate LOD keyed by distance, not by wall time, to keep determinism).
Scope
- In: 4 stress scenes + perf tests
- In: Deterministic distance-based update LOD for field chunks
- Out: Global frame budget — see e32-perf-budgets
Notes
LOD must not depend on measured frame time (non-deterministic); only on player distance and active counts.
Acceptance criteria
- AC-1 perf Given the fire-spread stress scene (2,000 active cells), when simulated on the reference machine, then element step ≤ 2.0 ms per tick p95.
- AC-2 perf Given the same scene on the low-preset profile, when simulated, then element step ≤ 3.5 ms per tick p95.
- AC-3 unit Given field chunks > 40 m from the player, when LOD applies, then they update every 4th tick and results are deterministic across runs.
- AC-4 perf Given the gas explosion scene (worst case), when simulated, then no single tick exceeds 8 ms (edge: spike).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
Closed
Completed in PR #367 (2026-11-09)
area:perfepic:e03milestone:m2size:s
E04Knight CombatSword/shield gameplay supports attack chains, blocks, parries, dodges, stagger, heavy/charged attacks, shield bashes, weapon reach, armor trade-offs and satisfying hit reactions — and a greybox skeleton fight is fun with no rewards. The shared damage model underpins every class.26/39 done · 67%
M1 Grey-box slice 16
mw-e04.1Deterministic damage model: damage types, resistances, poise, death
Description
Why
Every class and creature must resolve harm through one rule set so knight swings, arrows, backstabs, spells and falling rocks compose systemically (Combat constitution; "enemies reward understanding behaviour" via weaknesses).
What
A pure
src/sim/combat/damagemodule. ADamagePacketcarriesinstigator,source, per-type amounts (slash,pierce,blunt,fire,frost,shock,arcane,poison),poiseDamage,staminaDamage,impulse,impactForceandtags(e.g.critical,backstab,weakpoint,riposte,environment). Resolution runs an ordered modifier pipeline: attacker modifiers → hit-region multiplier → defender resistances → guard/block → armor flat absorption → clamp. Other groups register modifiers (e.g. thief backstab ×N, spell amplifiers) without editing core code. Components:Health,Poise(max, current, regen delay, regen rate),Resistances. EmitsDamageApplied(with per-type breakdown),PoiseBroken,Diedon the sim event bus. Amounts use integer hundredths to avoid float drift in replays.Scope
- In: Damage packet, damage types, resistance multipliers, modifier pipeline API, health, poise accumulation/regen, death event
- In: Zod schema fragment for resistances/poise reused by e12-creature-schema
- Out: Hit detection volumes (e04-hit-volumes)
- Out: Hit reaction selection (e04-hit-reactions)
- Out: Environmental damage sources (e04-environmental-damage)
- Out: Thief backstab rules (thief group, consumes modifier API)
- Out: Spell damage authoring (e06-spell-pipeline)
Notes
Poise defaults: poise regenerates at 25% of max per second after 2.0 s (120 ticks) without poise damage. Resistance multipliers in [0, 3]; 0 = immune. Example: skeleton blunt 1.5, pierce 0.5, poison 0.
This is the anchor other groups depend on. Related: thief backstab bonus (e10), sorcerer spell damage (e06), archer weak points (e05-weak-points) all register modifiers here.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a defender with slash resistance 0.5 and no armor, when a 40-slash packet resolves, then health decreases by exactly 20.
- AC-2 unit Given a packet of 30 blunt + 20 fire against resistances blunt 1.5 / fire 1.0, when resolved, then total damage is 65 and
DamageAppliedlists the per-type breakdown {blunt: 45, fire: 20}. - AC-3 unit Given two modifiers registered at stages
attacker(×2 when taggedbackstab) anddefender(−10 flat), when a tagged 20-pierce packet resolves, then result is 30 and stage order is stable by (stage, registration id) across 1000 seeded runs. - AC-4 unit Given poise max 50, when three 20-poise hits land within 120 ticks, then
PoiseBrokenis emitted exactly once on the third hit and poise resets to 50; given the same hits 150 ticks apart, then no break occurs because poise regenerated. - AC-5 unit Given a creature with 5 health, when a 1000-damage packet resolves, then health clamps to 0,
Diedis emitted exactly once, and later packets on the dead entity are ignored (no secondDied, no negative health). - AC-6 unit Given a resistance of 0 for a damage type, when only that type is dealt, then 0 damage is applied and
DamageApplied.immuneis true so feedback can show "no effect". - AC-7 content Given creature data with resistances, when validated, then unknown damage types or multipliers outside [0, 3] fail with the offending JSON path.
- AC-8 unit Given the combat sim module, when the coverage suite runs, then src/sim/combat/damage reports 100% lines/branches/functions/statements.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e31.14 Inject a DifficultyConfig of tunable multipliers into the sim done
Closed
Completed in PR #61 (2026-09-28)
area:simclass:allepic:e04milestone:m1mw-e04.2Swept hitboxes and region-tagged hurtboxes in the sim
Description
Why
Hits must land where the player sees them land and never tunnel through fast swings; region tags make heads and weak points matter for every class.
What
Sim-side collision shapes independent of the renderer: hurtboxes (capsule/sphere/box) attached to entity sockets with a region tag (
weakpoint>head>torso>limb, plusarmoredflag) and multiplier; hitboxes driven by authored socket tracks from move data and swept from previous to current tick pose so a 2 m sword arc over 4 active ticks cannot skip a thin target. One hit per target per active window. Faction/friendly-fire rules per move so creatures can be tricked into hitting each other. The render animation follows sim poses, never the other way round.Scope
- In: Shape types, swept capsule tests, region priority, per-swing hit registry, friendly-fire flags
- In: Debug draw data exposed to the greybox testbed overlay
- Out: Projectile swept tests (e05-arrow-projectile reuses the query)
- Out: Animation authoring (render)
Notes
Region priority resolves overlapping hurtboxes. Keep shape math in src/sim/geom so arrows and spells share it.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a sword hitbox sweeping a 2.0 m arc over 4 active ticks and a 0.4 m-radius target capsule mid-arc that no single tick pose overlaps, when stepped, then exactly one hit registers on the tick whose sweep intersects it.
- AC-2 unit Given a hitbox overlapping the same target for all 4 active ticks, when stepped, then exactly one hit event is emitted for that swing and target.
- AC-3 unit Given overlapping head and torso hurtboxes both intersected on the same tick, when resolved, then the hit carries region
headand its multiplier. - AC-4 unit Given a sweep crossing an ally with
friendlyFire: truein the move, when stepped, then the ally is damaged; withfriendlyFire: false, then the ally is ignored. - AC-5 unit Given an attacker destroyed during its active window, when the next tick runs, then its hit volumes are removed and no further hits are emitted.
- AC-6 integration Given the greybox testbed with the hitbox overlay enabled, when a swing plays, then the drawn shapes hash-match the sim shapes for every rendered frame.
- AC-7 perf Given 24 active hitboxes and 40 hurtboxes, when the hit query runs, then it takes ≤ 0.5 ms per tick p95 on the reference machine.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
Closed
Completed in PR #90 (2026-09-29)
area:simclass:allepic:e04milestone:m1mw-e04.3Data schema for attack moves: frames, costs, hitboxes, flags, cues
Description
Why
Combat must be data-driven so knights, creatures and archers share one move format and designers tune timing without code changes (Technical constitution).
What
A zod schema for
MoveDef:id, frame data (startup,active,recoveryticks),cancelWindows(into dodge/attack/block by tick range),staminaCost, damage packet template,poiseDamage,staminaDamage, hitbox track id,reachclass,swing(horizontal|vertical|thrust), flags (parryable,blockable,unblockable,hyperarmortick range + poise cap,iframestick range,interruptible),telegraphTick,chainNext, and presentation ids (anim,audioCue,vfxCue). Initial data: knight sword light 1–3, heavy, charged, shield bash, kick, dodge roll, backstep; training-dummy swing.Scope
- In: Schema, validators, initial knight move file, loader to runtime tables
- Out: Executing moves (e04-action-timeline)
- Out: Creature-specific move sets (e13 bestiary, via e04-creature-attack-hooks)
Notes
Unknown presentation cue ids warn (placeholder assets may lag) but never fail validation.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 content Given the knight move file, when validated, then all moves pass and
sword-light-1resolves to startup 12 / active 4 / recovery 18 ticks. - AC-2 content Given a move whose i-frame or hyperarmor range extends beyond startup+active+recovery, when validated, then it fails with the move id and field path.
- AC-3 content Given a move whose
chainNextreferences a missing move id, when validated, then it fails listing the dangling reference. - AC-4 content Given a move flagged both
parryableandunblockablewithoutparryableoverride note, when validated, then a warning is emitted (legal but unusual). - AC-5 content Given a move with an unknown
audioCueid, when validated, then validation passes with one warning naming the id.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
Closed
Completed in PR #64 (2026-09-28)
area:contentclass:allepic:e04milestone:m1mw-e04.4Action timeline FSM with input buffer, cancel windows and local time
Description
Why
Souls-like weight comes from committed startup/active/recovery phases; a fair 150 ms input buffer keeps that weight from feeling unresponsive.
What
A deterministic per-entity action state machine that executes
MoveDefs: phases, cancel windows, chain transitions, interruption by hit reactions, and a per-entity local time scale (used for hit-stop). Input buffer: an action pressed up to 9 ticks (150 ms) before it becomes legal executes on the first legal tick; the most recent buffered input wins. Consumes abstract actions from e02-input-actions, never raw keys.Scope
- In: Timeline runner, buffer, cancel rules, interrupt API, local time scale, action-rejected events
- Out: Specific knight moves and their tuning (e04-melee-core and siblings)
- Out: Hit-stop amounts (e04-hit-stop)
Notes
Stamina is spent on the first startup tick and is not refunded if interrupted (souls convention).
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a move with startup 12 / active 4 / recovery 18, when executed from tick 0, then phase transitions occur at ticks 12, 16 and 34 exactly.
- AC-2 unit Given attack pressed 8 ticks before recovery ends, when recovery ends, then the chained move starts that tick; given it pressed 12 ticks before, then the input is dropped.
- AC-3 unit Given a move with a dodge cancel window starting at recovery tick 6, when dodge is pressed at recovery tick 4, then the dodge executes on recovery tick 6.
- AC-4 unit Given an entity interrupted during startup, when the interrupt resolves, then no hitbox spawns and the stamina already spent is not refunded.
- AC-5 unit Given local time scale 0 for 4 ticks during an active phase, when time resumes, then the phase continues from the same offset and total move duration is extended by exactly 4 ticks.
- AC-6 integration Given a recorded input log of a 3-hit chain with a dodge cancel, when replayed 100 times through e00-replay-harness, then every tick's state hash is identical.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e04.3 Data schema for attack moves: frames, costs, hitboxes, flags, cues done
Closed
Completed in PR #87 (2026-09-29)
area:simclass:allepic:e04milestone:m1mw-e04.5Stamina pool governs attacks, blocks, dodges and sprint
Description
Why
Stamina gives fights rhythm — attack, breathe, defend — which is the backbone of weighty souls-like combat.
What
As a knight, I want a stamina bar that my attacks, blocks, dodges and sprinting draw from so that fights have a readable push-and-pull rhythm.
Max 100. Regenerates 40/s after a 30-tick (500 ms) delay since the last spend; while blocking regen ×0.35. Costs come from move data. An action is allowed whenever stamina > 0 and may drive it to 0 (the "last action" rule). Reaching 0 triggers Exhausted: regen delay becomes 72 ticks (1.2 s) and sprint is disabled until stamina ≥ 20. Sprint drains 10/s.
Scope
- In: Stamina component, regen rules, exhaustion, action gating,
ActionRejected{reason:"stamina"}event - Out: HUD bar (e04-hud-vitals)
- Out: Armor-weight regen modifiers (e04-armor-weight)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given 100 stamina, when a 12-cost attack starts, then stamina is 88 and does not regenerate for 30 ticks, after which it rises 40/s.
- AC-2 unit Given 5 stamina, when a 20-cost dodge is requested, then it executes and stamina becomes 0 (not negative) and Exhausted is set.
- AC-3 unit Given 0 stamina, when attack is pressed, then the action is rejected and
ActionRejected{reason:"stamina"}is emitted once per press. - AC-4 unit Given an Exhausted knight, when sprint is held, then sprint is refused until stamina ≥ 20.
- AC-5 unit Given a knight holding block, when 60 ticks pass after the regen delay, then stamina regenerates at 14/s (40 × 0.35).
- AC-6 manual Given a 5-minute sandbox session, when a playtester fights the attacker dummy, then they can describe when they are "out of breath" without looking at the HUD (audio + animation cues suffice).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
Closed
Completed in PR #57 (2026-09-28)
area:simclass:allepic:e04milestone:m1- In: Stamina component, regen rules, exhaustion, action gating,
mw-e04.6Knight light attack chain and shield block with guard break
Description
Why
This is the first playable fight of the vertical slice: a sword chain that feels weighty and a shield that makes defence a choice (Knight fantasy: weight, impact, mastery).
What
As a knight, I want a three-hit sword chain and a shield block so that I can trade blows deliberately and defend against frontal attacks.
Chain: L1 12/4/18 ticks, 12 stamina, 20 slash, 15 poise; L2 10/4/20, 14 stamina, 22 slash, 15 poise; L3 16/5/26, 18 stamina, 30 slash, 30 poise. The chain resets to L1 after 30 idle ticks past recovery. During startup the knight may rotate up to 360°/s toward the lock-on target (from e02-camera); during active frames rotation is locked (commitment).
Block (hold): shield becomes active after 6 ticks (100 ms); covers a frontal 120° arc; movement at 50% speed. Wood shield absorbs 85% physical / 30% fire, stability 60: stamina loss = attack
staminaDamage× (1 − stability/100). If stamina reaches 0 while blocking, Guard Break: 60-tick stagger,GuardBrokenevent, and the unabsorbed remainder of that hit applies.Scope
- In: Light chain, block, guard break, lock-on tracking during startup, initial wood shield data
- Out: Parry and riposte (e04-parry-riposte)
- Out: Dodge (e04-dodge-iframes)
- Out: Heavy/charged (e04-heavy-charged)
- Out: Shield bash (e04-shield-bash)
Notes
Anchor e04-melee-core: this item delivers the minimum fight for M1. Parry and dodge (also in the anchor's meaning) land as sibling items e04-parry-riposte and e04-dodge-iframes so dependents are not blocked on them.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given an idle knight with 100 stamina, when attack is pressed three times inside each buffer window, then L1→L2→L3 execute, 44 stamina is spent, and the next press starts L1 again.
- AC-2 unit Given a knight whose shield has been raised ≥ 6 ticks, when a frontal 30-slash hit with staminaDamage 20 lands, then health drops by 4.5 and stamina by 8.
- AC-3 unit Given a blocking knight, when a hit arrives from outside the frontal 120° arc, then full damage applies and no stamina is drained by the block.
- AC-4 unit Given the shield raised only 3 ticks, when a frontal hit lands, then the block is not yet active and full damage applies.
- AC-5 unit Given a blocking knight with 8 stamina, when a hit with staminaDamage 20 lands, then stamina is 0,
GuardBrokenis emitted, the knight is staggered for 60 ticks and the unabsorbed damage is applied. - AC-6 integration Given the combat sandbox dummy, when L1 connects, then
DamageAppliedappears on the sim event bus on a tick within L1's active window (ticks 12–15). - AC-7 e2e Given the greybox testbed in Chromium, when Playwright issues the attack action three times, then the dummy's health drops by 72 (20+22+30) and no console errors are logged.
- AC-8 manual Given a 5-minute sandbox session, when three playtesters use the chain, then median rating for "swings feel heavy and committed" is ≥ 4/5 and none describe attacks as floaty.
Waits on
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e04.2 Swept hitboxes and region-tagged hurtboxes in the sim done
- mw-e04.4 Action timeline FSM with input buffer, cancel windows and local time done
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
Closed
Completed in PR #105 (2026-09-30)
area:simclass:knightepic:e04milestone:m1mw-e04.7Sim-chosen hit reactions: flinch, stagger, knockback, knockdown
Description
Why
Readable reactions tell the player what their hit did; knockback into ledges and hazards lets combat interact with the world.
What
As a player, I want enemies (and me) to react differently to light hits, poise breaks and heavy impacts so that I can read the fight and exploit openings.
The sim selects a reaction tier from poise state, hyperarmor and impulse:
none(hyperarmor absorbed),flinch12 ticks (interrupts startup only),stagger45 ticks (onPoiseBroken),knockback60 ticks + physics displacement (impulse ≥ 300 N·s equivalent),knockdown90 ticks grounded then wake-up with 20 i-frame ticks. Direction quadrant (front/back/left/right) from hit vector vs facing. Creature schema may override tiers (e.g. trolls never knocked down). Displacement goes through physics so a shoved creature can fall off ledges or into fire.Scope
- In: Reaction selection, durations, direction, displacement via physics,
HitReactionevent with reaction id for render/audio - Out: Animation assets (render/art)
- Out: Critical/riposte states (e04-parry-riposte)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a creature with 40 poise and no hyperarmor, when a 15-poise hit lands during its startup, then a 12-tick flinch interrupts the attack.
- AC-2 unit Given a hit that emits
PoiseBroken, when resolved, then the reaction isstaggerfor 45 ticks and the victim's current action is cancelled. - AC-3 unit Given a hit from behind the victim, when resolved, then the reaction direction is
back; given a hit at 80° to the right, thenright. - AC-4 unit Given a victim in a hyperarmor window with remaining cap 40 and a 30-poise hit, when resolved, then reaction is
nonebut damage still applies. - AC-5 integration Given a creature standing 0.5 m from a ledge in the greybox testbed, when a knockback hit lands toward the ledge, then the creature falls off and environmental fall damage rules apply.
- AC-6 unit Given a knocked-down entity, when it wakes up, then it is invulnerable for 20 ticks and cannot be re-knocked-down during them.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e04.4 Action timeline FSM with input buffer, cancel windows and local time done
Closed
Completed in PR #101 (2026-09-30)
area:simclass:allepic:e04milestone:m1- In: Reaction selection, durations, direction, displacement via physics,
mw-e04.8Dodge roll and backstep with invulnerability frames
Description
Why
A precise dodge with honest i-frames is the souls-like skill expression that makes dangerous enemies fair.
What
As a knight, I want to roll through or away from attacks with a short invulnerability window so that timing, not stats, keeps me alive.
Roll (directional, relative to camera or lock-on): 36 ticks, 20 stamina, i-frames ticks 2–14 (13 ticks ≈ 217 ms) at medium defaults, 3.0 m travel. Backstep (no direction held): 24 ticks, 12 stamina, i-frames 2–7. Roll recovery cancellable into a roll-attack from tick 28. Hits landing during i-frames apply no damage, poise or reaction and emit
DodgedHit(feedback + telemetry). Rolling off ledges is allowed; rolling into a wall stops displacement but i-frames continue.Scope
- In: Roll, backstep, i-frames, roll-attack cancel,
DodgedHitevent - Out: Load-class variations (e04-armor-weight)
- Out: Archer quickstep (e05-mobile-archery)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a roll started on tick 0, when a hit lands on tick 14, then no damage/poise applies and
DodgedHitis emitted; when a hit lands on tick 15, then it applies normally. - AC-2 unit Given no movement input, when dodge is pressed, then a backstep executes costing 12 stamina with i-frames on ticks 2–7 only.
- AC-3 unit Given a roll toward a wall 1 m away, when stepped, then displacement stops at the wall and i-frames still run through tick 14.
- AC-4 unit Given attack pressed at roll tick 26 (inside the buffer), when tick 28 arrives, then the roll-attack move starts.
- AC-5 integration Given a replay of the attacker dummy swinging and a roll timed 1 tick early vs on time, when replayed, then outcomes differ exactly as recorded and hashes are deterministic across 100 runs.
- AC-6 manual Given the sandbox attacker dummy, when three playtesters practise for 3 minutes, then each reports dodges "felt fair" (no hits they believed they had dodged) — any counterexample is logged with a replay file.
Waits on
- mw-e04.2 Swept hitboxes and region-tagged hurtboxes in the sim done
- mw-e04.4 Action timeline FSM with input buffer, cancel windows and local time done
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
- mw-e31.14 Inject a DifficultyConfig of tunable multipliers into the sim done
Closed
Completed in PR #97 (2026-09-29)
area:simclass:allepic:e04milestone:m1- In: Roll, backstep, i-frames, roll-attack cancel,
mw-e04.9Combat sandbox scene with dummy creatures and frame-data overlay
Description
Why
Combat feel is tuned by iteration; a dedicated sandbox makes every timing change testable in seconds and gives e2e tests a stable arena.
What
As a developer, I want a combat sandbox scene with configurable dummies and live frame data so that I can tune and verify timing without playing through levels.
Contents: a training dummy (infinite-health toggle, configurable resistances/poise/hurtbox regions), an attacker dummy that performs a selected move on a metronome (default every 2.0 s; parryable/unblockable toggle) for parry and dodge practice, a frame-data overlay (current move, phase, tick, i-frame/hyperarmor highlights, hitboxes/hurtboxes), DPS/poise readouts, debug slow-motion (sim steps at 0.25× wall time; sim tick count unchanged), and spawners via e33-debug-console.
Scope
- In: Scene, dummies, overlay, slow-mo, console commands
- In: Deterministic scene seed for e2e tests
- Out: Archery range layout (e05-archery-range)
- Out: Real creature AI (e11)
Notes
Scene is loaded via e00-greybox-testbed's scene loader under /dev/combat.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 integration Given the sandbox, when
spawn dummy --poise 60 --resist slash=0.5is entered, then a dummy with those values exists in sim state. - AC-2 integration Given the attacker dummy set to a 2.0 s metronome, when 10 s elapse, then exactly 5 swings start, each on a tick multiple of 120.
- AC-3 e2e Given the sandbox loaded in Chromium, when the frame overlay is toggled, then it shows move id, phase and tick matching sim state and no console errors occur.
- AC-4 unit Given slow-motion 0.25×, when 4 s of wall time pass, then exactly 60 sim ticks have run and frame data is unchanged versus normal speed.
- AC-5 integration Given a dummy with infinite health, when 10,000 damage is dealt, then it never emits
Diedand health display resets after 3 s.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #110 (2026-09-30)
area:toolsclass:allepic:e04milestone:m1mw-e04.10Combat HUD: health and stamina bars with damage feedback
Description
Why
The player must read health and stamina at a glance without numbers cluttering the screen (Zelda-like readability).
What
As a player, I want clear health and stamina bars and a hint of where damage came from so that I can make decisions mid-fight.
Health bar with delayed "chip" segment (lost health shown in a lighter colour that drains after 500 ms); stamina bar that flashes when an action is rejected for stamina; low-health pulse below 25%; off-screen damage direction indicator (arc toward source for 1.5 s); HUD scale from settings. UI reads sim state through the game layer only — no rules in UI.
Scope
- In: Health/stamina bars, chip, rejection flash, low-health pulse, damage direction indicator, HUD scale setting
- Out: Target/lock-on HUD (e04-hud-target)
- Out: Screen shake/vignette (e29-screen-feedback)
- Out: Stealth HUD (stealth group)
Notes
Placeholder UI art until the style bible (e37-style-bible) lands; integration of final UI art is a separate asset bead.
Acceptance criteria
- AC-1 unit Given health 100→70 on one hit, when rendered, then the bar shows 70 filled and a 30-wide chip segment that begins draining at 500 ms and is gone by 1000 ms.
- AC-2 unit Given an
ActionRejected{reason:"stamina"}event, when processed, then the stamina bar enters its flash state for 300 ms. - AC-3 unit Given a hit from a source 120° behind the camera forward, when processed, then the damage indicator arc points to 120° and fades out at 1.5 s.
- AC-4 unit Given health at 24% of max, when rendered, then the low-health pulse is active; at 25% it is not.
- AC-5 e2e Given the combat sandbox, when the player takes a dummy hit, then the DOM/engine HUD health value matches sim health within one rendered frame.
- AC-6 unit Given HUD scale set to 150% in settings, when the HUD mounts, then bar dimensions are 1.5× baseline and remain within the viewport at 1280×720.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #172 (2026-10-03)
area:uiclass:allepic:e04milestone:m1mw-e04.20Creatures attack via the move system with readable telegraphs
Description
Why
Enemies are creatures, not dummies: their attacks must share the player's rules (parry, block, poise) and telegraph clearly for Zelda-like readability.
What
As a designer, I want creature attacks defined as move data with enforced telegraphs so that every creature is parryable, dodgeable and readable by the same rules as the player.
Creature schema references moves; the combat layer exposes
canUseMove,rangeFor(move), cooldowns andinRangequeries for AI (e11). Readability rules: every creature attack has a windup ≥ 18 ticks (300 ms) before active frames;unblockable/grab moves ≥ 30 ticks (500 ms) and must declare a distinct telegraph cue (audio + VFX).TelegraphStartedevent fires on the move'stelegraphTick.Scope
- In: Creature move references, AI query API, telegraph rules/events, greybox skeleton 3-move set for validation
- Out: AI decision-making (e11)
- Out: Bestiary content (e13)
- Out: Creature animation/art
Notes
Greybox skeleton moves: overhead chop (parryable), 2-hit slash (parryable), lunging thrust (unparryable, blockable). Related: bestiary skeleton (e13) should adopt these.
Integration note: Promoted to P0/m1 — required by the E01 grey-box vertical slice.
Acceptance criteria
- AC-1 content Given a creature move with startup 12 ticks, when validated, then it fails the readability rule (minimum 18).
- AC-2 content Given an
unblockablecreature move with no telegraph cue or startup < 30 ticks, when validated, then it fails. - AC-3 unit Given a creature executing a move with telegraphTick 4, when tick 4 runs, then exactly one
TelegraphStartedevent is emitted with the move's cue ids. - AC-4 unit Given a creature move on cooldown 120 ticks, when
canUseMoveis queried at tick 60, then it returns false with reasoncooldown. - AC-5 integration Given the greybox skeleton's overhead chop, when the knight parries it in the window, then the skeleton enters Parried exactly as the dummy does.
Waits on
- mw-e02.20 Build layered animation state machine synced to sim timelines done
- mw-e04.3 Data schema for attack moves: frames, costs, hitboxes, flags, cues done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #142 (2026-10-02)
area:simclass:allepic:e04milestone:m1mw-e04.11Hit-stop freezes attacker and victim briefly on impact
Description
Why
A few frames of freeze on impact is the single biggest contributor to perceived weight in melee combat.
What
As a knight, I want a tiny freeze when my blade connects so that every hit feels like it met resistance.
Per-entity local time freeze (via e04-action-timeline) for attacker and victim only: light 3 ticks (50 ms), heavy 5 (83 ms), charged 6, parry 8 (133 ms), riposte/critical 10. Other entities, projectiles and the world keep running. Overlapping freezes take the max, never the sum; multi-target hits freeze once.
Scope
- In: Freeze durations by hit tier, stacking rule, event for presentation sync (audio/VFX start on first frozen frame)
- Out: Camera shake (e29-screen-feedback)
Notes
Hit-stop is a sim rule because it shifts action timing; it is recorded in replays. Reduce-motion settings affect camera shake, not hit-stop.
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a heavy attack connecting on tick 20, when stepped, then attacker and victim local time are 0 for ticks 20–24 and resume on tick 25.
- AC-2 unit Given a third creature attacking during that freeze, when stepped, then its timeline advances normally.
- AC-3 unit Given one heavy swing hitting three enemies on the same tick, when stepped, then the attacker freezes 5 ticks total, not 15.
- AC-4 unit Given a victim already frozen 3 ticks by one hit and hit by a parry-tier event, when stepped, then remaining freeze becomes 8 ticks (max), not 11.
- AC-5 manual Given A/B builds with hit-stop on vs off, when three playtesters try both blind, then at least two of three prefer hit-stop on for "impact".
Waits on
- mw-e04.4 Action timeline FSM with input buffer, cancel windows and local time done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
Closed
Completed in PR #116 (2026-09-30)
area:simclass:allepic:e04milestone:m1mw-e04.26Socket track content type: author the arcs move hitboxes sweep along
Description
Why
Moves name a socket track (hitbox.track, e.g. knight-sword-arc-light-1) but no track data exists yet. mw-e04.2 added the sim side: SocketTrack (key 0 = pose before the first active tick, key k = active tick k) and hitboxFromMove(move, track, aim), so today callers must build tracks in code.
What
A socket-track content type (src/content/data/socket-track/<id>.json): per-key socket poses (position + rotation) in the attacker frame, validated so a track has 1 … active+1 keys; a ref check that every move's hitbox.track exists; compiled into a lookup the knight attacks (e04.6) and creature attacks use with hitboxFromMove. Author tracks for every move id that names one (knight sword arcs light 1-3, heavy, shield bash, kick, dummy swing).
Notes
Tracks are sim data; the render animation follows them (never the reverse). Keep keys at tick resolution; sweeps interpolate between keys.
Acceptance criteria
- AC-1 unit Given a move whose hitbox.track names a missing track, when content loads, then validation fails naming the move and track.
- AC-2 unit Given a track with more keys than the move's active ticks + 1, when content loads, then validation fails.
- AC-3 integration Given every shipped move with a hitbox, when its track is looked up and swept through its active ticks, then the hitbox sweeps a non-empty arc and each key is finite.
Closed
Completed in PR #96 (2026-09-29)
area:contentarea:simclass:knightepic:e04milestone:m1milestone:m2mw-e04.28Creature attack executor honours dodge i-frames
Description
Why
mw-e04.8 made hits during a dodge's i-frames become DodgedHit in the hit-volume system (hits/system.ts, iframeRule). The creature attack executor (src/sim/combat/attacks/executor.ts, mw-e12.5) still tests its own bounding-sphere hurtboxes and applies damage directly, so a creature attack run through it ignores i-frames.
What
Either route executor hits through the hit-volume system (likely part of mw-e04.20) or have the executor ask the same InvulnerabilityRule and emit DodgedHit instead of damage.
Scope
- In: executor melee and projectile hits respect i-frames
- Out: new dodge rules
Notes
iframeRule(moves) and isInvulnerable in src/sim/combat/dodge/dodge.ts.
Acceptance criteria
- AC-1 unit Given a knight rolling on tick 0, when an executor melee attack's active tick lands on tick 14, then no damage applies and DodgedHit is emitted; on tick 15 it applies.
- AC-2 unit Given a projectile reaching a knight during i-frames, then it is DodgedHit and the projectile behaves as a miss.
Closed
Completed in PR #106 (2026-09-30)
area:simclass:allclass:knightepic:e04milestone:m1milestone:m2mw-e04.30Wake-up i-frames make hit volumes emit DodgedHit, like dodge i-frames
Description
Why
Hit reactions (mw-e04.7) make a knocked-down entity invulnerable for 20 wake-up ticks through a damage-model modifier (wakeIframesModifier), so a hit in that window still emits HitboxHit and a 0-damage DamageApplied. Dodge i-frames (mw-e04.8) instead turn such hits into DodgedHit in the hit-volume system. One invulnerability rule keeps feedback and telemetry consistent.
What
As a developer, I want the hit-volume system's invulnerability rule to include wake-up i-frames so that every invulnerable hit is a DodgedHit whatever made the target invulnerable.
Scope
- In: compose hasWakeIframes (src/sim/combat/reactions) with iframeRule in the hit-volume options; keep wakeIframesModifier for damage that does not come through hit volumes (projectiles, hazards).
- Out: new invulnerability sources.
Notes
hasWakeIframes(world, entity) already has the InvulnerabilityRule shape.
Acceptance criteria
- AC-1 unit Given an entity 5 ticks into its wake-up i-frames, when a hitbox sweeps it, then DodgedHit is emitted and no HitboxHit, DamageApplied or HitReaction follows.
- AC-2 unit Given the same entity one tick after its i-frames, when a hitbox sweeps it, then HitboxHit is emitted and the hit applies.
- AC-3 unit Given a rolling entity inside its dodge i-frames and no knockdown, when swept, then DodgedHit is emitted exactly as before this change.
Waits on
- mw-e04.8 Dodge roll and backstep with invulnerability frames done
Closed
Completed in PR #106 (2026-09-30)
area:simclass:allclass:knightepic:e04milestone:m1milestone:m2mw-e04.31Wire guard breaks and blocked hits into hit reactions
Description
Why
Guard breaks (mw-e04.6) lock the blocker's timeline for 60 ticks and emit GuardBroken, and blocked hits arrive as DamageApplied tagged
blocked/guard-breakwith 0 poise damage. Hit reactions (mw-e04.7) must not flinch on a blocked hit and should turn GuardBroken into a readable guard-break stagger reaction (HitReaction event) for animation and audio.What
Map GuardBroken to a guard-break reaction (60 ticks, no second interruptAction), and skip or use a block-recoil reaction for DamageApplied tagged blocked.
Scope
- In: reaction selection for blocked/guard-break hits
- Out: parry (mw-e04.12)
Notes
Follow-up from mw-e04.6.
Acceptance criteria
- AC-1 unit Given a blocked hit (tag blocked), when reactions resolve, then no flinch interrupts the blocker.
- AC-2 unit Given GuardBroken, when reactions resolve, then a guard-break reaction of 60 ticks is emitted and the timeline is not re-locked.
Closed
Completed in PR #110 (2026-09-30)
area:simclass:knightepic:e04milestone:m1milestone:m2
M2 Class fantasies 10
mw-e04.12Parry window and riposte critical attack
Description
Why
Parrying is the knight's mastery verb: high risk, high reward, readable windows (Knight fantasy: mastery).
What
As a knight, I want to parry a well-timed attack and punish it with a riposte so that learning enemy rhythms is rewarded.
Parry: 30-tick action, 10 stamina, active window ticks 4–13 (10 ticks ≈ 167 ms). A
parryablehit landing in the window: defender takes 0 damage and 0 stamina loss, attacker enters Parried for 90 ticks (1.5 s), 8-tick hit-stop. Failed parry (no hit in window): ticks 14–30 are vulnerable and a hit there is a counter-hit (×1.25 damage). Unparryable moves (parryable: false, grabs, most spells) pass through as normal hits. Riposte: attack pressed while a Parried target is within 2.0 m and within the knight's frontal 60°: critical move (3× base damage, taggedcritical/riposte, ignores poise) with i-frames for its duration. Parry window values are data-driven so progression (e19) and assists (e04-combat-assists) can widen them.Scope
- In: Parry action, window, Parried state, counter-hit rule, riposte critical move
- Out: Parrying projectiles (P3 idea, see Notes)
- Out: Thief backstab critical (thief group; shares
criticaltag and the modifier API)
Notes
Idea for post-MVP: deflecting arrows with a perfect parry. Related: progression upgrades that widen the window (e19).
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given parry pressed on tick 0, when a parryable hit lands on tick 4 or tick 13, then it is parried; on tick 3 or tick 14, then it is not.
- AC-2 unit Given a successful parry, when resolved, then the defender loses 0 health and 0 stamina and the attacker is Parried for exactly 90 ticks.
- AC-3 unit Given a Parried target 1.8 m away in the frontal 60°, when attack is pressed, then the riposte move executes and deals 3× base damage tagged
criticalandriposte. - AC-4 unit Given a Parried target 2.2 m away, when attack is pressed, then a normal L1 executes instead of a riposte.
- AC-5 unit Given a failed parry, when a hit lands on tick 20, then damage is multiplied by 1.25 and tagged
counter. - AC-6 unit Given an
unparryablemove, when it lands inside the parry window, then it applies as a normal unblocked hit. - AC-7 integration Given golden replays of parry-on-first-tick, parry-on-last-tick and parry-one-tick-late, when replayed, then outcomes match the fixtures exactly.
- AC-8 manual Given the sandbox attacker dummy with a 2 s metronome, when three playtesters practise for 5 minutes, then each lands ≥ 5 parries and rates the window "learnable" ≥ 4/5.
Waits on
- mw-e04.11 Hit-stop freezes attacker and victim briefly on impact done
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
- mw-e31.14 Inject a DifficultyConfig of tunable multipliers into the sim done
Closed
Completed in PR #125 (2026-10-02)
area:simclass:knightepic:e04milestone:m2mw-e04.13Heavy and charged attacks with hyperarmor
Description
Why
Heavy and charged attacks give the knight a committed, high-impact option that breaks poise and walls (weight, impact, stagger).
What
As a knight, I want a heavy attack I can charge so that I can gamble time for a stagger or a smashed wall.
Heavy: 24/5/26 ticks, 25 stamina, 1.6× damage, 2.2× poise damage, hyperarmor ticks 10–29 absorbing up to 40 poise. Charged: hold heavy up to 60 ticks (1 s); damage multiplier lerps 1.0→1.8 and poise 2.2→3.5×;
ChargeReadyevent at full charge; auto-release at 90 ticks; total stamina 25 + 10 at full charge. Released before 12 ticks of hold = normal heavy. If hyperarmor is broken during charge, the charge is lost. Every heavy/charged hit carriesimpactForce(used by e04-smash-breakables).Scope
- In: Heavy move, charge hold, multipliers, hyperarmor, impactForce values
- Out: Breaking objects (e04-smash-breakables)
- Out: Charge VFX (e29-weapon-trails) and cue (e28-combat-hit-audio)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given heavy held for 60 ticks and released, when it connects, then damage is 1.8× base and poise damage 3.5× base, and 35 stamina was spent.
- AC-2 unit Given heavy released after 8 ticks of hold, when resolved, then it behaves as an uncharged heavy (1.6×/2.2×).
- AC-3 unit Given heavy held for 90 ticks, when tick 90 arrives, then it auto-releases at full charge.
- AC-4 unit Given a charging knight with hyperarmor cap 40, when a 30-poise hit lands, then no reaction occurs; when a further 15-poise hit lands, then the knight staggers and the charge is lost.
- AC-5 unit Given full charge reached, when the tick completes, then exactly one
ChargeReadyevent is emitted. - AC-6 manual Given the sandbox, when three playtesters use charged attacks against a 60-poise dummy, then each can reliably stagger it and rates the payoff ≥ 4/5.
Waits on
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
area:simclass:knightepic:e04milestone:m2mw-e04.14Shield bash interrupts, shoves and breaks bashable objects
Description
Why
The shield should be a tool, not just a wall: interrupting casters and shoving things fits the knight's physical-world fantasy.
What
As a knight, I want to bash with my shield while blocking so that I can interrupt enemies, break weak guards and shove objects.
Bash (attack while blocking): 14/4/20 ticks, 18 stamina, 5 blunt, 45 poise damage, forward impulse. Interrupts any target currently in a move flagged
interruptible(e.g. spell windups). Against shieldless blockers it causes Guard Break. Applies impulse to physics objects ≤ 60 kg andimpactForcetobreakableobjects (e.g. flimsy barricades). Without a shield equipped, the input falls back to kick (e04-kick-verb).Scope
- In: Bash move, interrupt rule, guard-break-vs-shieldless, physics impulse, impactForce
- Out: Kick (e04-kick-verb)
- Out: Breakable toughness rules (e04-smash-breakables)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a creature in an
interruptiblewindup, when a bash connects, then its move is cancelled and it flinches. - AC-2 unit Given a shieldless creature blocking, when a bash connects, then it is Guard Broken for 60 ticks.
- AC-3 integration Given a 30 kg barrel in front of the knight, when a bash connects, then the barrel is displaced ≥ 1.0 m by physics; given a 100 kg crate, then it does not move and a
ImpactResistedevent fires. - AC-4 unit Given no shield equipped, when block+attack is pressed, then the kick move executes instead.
- AC-5 unit Given 10 stamina, when bash is requested, then it executes (last-action rule) and stamina becomes 0.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e04.6 Knight light attack chain and shield block with guard break done
Closed
Completed in PR #190 (2026-10-03)
area:simclass:knightepic:e04milestone:m2mw-e04.15Weapon reach classes and swing-type wall recoil
Description
Why
Weapon choice should change how you fight in a space — a halberd rules a hall but clangs off corridor walls (positioning, weapon reach).
What
As a knight, I want weapons with different reach and swing types so that my choice of weapon matters in tight corridors versus open halls.
Reach classes (data):
short1.2 m (dagger/mace, fast 8/3/12),medium2.0 m (sword),long3.0 m (spear thrust, halberd sweep). Wall recoil: ahorizontalswing from a medium/long weapon whose hitbox strikessolidstatic geometry during the first half of its active window before any creature → 30-tick recoil,WallRecoilevent (sparks/clang).verticalandthrustswings never recoil. Weapon stats (reach, moveset, damage types) come from item data via e17-inventory-core.Scope
- In: Reach class data, swing types, recoil rule, three starter weapons (sword, spear, mace)
- Out: Weapon loot/placement (e18)
- Out: Weapon models (art)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a spear thrust with 3.0 m reach, when a target stands at 2.8 m, then it is hit; with a sword (2.0 m), then it is not.
- AC-2 integration Given a halberd horizontal sweep in a 2 m-wide greybox corridor, when swung, then
WallRecoilfires and the knight is locked in recoil for 30 ticks. - AC-3 integration Given the same corridor, when a spear thrust or vertical sword swing is used, then no recoil occurs.
- AC-4 unit Given a horizontal sweep that hits a creature before the wall on the same tick, when resolved, then the creature is hit and no recoil occurs.
- AC-5 content Given weapon item data, when validated, then every weapon declares a reach class and a moveset whose moves exist.
Waits on
- mw-e04.2 Swept hitboxes and region-tagged hurtboxes in the sim done
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #362 (2026-11-09)
area:simclass:knightepic:e04milestone:m2mw-e04.16Armor weight sets roll class, stamina regen, poise and noise
Description
Why
Armor must be a trade-off — protection and poise versus agility and stealth — not a bigger number (anti-grind rule).
What
As a knight, I want heavier armor to protect me and hold my ground while slowing my rolls so that I choose a playstyle, not just a stat.
Load ratio = equipped weight / capacity. Light ≤ 30%: roll 3.6 m, i-frames 13, 36 ticks. Medium ≤ 70%: 3.0 m, 11 i-frames, 40 ticks. Heavy ≤ 100%: 2.2 m, 9 i-frames, 46 ticks, stamina regen ×0.85. Overloaded > 100%: no roll (stumble 20 ticks), walk only. Armor pieces add flat per-type absorption and poise bonus. Armor exposes a
noiseMultiplierproperty (plate 1.6, chain 1.3, leather 1.0) consumed by stealth sound propagation.Scope
- In: Load classes, roll variants, regen modifier, absorption/poise from armor, noise multiplier property
- Out: Stealth noise simulation (e09-sound-propagation)
- Out: Armor items and art (e17/e18/art)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given load ratio exactly 30%, when a roll executes, then it uses the light profile; at 30.1%, then the medium profile.
- AC-2 unit Given heavy load, when a roll executes, then it lasts 46 ticks, travels 2.2 m and has 9 i-frame ticks.
- AC-3 unit Given load ratio 105%, when dodge is pressed, then a 20-tick stumble plays with no i-frames and sprint is disabled.
- AC-4 unit Given plate chest with 12 flat slash absorption, when a 30-slash hit lands, then 18 damage applies after resistances.
- AC-5 integration Given plate armor equipped, when the player walks, then emitted footstep noise events carry multiplier 1.6.
Waits on
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e04.5 Stamina pool governs attacks, blocks, dodges and sprint done
- mw-e04.8 Dodge roll and backstep with invulnerability frames done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #356. Starting knight is Medium: roll 40 ticks, 11 i-frames. Clip timing and armour tuning in mw-h97. (2026-11-09)
area:simclass:knightepic:e04milestone:m2mw-e04.18Kick verb shoves creatures and physics objects
Description
Why
Kicking over a brazier into oil or a barrel down stairs is the kind of improvised "wait, I can do that?" moment the world should reward.
What
As a knight, I want to kick objects and enemies so that I can knock braziers over, send barrels rolling and shove foes off ledges.
Kick: 10/3/16 ticks, 8 stamina, 20 poise damage, 3 blunt, impulse to physics objects ≤ 40 kg; small creatures (schema size
small) receive knockback. Kicking an unlocked, closed door swings it open hard: a creature behind it takes blunt damage and flinches. Consequences flow from properties: a kickedburningbrazier carries fire toflammablesurfaces via e03-element-propagation — no bespoke pairings.Scope
- In: Kick move, object impulse, door slam, small-creature knockback
- Out: Fire spread rules (e03-element-propagation)
- Out: Door locking (e03-doors-locks-switches)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 integration Given a 25 kg object, when kicked, then it receives an impulse and moves ≥ 1.5 m on flat ground; given a 50 kg object, then it does not move.
- AC-2 integration Given a
burningbrazier next to aflammablerug, when the brazier is kicked over onto it, then the rug ignites via element propagation within 2 s. - AC-3 integration Given an unlocked closed door with a creature 0.5 m behind it, when kicked, then the door opens and the creature takes blunt damage and flinches.
- AC-4 unit Given a locked door, when kicked, then it stays closed and emits
ImpactResistedplus a noise event. - AC-5 unit Given a
smallcreature at a ledge, when kicked toward it, then it is knocked back over the ledge.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e04.4 Action timeline FSM with input buffer, cancel windows and local time done
- mw-e04.7 Sim-chosen hit reactions: flinch, stagger, knockback, knockdown done
Closed
Completed in PR #316 (2026-11-08)
area:simclass:allepic:e04milestone:m2mw-e04.19Fall, crush and hazard damage through the damage model
Description
Why
Shoving a goblin off a bridge or dropping a chandelier on a troll must work through the same rules as swords (systemic world, multiple solutions).
What
As a player, I want falls, falling objects and hazards to hurt anything they hit so that the environment is a weapon for every class.
Fall damage: none below 4 m, linear from 4 m (0%) to 14 m (100% max health), lethal above 14 m unless landing in deep water. Kinetic impacts: physics objects striking a creature at relative speed > 6 m/s deal blunt = 0.5·m·v² / 20 (tunable) with impactForce. Fire volumes deal 8 fire/s and apply
burningper creature rules. All sources build aDamagePackettaggedenvironmentwith the instigator set to whoever caused it (for credit and AI blame).Scope
- In: Fall damage, kinetic impact damage, hazard volume damage, instigator attribution
- Out: Fire/water propagation rules (e03-element-propagation)
- Out: Trap design (thief group)
Notes
Instigator chains let AI blame the player for a dropped chandelier (Related: e11 AI reactions).
Acceptance criteria
- AC-1 unit Given a 9 m fall, when the entity lands, then it takes 50% of max health as damage.
- AC-2 unit Given a 16 m fall into water ≥ 1.5 m deep, when landing, then no fall damage applies.
- AC-3 integration Given a 40 kg chandelier dropped 5 m onto a creature, when it hits, then blunt damage is dealt with the instigator set to the entity that cut its rope.
- AC-4 unit Given a creature with fire resistance 0 standing in a fire volume, when 3 s pass, then it takes 0 fire damage (immunity respected).
- AC-5 unit Given an object moving at 5 m/s touching a creature, when resolved, then no damage is dealt (below threshold).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e31.14 Inject a DifficultyConfig of tunable multipliers into the sim done
Closed
Completed in PR #107 (2026-09-30)
area:simclass:allepic:e04milestone:m2mw-e04.21Lock-on reticle, target health/poise bar and hit markers
Description
Why
Players need to see who they are locked onto and whether their hits matter (poise, immunity) without number spam.
What
As a player, I want a lock-on reticle and a target health/poise readout so that I know my target and how close it is to breaking.
Reticle on the lock-on target from e02-camera; target health bar with chip; poise bar appears after first poise damage and flashes on
PoiseBroken; "no effect" indicator whenDamageApplied.immune; boss bar docked at bottom for creatures flaggedboss; hit marker variants (normal, weak point, headshot, deflected). Damage numbers exist as an option, off by default.Scope
- In: Reticle, target bars, immune indicator, boss bar, hit markers, damage-number option
- Out: Lock-on targeting logic (e02-camera)
- Out: Final UI art (asset beads)
Notes
Hit marker variants are also used by archery (e05-weak-points).
Acceptance criteria
- AC-1 unit Given a lock-on target, when rendered, then the reticle is anchored to its lock-on socket and hidden when no target exists.
- AC-2 unit Given the target dies, when
Diedis processed, then the reticle and target bar hide within one rendered frame. - AC-3 unit Given
DamageApplied.immuneon the target, when processed, then a "no effect" indicator shows for 800 ms and no chip appears. - AC-4 unit Given a creature flagged
bossenters combat, when processed, then the boss bar shows its display name and health at the bottom of the screen. - AC-5 unit Given damage numbers disabled (default), when damage occurs, then no numbers render; enabled, then the number equals the applied total.
Waits on
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e04.10 Combat HUD: health and stamina bars with damage feedback done
Closed
Completed in PR #324 (2026-11-08)
area:uiclass:allepic:e04milestone:m2mw-e04.39Adopt shoulder-button combat gamepad layout
Description
Implement the owner-selected controller layout: R1/RB right-hand attack, R2/RT strong attack, L1/LB left-hand action (shield parry with current equipment), L2/LT held shield block, and R3/RS crouch. Move lock-on off R3 to Triangle/Y, update HUD/docs/tests, and verify in the demo.
Acceptance criteria
Default pad bindings have no conflicts; combat actions fire from the requested physical controls; the HUD and controls documentation match; focused tests and typecheck pass.
Closed
Implemented the owner-selected controller layout, updated runtime HUD and controls docs, added unit/integration coverage, and passed 42 focused tests, typecheck, lint, and build. (2026-10-03)
area:simclass:knightepic:e04milestone:m2mw-e04.34Wire environmental damage and character impulses into the game world
Description
Why
mw-e02.15 / mw-e04.19 shipped the sim rules (applyCharacterImpulse, installCharacterImpulses, installEnvironmentDamage with src/content/data/environment-damage/default.json) but the game world (src/game, testbed player) installs no damage model yet, so none of it runs in play. Also: characters have no spatial.placement, so only contact-shaped force stimuli reach them; area Gusts/explosions need character placements kept in step with the controller.
What
When the player/creatures get a damage model in the game world, install installCharacterImpulses + installEnvironmentDamage (capsule from controller tuning, surface = physicsSurface) and keep characters' placements synced so shaped stimuli reach them.
Discovered from mw-e02.15 / mw-e04.19.
Acceptance criteria
- AC-1 integration Given the testbed with the player, when a sphere force stimulus goes off next to them, then the player is launched.
- AC-2 e2e Given the testbed, when the player drops off a ledge higher than 4 m-equivalent, then fall damage shows on the HUD.
Closed
Completed in PR #119 (2026-09-30)
area:gamearea:simclass:knightepic:e04milestone:m2
E05ArcheryBows feel excellent; arrows have physical presence (gravity, drag, sticking, retrieval), weak points and elevation matter, and trick arrows (fire, water, rope, noise, blunt) solve problems through world properties rather than scripted pairings.7/21 done · 33%
M2 Class fantasies 7
mw-e05.1Data schema for arrow types and property-based payloads
Description
Why
Trick arrows must act through world properties, not scripted pairings, so a new arrow is data and interactions emerge (architectural principle).
What
A zod schema for
ArrowDef:id,mass_g,dragK, baseDamagePackettemplate,penetration,onImpact(stick|bounce|shatter),retrievable, and apayloadlist of property emissions using the e03 vocabulary, e.g.{applyProperty:"burning", radius:0.3},{applyProperty:"wetness", radius:1.5},{spawn:"rope", length:8},{noise:{loudness:70, duration:2}},{impactForceMul:2.5}. Initial data: standard, broadhead, fire, water, rope, noise, blunt.Scope
- In: Schema, validators, initial arrow data file
- Out: Projectile behaviour (e05-arrow-projectile)
- Out: Payload effects (per trick-arrow items)
Notes
No payload may name a specific target type; validators reject target-specific keys.
Acceptance criteria
- AC-1 content Given the initial arrow file, when validated, then all seven arrows pass and each payload property exists in the e03 world-property vocabulary.
- AC-2 content Given a payload referencing an unknown property (e.g.
applyProperty:"sparkly"), when validated, then it fails with the path. - AC-3 content Given an arrow with negative mass or dragK, when validated, then it fails.
- AC-4 content Given a payload key that targets a specific entity type (e.g.
onlyAffects:"rope"), when validated, then it is rejected as non-systemic.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
Closed
Completed in PR #118 (2026-09-30)
area:contentclass:archerepic:e05milestone:m2mw-e05.2Deterministic arrow ballistics with gravity, drag and sticking
Description
Why
Arrows need physical presence — arcs, drop, sticking in wood, glancing off stone — to make archery precise and learnable.
What
Sim-side projectile integration at 60 Hz: semi-implicit Euler, gravity 9.81 m/s², quadratic drag
a = -k·v·|v| / m; orientation follows velocity. Each tick a swept segment test runs against hurtboxes (shared e04-hit-volumes query) and world colliders. On hit: damage scales with (v/v0)² clamped to [0.3, 1.0]; stick/penetrate ifpenetration≥ surface hardness, else ricochet (reflect with 40% speed) or drop. Airborne lifetime 10 s max. Shooter is immune to its own arrow for its first 30 ticks, then can be hit (an arrow shot straight up can come back down).Scope
- In: Integration, swept collision, speed-scaled damage, stick/ricochet/drop, lifetime, self-hit rule
- Out: Bow input and draw (e05-bow-core)
- Out: Arrow persistence/pickup (e05-arrow-retrieval)
Notes
Surface hardness comes from the
surfaceHardnessworld property (e03-world-properties: soft, medium or hard, set by every material preset), rated for penetration as soft 5 (soil, flesh), medium 10 (wood, bone), hard 40 (stone, metal, glass).Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a 60 m/s launch at 30° with k = 0 from ground level, when simulated, then range is within ±1% of the analytic value (≈ 318 m).
- AC-2 unit Given drag k from the standard arrow, when simulated, then landing point matches a reference RK4 table within ±0.5 m at 50 m.
- AC-3 unit Given an 80 m/s arrow and a 5 cm plank collider, when stepped, then the hit registers (no tunneling).
- AC-4 unit Given an arrow with penetration 20 hitting stone (hardness 40), when it hits, then it ricochets at 40% speed and deals no damage.
- AC-5 unit Given an arrow shot straight up at 40 m/s, when it falls back through the shooter after 30 ticks, then the shooter is hit; within the first 30 ticks, then not.
- AC-6 integration Given a replay of 20 arrows fired at moving dummies, when replayed 100 times, then state hashes are identical every tick.
- AC-7 perf Given 60 arrows in flight, when stepped, then projectile update ≤ 0.4 ms per tick p95 on the reference machine.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e04.2 Swept hitboxes and region-tagged hurtboxes in the sim done
- mw-e05.1 Data schema for arrow types and property-based payloads done
Closed
Completed in PR #124 (2026-09-30)
area:simclass:archerepic:e05milestone:m2mw-e05.6Headshots, weak points and armored regions that deflect
Description
Why
"Weak points matter" — precise shots on heads and exposed cores should change fights, and armor should be something you shoot around (Archer fantasy; creature weaknesses).
What
As an archer, I want headshots and weak points to matter and armor to deflect weak arrows so that where I hit is as important as whether I hit.
Hurtbox regions from e12-creature-schema:
head×2.0 (most humanoids),weakpoint×3.0 with ×2 poise damage (can be enabled/disabled by creature state, e.g. a core exposed only while roaring),armoredregions with an armor value: arrows with penetration < armor ricochet (no damage), blunt arrows ignore deflection but deal ×0.5. A headshot on an unaware humanoid causes an instant stagger. HUD hit markers distinguish normal / headshot / weak point / deflected. Melee uses the same regions.Scope
- In: Region multipliers in the damage pipeline, state-toggled weak points, deflection rule, unaware headshot stagger, hit-marker events
- Out: Creature region authoring (e12/e13)
- Out: Stealth unaware state (e11-alert-states — read only)
Notes
Timing convention: sim runs at a fixed 60 Hz (1 tick = 16.67 ms, per e00-sim-core). All frame numbers are sim ticks; ms values are for readability only.
Acceptance criteria
- AC-1 unit Given a humanoid with head ×2.0, when a 20-pierce arrow hits the head, then 40 damage applies and the hit is tagged
headshot. - AC-2 unit Given a weakpoint currently disabled, when an arrow hits its hurtbox, then it resolves against the underlying region instead.
- AC-3 unit Given an
armoredregion with armor 30 and an arrow with penetration 20, when it hits, then it deflects with 0 damage and aDeflectedevent; a blunt arrow deals ×0.5 instead. - AC-4 unit Given an unaware humanoid, when hit in the head, then it staggers regardless of poise.
- AC-5 content Given creature data, when validated, then every creature with a
weakpointregion declares the state(s) in which it is enabled.
Waits on
- mw-e04.2 Swept hitboxes and region-tagged hurtboxes in the sim done
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #327 (2026-11-08)
area:simclass:archerepic:e05milestone:m2mw-e05.7Stuck arrows persist and can be retrieved or looted
Description
Why
Arrows with physical presence that you walk over and pull from a door make the world feel real and keep ammo pressure fair.
What
As an archer, I want to pull my arrows back out of walls and fallen enemies so that ammo feels physical and I am not punished for practising.
Stuck arrows persist as world entities (cap 64 per area; oldest non-visible despawn first). Interact (e02-interaction) near stuck arrows collects all intact arrows within 1.0 m. Intact chance decided at impact with sim RNG: soil/wood/flesh 90%, stone 40%, metal 20%, burned (fire arrow or burning target) 0% — broken arrows render as broken. Arrows in creatures move with them and drop into their loot on death. Arrows stuck in moving physics objects move with them. Spent trick arrows return as standard arrows if intact.
Scope
- In: Persistence, cap/despawn policy, pickup, intact roll at impact, corpse loot transfer
- Out: Inventory UI (e17)
- Out: Save of stuck arrows beyond the current area (not persisted)
Notes
Rope arrows are retrieved only at their anchor and return as rope arrows.
Acceptance criteria
- AC-1 unit Given an arrow hitting wood with the seeded RNG roll 0.5, when resolved, then it is intact (0.5 < 0.9) and
retrievableis true. - AC-2 integration Given three intact arrows stuck within 1.0 m, when the player interacts, then quiver count increases by 3 and the entities are removed.
- AC-3 unit Given 65 stuck arrows in an area, when the 65th sticks, then the oldest arrow not in view is despawned.
- AC-4 integration Given a creature with 2 arrows in it, when it dies, then its loot contains those arrows that were intact.
- AC-5 unit Given an arrow stuck in a target that later ignites, when the fire reaches it, then it becomes broken and non-retrievable.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #360 (2026-11-09)
area:simclass:archerepic:e05milestone:m2mw-e05.13Shootable triggers and cuttable ropes activate mechanisms
Description
Why
"The archer might shoot a distant mechanism." Shooting targets, bells and ropes opens routes at range (Classes change possibilities; puzzle constitution).
What
As an archer, I want to shoot distant targets and ropes so that I can open gates, drop chandeliers and trigger mechanisms from afar.
Objects with the
shootableproperty fire an e03 switch signal when any projectile hits with impulse ≥ their threshold. Objects withcuttable(ropes, cords) are severed by pierce/slash damage landing on them; anything they suspend becomes a free physics object. Fully property-driven: thrown daggers or spell projectiles that produce the same packet also work.Scope
- In: Shootable signal emission, cuttable severing, examples in the archery range
- Out: Mechanism wiring (e03-doors-locks-switches)
- Out: Level placement (e26 dungeon)
Notes
Related: puzzle group (e15-puzzle-kit) consumes shootable/cuttable in puzzle designs.
Acceptance criteria
- AC-1 integration Given a
shootabletarget wired to a gate, when an arrow hits it with impulse ≥ threshold, then the gate receives the switch signal and opens. - AC-2 integration Given a chandelier hanging from a
cuttablerope, when a broadhead arrow hits the rope, then the rope severs and the chandelier falls as a physics object. - AC-3 integration Given the same rope, when a blunt arrow hits it, then it does not sever (blunt only).
- AC-4 unit Given a
shootabletarget, when a projectile hits below threshold impulse, then no signal is emitted. - AC-5 integration Given a non-arrow projectile producing a pierce packet, when it hits a
cuttablerope, then the rope severs identically.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e03.20 Trigger distant mechanisms with projectiles, impacts and force done
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
Closed
Completed in PR #325 (2026-11-08)
area:simclass:archerepic:e05milestone:m2mw-e05.19Bind arrow flight and impact events to the cue sheets for audio and VFX
Description
Why
Arrow impacts must be heard and seen: a thunk into wood, a clang off stone, a flyby past your head. The sim now emits them (mw-e05.2) but no cue sheet can bind them yet.
What
Add
ArrowFiredandarrowImpact(src/sim/combat/arrows/events.ts, which follow physicsImpact's conventions: entity, other, material, hardness, outcome, energy, impulse, speed, normal, position) to the cue event table (src/content/cue-events.ts) and the game's cue bindings (src/game/cues/events.ts), with the arrow id, outcome and surface impact class as facts, so audio (e28) and VFX (e29.11) cue sheets can pick sounds and puffs by arrow and surface.Scope
- In: cue event specs and bindings for arrow fired/impact, contract tests.
- Out: the sounds themselves (e38.28–31), arrow VFX (e29.11).
Discovered from mw-e05.2.
Acceptance criteria
- AC-1 unit Given an arrowImpact on stone, when bound, then the cue event carries anchors entity/other/at and facts arrow, outcome, surface impact class, energy and speed.
- AC-2 unit Given a cue sheet rule on arrowImpact with outcome ricochet, when a standard arrow ricochets off stone in a sim run, then the rule fires once.
- AC-3 unit Given ArrowFired, when bound, then the cue event is anchored at the arrow and carries the arrow id.
Waits on
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
Closed
Completed in PR #128 (2026-10-02)
area:contentarea:simclass:archerepic:e05milestone:m2mw-e05.21Wire the bow and arrows into the game world with the aim camera
Description
Why
The bow rules (mw-e05.3) and arrow ballistics (mw-e05.2) run in the sim, but nothing in the game world flies arrows yet, so the archer fantasy (precision at range) cannot be felt or tested in a browser.
What
As an archer, I want to take out the bow in the testbed and shoot so that draw, aim and release can be felt and tuned.
Install the arrow system (installArrows) in the game world with the physics collision query; give the testbed player the shortbow and a quiver (installPlayer
combat.bow); render loosed and stuck arrows as grey-box shafts; ease the orbit camera's FOV from 70 deg to the bow'saim.fov(55 deg) overaim.timewhileisDrawing, and back out on release; show the selected arrow type and quiver count in the?framesoverlay.Scope
- In: game/render wiring of arrows and the bow, aim-mode camera FOV, quiver readout.
- Out: sway and reticle (mw-e05.5), the archery range (mw-e05.14), final arrow art and VFX (mw-e29.11).
Notes
Discovered from mw-e05.3. The sim side (BowComponent, bowSystem, lookAim, bowLocomotionScale) is done; this is glue in src/game, src/render and src/main.ts.
Acceptance criteria
- AC-1 integration Given the testbed player with the shortbow, when the toggle is pressed and fire is held 48 ticks and released, then an arrow entity flies from the nock point and an ArrowFired event is emitted.
- AC-2 unit Given the bow drawn, when the camera updates, then its vertical FOV eases from 70 deg toward the bow's aim.fov (55 deg), reaching about two thirds of the way in aim.time seconds, and eases back after release.
- AC-3 e2e Given the testbed in Chromium, when Playwright takes out the bow and looses an arrow at a wall, then the arrow renders stuck in the wall and no console errors occur.
- AC-4 unit Given an empty quiver for the selected type, when fire is pressed, then the overlay shows 0 for that type and no arrow renders.
Closed
Completed in PR #132 (2026-10-02)
area:gamearea:simclass:archerepic:e05milestone:m2
E06Magic FrameworkA data-driven spell architecture where every spell is a content file composed of a delivery (self, projectile, AoE, beam, touch, summon point) and a list of effect operations (damage, world-property stimulus, force, status, summon, transform, displace). Spells never script pairwise interactions: they emit world stimuli (heat, cold, force, light, moisture, charge, life, decay, sound) into the systemic engine (E03), which resolves outcomes through object properties. Casting has a mana resource, cast times, channeling, interruption and botched casts that create new situations.14/25 done · 56%
M2 Class fantasies 14
mw-e06.1Define zod spell schema with delivery, effect ops and cue ids
Description
Why
Data-driven spells (technical constitution) require a single validated shape so designers add spells as content, not code.
What
A zod schema in src/content/spells for SpellDefinition: id, school, tier/verb-chain links, cost (mana), castTime, channel, cooldown, delivery {self|touch|projectile|aoe|beam|summonPoint}, effects[] (discriminated union of effect ops), tags, vfxCue, sfxCue, bookId, flags (e.g. tooUseful). Validator produces readable errors with file + JSON path.
Scope
- In: schema, types, validator, content test harness registration
- In: fixture spells for tests
- Out: runtime execution (e06-spell-pipeline)
- Out: real spells (mw-e08)
Notes
Effect op union starts with: damage, stimulus, force, status, summon, transform, displace, spawnVolume, light, noise. New ops are code; new spells are data. Unknown ops must fail validation, not be ignored.
Acceptance criteria
- AC-1 content Given a valid fixture spell file, when validated, then it parses into a typed SpellDefinition with no errors.
- AC-2 content Given a spell with an unknown effect op "teleportEverything", when validated, then validation fails naming the file and JSON path effects[0].op.
- AC-3 content Given a spell with negative mana cost or castTime, when validated, then validation fails with a range error.
- AC-4 unit Given two spell files with the same id, when the catalogue loads, then loading fails with a duplicate-id error listing both files.
- AC-5 unit Given a spell whose vfxCue or sfxCue is missing, when validated, then validation fails (placeholders must still be named).
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
Closed
Completed in PR #88 (2026-09-29)
area:contentclass:sorcererepic:e06milestone:m2mw-e06.2Run data-driven spells through a cast→deliver→effect pipeline
Description
Why
Magic should feel magical and scale combinatorially; one generic pipeline is what lets 20 spells interact without bespoke code.
What
As a designer, I want any SpellDefinition to execute through one pipeline so that new spells need only data.
The sim resolves a cast request into: validate (known, resources, cooldown) → cast phase → delivery entity → hit/area resolution → apply effect ops in order to targets and the world. Each op is a registered pure handler; effects are sim events on the event bus that render/audio subscribe to.
Scope
- In: src/sim/magic pipeline, op registry, delivery entity lifecycle, events (SpellCast, SpellHit, SpellEffectApplied, SpellFizzled)
- In: self and touch delivery (other deliveries in their own beads)
- Out: mana and cast timing (e06-mana-resource, e06-cast-lifecycle)
- Out: projectile/AoE/beam delivery (own beads)
- Out: UI and input (e06-spell-hotbar-ui, e06-casting-input-aiming)
Notes
No Math.random/Date.now; use injected seeded RNG and clock from e00-sim-core. Op handlers must not branch on spell id or target id — enforce via a lint rule or test that greps src/sim/magic for literal spell ids.
Acceptance criteria
- AC-1 unit Given a fixture self-delivery spell with a status op, when cast, then SpellCast then SpellEffectApplied events are emitted in that order within the same tick and the caster has the status.
- AC-2 unit Given a fixture spell with three effect ops, when it hits a target, then ops apply in declared order and each emits one SpellEffectApplied event.
- AC-3 unit Given a cast request for a spell the caster does not know, when processed, then the cast is rejected with reason "unknown-spell" and no resources are spent.
- AC-4 integration Given a recorded input log casting 50 fixture spells, when replayed twice via the replay harness, then final sim state hashes are identical.
- AC-5 unit Given a new op handler registered under an existing op name, when registering, then registration throws (no silent override).
- AC-6 content Given a spell composed only of existing ops, when added as a new data file, then it casts successfully with zero src/sim changes (fixture test).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e06.1 Define zod spell schema with delivery, effect ops and cue ids done
Closed
Completed in PR #264 (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.3Track mana pool, regeneration and spell costs
Description
Why
A casting resource makes spell choice meaningful and lets running dry create tense moments rather than spam.
What
As a sorcerer, I want a mana pool that drains when casting and regenerates over time so that I choose spells deliberately.
Mana: current/max, regen per second with a delay after casting, cost reservation for channeled spells, over-cast (casting with insufficient mana) routed to the miscast system rather than a flat refusal when the spell allows it.
Scope
- In: mana component, regen, spend/refund, HUD-facing events
- In: config data for base values
- Out: HUD bar visuals (mw-e06 hotbar UI shows mana)
- Out: mana potions (items epic, Related: E17 consumables)
Notes
Stat increases to max mana are allowed but must not be the main progression (anti-grind rule).
Acceptance criteria
- AC-1 unit Given 50/100 mana and a 20-cost spell, when cast completes, then mana is 30 and a ManaChanged event is emitted.
- AC-2 unit Given regen 5/s and a 1.5 s post-cast delay, when 3.5 s elapse after a cast, then exactly 10 mana has regenerated.
- AC-3 unit Given 10 mana and a 20-cost spell that disallows over-cast, when cast is attempted, then it is rejected with reason "insufficient-mana" and mana is unchanged.
- AC-4 unit Given 10 mana and a 20-cost spell that allows over-cast, when cast, then a miscast roll is requested with severity proportional to the 10-mana deficit and mana goes to 0.
- AC-5 unit Given a cast interrupted during its cast phase, when interrupted, then 50% of the cost is refunded (configurable).
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
Closed
Completed in PR #267 (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.4Support cast times, channeling, cooldowns and interruption
Description
Why
Cast timing makes sorcerer combat about positioning and reading enemies, and interruption creates risk and stories.
What
As a sorcerer, I want spells to have wind-up, channel and cooldown timings that enemies can interrupt so that casting is a skill, not a button press.
States: idle → winding (castTime) → released | channeling (tick every N ms, drains mana) → recovery. Interruption sources: poise break from e04 damage model, being grappled/restrained, silence status, caster movement rules per spell (castWhileMoving flag).
Scope
- In: cast state machine, cooldown tracking, interruption rules, cancel by player
- Out: animation (render/game layer)
- Out: botched cast outcomes (e06-botched-casts)
Notes
Interruption during the final 20% of wind-up should be eligible for a miscast instead of a clean fizzle (hook for e06-botched-casts).
Acceptance criteria
- AC-1 unit Given a spell with castTime 800 ms, when cast begins, then no delivery is spawned before 800 ms of sim time and exactly one at 800 ms.
- AC-2 unit Given a channeled spell ticking every 250 ms at 4 mana/tick, when channeled for 1 s, then 4 ticks apply and 16 mana is drained.
- AC-3 unit Given a caster winding up, when it receives a hit that breaks poise, then the cast enters interrupted state, emits SpellInterrupted and spawns no delivery.
- AC-4 unit Given a spell with cooldown 5 s, when recast after 4.9 s, then it is rejected with reason "cooldown" and after 5.0 s it succeeds.
- AC-5 unit Given castWhileMoving=false, when the caster moves more than 0.5 m during wind-up, then the cast is cancelled without miscast and with refund.
- AC-6 unit Given interruption in the last 20% of wind-up, when interrupted, then a miscast request is emitted instead of a clean fizzle.
Waits on
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e06.3 Track mana pool, regeneration and spell costs done
Closed
Completed in PR #272 (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.5Emit world-property stimuli from spell effects into the world sim
Description
Why
The constitution's top architectural rule: spells interact with the world via properties, never "Fireball + Rope" scripts.
What
As a designer, I want spell effects to emit typed stimuli (heat, cold, force, light, moisture, charge, life, decay, sound) with magnitude, radius and duration so that the E03 engine decides what burns, freezes, conducts, grows or rots.
The stimulus op writes into the E03 element-propagation / property system at a point, along a ray, or in a volume. Persistent spell volumes (fire wall, ice sheet, rain) register as ongoing stimulus sources.
Scope
- In: stimulus op handler, stimulus vocabulary mapping to E03 property rules, persistent spell-sourced stimulus volumes
- In: test fixtures with generic property-tagged objects (flammable crate, wet floor, conductive chain, fragile vase)
- Out: the propagation rules themselves (e03-element-propagation)
- Out: light field integration (e06-spell-light-noise-emission)
Notes
Tests must use property-tagged generic fixtures, never named props. If a desired behaviour needs a new stimulus type, add it to the vocabulary here and the rule in E03 — Related: coordinate with Core group for any new property.
Acceptance criteria
- AC-1 integration Given any object tagged flammable inside a heat stimulus ≥ its ignition threshold, when 1 sim second passes, then it is burning.
- AC-2 integration Given any wet or
waterSurfacetile inside a cold stimulus ≥ freeze threshold, when applied, then it gains the frozen property for the stimulus duration plus melt time. - AC-3 integration Given a charge stimulus hitting a conductive object that touches other conductive or wet objects, when applied, then charge propagates to connected objects per E03 rules.
- AC-4 unit Given a stimulus op with radius 0 and no ray, when validated, then content validation fails ("stimulus needs point radius, ray or volume").
- AC-5 integration Given a heat stimulus and a cold stimulus overlapping a burning object in the same tick, when resolved, then the result is deterministic and identical across two replays.
- AC-6 unit Given src/sim/magic, when scanned by the property-only lint test, then no code references object ids or spell ids in interaction logic.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.3 Inject heat, cold, water, charge, force and gas via one stimulus API done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
Closed
Completed in PR #270 (AC-2/AC-3 re-scoped to mw-e06.22, mw-e06.23) (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.6Deliver spells as physical projectiles with collision and arcs
Description
Why
Projectiles are the most common spell delivery and must feel precise and physical for sorcerer combat.
What
As a sorcerer, I want projectile spells with speed, gravity, lifetime, pierce and bounce parameters so that bolts, lobbed orbs and ricochets all come from data.
Projectile collides with hurtboxes (damage model) and world geometry; on impact it applies on-hit ops and optional impact AoE; it carries its stimulus along its path (e.g. heat trail) if configured.
Scope
- In: projectile delivery entity, sweep collision (no tunnelling), impact ops, pierce/bounce counts, lifetime expiry ops
- Out: homing (post-MVP, Related: future spell)
- Out: arrows (e05-bow-core)
Notes
Share collision sweep utilities with e05-bow-core if available; do not depend on it.
Acceptance criteria
- AC-1 unit Given a projectile at 60 m/s and a 0.1 m wall, when simulated at the fixed timestep, then it impacts the wall (no tunnelling) in 100% of 1000 randomized seeded trials.
- AC-2 unit Given gravity 9.8 and launch angle 30°, when simulated, then the impact point is within 5 cm of the analytic ballistic solution.
- AC-3 unit Given bounce=2, when the projectile hits walls three times, then it bounces twice and applies impact ops on the third.
- AC-4 unit Given lifetime 3 s and no impact, when 3 s pass, then onExpire ops apply at its position and the entity is removed.
- AC-5 integration Given a projectile with a heat trail stimulus passing over a flammable object, when it passes within 0.5 m, then the object receives heat per E03 rules.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
Closed
Completed in PR #268 (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.8Apply spell forces: impulse, knockback, pull and lift
Description
Why
"Throw enemies through furniture" is a core magic fantasy; forces must work identically on creatures and loose objects.
What
As a sorcerer, I want force ops (radial push, directional impulse, point pull, sustained lift) so that I can hurl, drag and float things.
Force respects weight: magnitude / mass → displacement; creatures convert excess force into stagger/knockdown via the damage model; objects colliding at speed apply impact damage and break fragile things.
Scope
- In: force op handler for rigid bodies and creatures, mass scaling, knockdown thresholds, impact damage on collision
- Out: telekinetic grab/hold (Mage Hand spell uses a sustained-lift + tether op defined here but tuned in mw-e08)
Notes
Heavy objects (weight property above threshold) should visibly resist rather than ignore force.
Acceptance criteria
- AC-1 unit Given a 10 kg and a 40 kg object at equal distance from a radial push of 200 N·s, when applied, then the lighter object's velocity is 4× the heavier's.
- AC-2 integration Given a creature pushed into any object tagged fragile at ≥ 6 m/s, when they collide, then the object breaks and the creature takes collision damage.
- AC-3 unit Given a creature with poise 50 hit by force equivalent to 70 poise damage, when applied, then it is knocked down.
- AC-4 unit Given an object tagged anchored or weight above the max-liftable threshold, when a lift op is applied, then it does not leave the ground and a ForceResisted event is emitted.
- AC-5 unit Given a point pull, when targets reach the pull centre, then they stop within 0.5 m instead of oscillating (damped).
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
Closed
Completed in PR #307 (2026-11-08)
area:simclass:sorcererepic:e06milestone:m2mw-e06.9Apply buffs, debuffs and statuses with stacking and dispel
Description
Why
Restrain, levitate, slow, silence and ward effects are how many spells change situations without damage.
What
As a designer, I want a data-driven status system (duration, stack rule, tick ops, modifiers, tags, break conditions) so that spells like Grasping Vines or Levitate are composed rather than coded.
Statuses: rooted, slowed, levitating, silenced, frozen-solid, burning (mirrors world state), warded, revealed, invisible-to-senses (hook for AI).
Scope
- In: status definitions in content, apply/refresh/stack/expire, break conditions (e.g. rooted breaks when the root object burns), dispel by tag
- Out: transformations (e06-transformation-framework)
- Out: AI reaction to statuses (E11/E12)
Notes
Break conditions must be property-driven: "rooted breaks if the restraining entity loses the intact property" rather than "vines break on fire".
Acceptance criteria
- AC-1 unit Given a status with stack rule "refresh", when applied twice 2 s apart with 5 s duration, then it expires 7 s after first application.
- AC-2 unit Given a status with stack rule "stack(max 3)", when applied 4 times, then stack count is 3.
- AC-3 unit Given a rooted status whose source entity is destroyed, when the source is removed, then the status ends in the same tick.
- AC-4 unit Given a dispel of tag "magical", when applied to a target with 2 magical and 1 mundane status, then only the 2 magical statuses are removed.
- AC-5 content Given a status definition referencing an unknown modifier stat, when validated, then validation fails.
- AC-6 integration Given a silenced caster, when it attempts a cast, then the cast is rejected with reason "silenced".
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
Closed
Completed in PR #296 (2026-11-07)
area:simclass:sorcererepic:e06milestone:m2mw-e06.14Make spells emit light and noise that stealth and AI perceive
Description
Why
Magic must feed the same world systems as the thief: a glowing spell reveals you, a Thunderclap wakes the dungeon.
What
As a sorcerer, I want spells to contribute light to the light field and noise events to sound propagation so that casting has stealth consequences and light spells solve darkness.
Light op: intensity, radius, colour tag, duration, attach-to (caster/projectile/point). Noise: per-phase loudness (wind-up, release, impact).
Scope
- In: light op writing to e03-light-field, spell noise events into e09-sound-propagation, per-phase loudness data
- Out: visual lighting (render)
- Out: AI reaction logic (E11)
Notes
Light spells should also be usable by creatures (light-averse creatures flee via their own property rules in E12).
Acceptance criteria
- AC-1 integration Given a dark room with light level 0.05, when a light op of intensity 0.8 radius 6 m is placed at its centre, then the light field query at 3 m returns ≥ 0.4.
- AC-2 integration Given a projectile carrying a light op, when it moves, then the light field updates at its position each tick and clears at its previous position within 1 tick.
- AC-3 integration Given a spell with impact loudness 90, when it impacts, then a noise event with loudness 90 is emitted at the impact position.
- AC-4 unit Given a light op with duration 10 s, when 10 s pass, then its light field contribution is removed.
- AC-5 unit Given a spell with no noise data, when validated, then it defaults to school default loudness rather than silence.
Waits on
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
Closed
Completed in PR #271 (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.15Aim and cast spells with reticle, ground targeting and cancel
Description
Why
Sorcerer combat must feel precise and responsive with keyboard and mouse first.
What
As a sorcerer, I want to aim projectiles with a reticle, place AoEs with a ground marker, hold to channel and cancel with a key so that casting feels deliberate.
Game layer binds input actions → sim cast requests; targeting modes per delivery (reticle, ground, self, target-lock); invalid target feedback (red marker) before committing.
Scope
- In: game-layer casting controller, targeting modes, placeholder ground marker, input bindings through the action map, gamepad fallback
- Out: hotbar/selection (e06-spell-hotbar-ui)
- Out: final reticle art (asset beads)
Notes
Latency budget: input-to-cast-start ≤ 100 ms (one render frame + sim tick).
Acceptance criteria
- AC-1 integration Given a projectile spell selected, when the cast action is pressed, then a cast request with the camera-reticle aim ray is sent to the sim within the same frame.
- AC-2 integration Given a ground-targeted AoE and an aim point beyond max range, when previewed, then the marker clamps to max range and is flagged invalid if no floor exists.
- AC-3 integration Given a channeled spell, when the cast action is released, then channeling stops within one sim tick.
- AC-4 e2e Given the greybox testbed, when a scripted player selects, aims and casts a fixture projectile at a dummy, then the dummy registers a hit.
- AC-5 perf Given the reference machine, when measuring input-to-cast-start, then p95 ≤ 100 ms.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e06.4 Support cast times, channeling, cooldowns and interruption done
Closed
Completed in PR #278 (2026-10-24)
area:gameclass:sorcererepic:e06milestone:m2mw-e06.16Select spells from a hotbar and quick-select wheel with mana HUD
Description
Why
Combining spells needs fast switching; a sorcerer with 15 spells cannot rely on a menu.
What
As a sorcerer, I want 8 hotbar slots plus a hold-to-open quick-select wheel and a visible mana bar so that I can switch spells mid-fight.
Shows icon (placeholder), cost, cooldown sweep, unaffordable state, channel indicator; drag from grimoire to slot; slot state persists in saves.
Scope
- In: hotbar UI, quick-select wheel (slows sim time to 25% while open, configurable), mana bar, keybinds 1–8, gamepad wheel
- Out: grimoire (e07-grimoire-ui)
- Out: final icon art (asset prompt beads per spell)
Notes
Time-slow while wheel is open is an accessibility option (E31); default on.
Acceptance criteria
- AC-1 integration Given spells in slots 1–3, when key 2 is pressed, then the selected spell becomes slot 2 and a SpellSelected event fires.
- AC-2 integration Given a spell on cooldown, when rendered, then the slot shows remaining cooldown in 0.1 s resolution and is marked unavailable.
- AC-3 integration Given mana below a slot's cost and over-cast disallowed, when rendered, then the slot shows the unaffordable state.
- AC-4 integration Given an empty slot, when its key is pressed, then nothing is selected and no error is thrown.
- AC-5 integration Given hotbar assignments, when saved and reloaded, then all 8 slot assignments are restored.
- AC-6 e2e Given the greybox testbed, when the quick-select wheel is held, then sim time scale is 0.25 until release.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e06.15 Aim and cast spells with reticle, ground targeting and cancel done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #280 (2026-10-24)
area:uiclass:sorcererepic:e06milestone:m2mw-e06.21ADR: spell pipeline extension points (gates, op/delivery registries, pending deliveries, free casts)
Description
PR #264 (mw-e06.2) set the extension contract that mw-e06.3/4/6/7/9/10 and mw-e07.2 must follow: installSpells/SpellPipeline, addGate check/spend, ops.register, deliveries.register with pending + land/cancel, source 'free' for scrolls, magic.known for learning. Write it down as an ADR (Accepted, owner delegated design calls 2026-10-24).
Acceptance criteria
- AC-1 ADR in docs/adr following the existing format, status Accepted, linked from mw-e06 beads
Closed
Completed in PR #267 (2026-10-24)
area:simclass:sorcererepic:e06milestone:m2mw-e06.24Wire loaded status definitions into the running game's spell pipeline
Description
Why
mw-e06.9 (PR #296) added status content and stack/break/dispel rules in src/sim/magic/statuses.ts, but the game never passes a statuses lookup to the pipeline (installSpells is only called from tests), so in play statuses use the old non-stacking behaviour.
What
Pass the loaded status content to the spell pipeline in the game wiring; run updateStatuses each tick; consume modifiedStat for movement speed (slowed/rooted).
Notes
Same-tick AC-3 nuance: a source destroyed inside a step ends dependent statuses on the next pass.
Acceptance criteria
AC-1 integration Given the running game sim, when a spell applies slowed, then the player's move speed reads through modifiedStat. AC-2 integration Given a rooted target whose source burns, then rooted ends.
Closed
Completed in PR #303 (2026-11-08)
area:simclass:sorcererepic:e06milestone:m2mw-e06.25Validate that spells only apply defined statuses at content load
Description
Why
PR #296 dropped a load-time check that spell status ops reference defined statuses because test fixtures use ad hoc ids (marked, glowing, drowsy).
What
Add the check for shipped content (src/content/data), migrate or define fixture statuses.
Acceptance criteria
AC-1 content Given a spell whose status op names an undefined status, when content validates, then validation fails.
Closed
Completed in PR #305 (2026-11-08)
area:simclass:sorcererepic:e06milestone:m2
E07Spellbook & Magical LearningSpells are discovered through books, shops, exploration, quests, secrets and experimentation rather than appearing on level-up. Books are inventory items with language, condition, comprehension difficulty and legality; reading and study teach spells; damaged, untranslated and forbidden books create side-goals; experimentation with the world reveals new verbs. Progression unlocks new verbs (Firebolt → Flame Jet → Fire Wall), never +% tiers.3/13 done · 23%
M2 Class fantasies 3
mw-e07.1Define book item schema: spell, language, condition, legality
Description
Why
Magic is learned primarily from books (constitution §4); books need a rich, validated data shape.
What
Zod schema for BookItem extending the inventory item: bookId, title, author, school, teaches (spellId | insight | lore-only), pages (text blocks), language, comprehensionDifficulty (0–1), studyMinutes (in-game), condition {intact|damaged(missingPages[])}, legality {legal|forbidden}, rarity, coverArtId, flags (unique, cursed-whispers).
Scope
- In: schema + validator + fixtures
- In: registration with inventory item types
- Out: learning runtime (e07-spellbook-learning)
- Out: shop stock (mw-e21)
Notes
Cover art id follows asset convention: ui-book-cover-<bookId>.
Acceptance criteria
- AC-1 content Given a valid fixture book teaching a known spell, when validated, then it parses and registers as an inventory item of type book.
- AC-2 content Given a book whose teaches references an unknown spell id, when validated, then validation fails naming the book and spell id.
- AC-3 content Given a damaged book whose missingPages index exceeds its page count, when validated, then validation fails.
- AC-4 unit Given two book definitions teaching the same spell, when loaded, then both are allowed (multiple sources per spell).
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e06.1 Define zod spell schema with delivery, effect ops and cue ids done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #266 (2026-10-24)
area:contentclass:sorcererepic:e07milestone:m2mw-e07.2Learn spells by reading and studying book items
Description
Why
Spells should come from discovery, not level-ups; reading a newly bought grimoire should be a moment.
What
As a sorcerer, I want to read a book and study it for some in-game time so that I learn its spell and can cast it.
Read action opens the book (lore always readable); Study action advances in-game time (resting/at a desk bonus), accumulates study progress; at 100% the spell enters the known-spells registry and a SpellLearned event fires. Non-sorcerer classes can read lore but only learn if the spell allows (e.g. Light scrolls).
Scope
- In: known-spells registry, study progress per book, study time consumption, class gating, events
- Out: comprehension/partial understanding (e07-comprehension-study)
- Out: reader UI (e07-book-reader-ui)
Notes
Study can be interrupted (e.g. an ambush) and resumes from saved progress.
Acceptance criteria
- AC-1 unit Given a book with studyMinutes 60 and no bonuses, when the player studies for 60 in-game minutes, then the spell is known and SpellLearned {spellId, source: bookId} fires once.
- AC-2 unit Given study at a desk-tagged object with a 1.5× bonus, when studying 40 minutes, then progress is 100%.
- AC-3 unit Given study interrupted at 30 minutes by a hostile entering Alerted state nearby, when interrupted, then progress is 50% and resuming later completes after 30 more minutes.
- AC-4 unit Given an already-known spell, when its book is studied, then no duplicate learn occurs and the book is flagged "known" instead.
- AC-5 unit Given a knight reading a sorcerer-only book, when attempting to study, then it is rejected with reason "class" while lore pages remain readable.
- AC-6 e2e Given the greybox testbed, when the player picks up a fixture book, studies it and casts its spell, then the cast succeeds.
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
- mw-e07.1 Define book item schema: spell, language, condition, legality done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
Closed
Completed in PR #276 (2026-10-24)
area:simclass:sorcererepic:e07milestone:m2mw-e07.13Route paid spell lessons (Shops.train) through SpellLearning.learn
Description
PR #275 (bridge sorcerer) has Shops.train call learnSpell directly; PR #276 (book study) adds SpellLearning.learn as the single door for every teacher (known spell + spell.<id> capability via unlock book + SpellLearned event). After both merge, make Shops.train use SpellLearning.learn so lessons fire SpellLearned and honour prerequisites the same way books do.
Acceptance criteria
- AC-1 paying Idris Quillon fires SpellLearned {source: trainer} and Ember becomes castable (integration test)
Closed
Completed in PR #277 (2026-10-24)
area:simclass:sorcererepic:e07milestone:m2
E08Spell CatalogueAn MVP library of ~20 spells across Fire, Frost, Storm, Arcane, Illusion, Alteration, Conjuration, Necromancy, Nature, Light, Shadow, Gravity and Time, emphasising qualitatively different effects: ignition, freezing, force, telekinesis, teleportation, decoys, restraint, summoning, gravity, transformation, light, darkness, water and time. Every spell interacts with the world only through properties and stimuli, and ridiculous combinations are encouraged.3/25 done · 12%
M2 Class fantasies 3
mw-e08.2Add Ember: precise ignition of flammable things
Description
Why
Constitution example: ignite candles, ropes, oil and enemies — the gateway spell for systemic fire.
What
As a sorcerer, I want a cheap, short-range ignition bolt so that I can light anything flammable precisely.
Slow projectile, low damage, strong heat stimulus at impact point (small radius). Carries a small light op.
Scope
- In: Ember spell
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Fire. Placeholder cues: vfx-spell-ember-cast, vfx-spell-ember-travel, vfx-spell-ember-impact, sfx-spell-ember-cast, sfx-spell-ember-impact. Icon: icon-spell-ember. Book cover: ui-book-cover-ember.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given any object tagged flammable, when hit by Ember, then it ignites within 1 sim second.
- AC-3 integration Given an object tagged wet, when hit by Ember, then it does not ignite and loses wet after 3 hits (steam) per E03 rules.
- AC-4 integration Given an unlit light-source object tagged flammable (candle, torch), when hit, then it becomes lit and the light field increases at its position.
- AC-5 unit Given a creature hit by Ember, when resolved, then it takes fire damage ≤ 10% of a Firebolt's and gains burning only if tagged flammable.
- AC-6 unit Given Ember fired underwater or into a water volume, when resolved, then it fizzles with no ignition and emits a steam cue event.
Waits on
- mw-e06.14 Make spells emit light and noise that stealth and AI perceive done
- mw-e06.5 Emit world-property stimuli from spell effects into the world sim done
- mw-e06.6 Deliver spells as physical projectiles with collision and arcs done
Closed
Completed in PR #274 (2026-10-24)
area:contentclass:sorcererepic:e08milestone:m2mw-e08.10Add Mage Hand: grab, move and use distant objects
Description
Why
Constitution example; telekinesis is the most versatile world-manipulation verb (levers, keys, items, stacking).
What
As a sorcerer, I want to grab a distant object, move it through the air and use distant interactables so that I can pull levers, fetch keys and stack crates from afar.
Sustained tether: hold up to a weight limit at up to 15 m, move with aim, release; "use" performs the object's interact verb remotely (levers, switches, doors).
Scope
- In: Mage Hand spell
- In: remote-interact via e02 interaction verbs
- In: spell JSON + book item JSON, unit tests for effects, integration tests for world interactions, placeholder cue ids
- Out: final VFX/SFX/icon/book cover (asset prompt beads)
- Out: world placement of the book (E24–E26)
Notes
School: Arcane. Placeholder cues: vfx-spell-mage-hand-tether, vfx-spell-mage-hand-grab, sfx-spell-mage-hand-{grab,hold-loop,release}. Icon: icon-spell-mage-hand. Book cover: ui-book-cover-mage-hand.
TOO USEFUL — INTENTIONALLY ALLOWED (constitution §4). Do not nerf this spell to prevent sequence breaks or cheese; encounters and puzzles must tolerate it. Balance only via mana/cast time/cooldown, never by adding pairwise exceptions.
Acceptance criteria
- AC-1 content Given the spell data file, when validated and loaded, then it has a school, book item, vfxCue and sfxCue and casts in the headless sim.
- AC-2 integration Given any lever or switch within 15 m and line of sight, when Mage Hand uses it, then it activates exactly as a direct interaction would.
- AC-3 integration Given a loose object below the weight limit, when grabbed and moved, then it follows the aim point with ≤ 0.3 m lag and can be dropped onto a pressure plate.
- AC-4 integration Given an object above the weight limit, when grabbed, then it is not lifted and a ForceResisted event fires.
- AC-5 integration Given a held object, when it collides with a creature at low speed, then no damage occurs (throwing is the follow-on verb).
- AC-6 unit Given line of sight breaks for > 0.5 s while holding, when resolved, then the object is dropped.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e06.4 Support cast times, channeling, cooldowns and interruption done
- mw-e06.8 Apply spell forces: impulse, knockback, pull and lift done
Closed
Completed in PR #297 (own tether delivery; does not need the e06.8 force op) (2026-11-07)
area:contentclass:sorcererepic:e08milestone:m2too-usefulmw-e08.25Bind Mage Hand use and release to player input
Description
Why
Mage Hand (mw-e08.10, PR #297) exposes sim commands tether-use and tether-release, but no key or pad button issues them, so a player can grab and carry but not remotely pull a lever.
What
Bind 'use while tethering' (interact key / pad A while channelling) to tether-use, and an explicit release to tether-release; show a reticle prompt when the held thing is usable.
Acceptance criteria
AC-1 e2e Given Mage Hand learned and a lever 10 m away, when the player channels on it and presses interact, then the lever switches. AC-2 unit Given a tether active, when the release input fires, then tether-release is issued.
Closed
Completed in PR #302 (2026-11-08)
area:contentclass:sorcererepic:e08milestone:m2
E09Systemic StealthVisibility, lighting, sound, suspicion, searching, last-known-position and hiding work as coherent, deterministic systems. Stealth is not crouching + an eye icon: players read light exposure, noise and each enemy’s awareness, and failure escalates into new situations rather than a reload.6/23 done · 26%
M1 Grey-box slice 4
mw-e09.1Multi-point line-of-sight queries with partial cover and see-through
Description
Why
Hiding behind a crate with your head poking out should be different from full cover; windows and grates should let guards see but not pass.
What
A sim LOS service: raycast from the observer’s eye to N sample points on the target (head, chest, hips, feet — configurable) returning fraction visible 0–1. Colliders carry an
occlusionproperty:opaque,transparent(glass, grates),partial:<factor>(foliage, curtains, smoke volumes). Queries go through a physics adapter with a pure-TS fake for unit tests.Scope
- In: LOS service, sample points, occlusion properties, adapter + fake
- In: Dynamic occluders (smoke volumes, closing doors)
- Out: Visibility score composition (e09-visibility-model)
- Out: Sight cone logic (e11-perception)
Notes
Smoke and foliage are volumes; their factor multiplies along the ray. Batch API for many observers per tick.
Acceptance criteria
- AC-1 unit Given a target fully behind an
opaquewall, when queried, then visible fraction is 0. - AC-2 unit Given a target crouched behind a crate that hides hips and feet only, when queried with 4 sample points, then visible fraction is 0.5.
- AC-3 unit Given a target behind a
transparentwindow, when queried, then visible fraction equals the unobstructed value. - AC-4 unit Given a ray through two
partial:0.5foliage volumes, when queried, then the per-ray visibility is 0.25. - AC-5 unit Given a zero-length ray (observer eye inside target), when queried, then visible fraction is 1 and no error is thrown.
- AC-6 perf Given 24 observers × 4 samples, when batched, then the query cost p95 ≤ 0.3 ms per tick on the reference machine.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e03.1 Define typed world-property components for systemic interactions done
Closed
Completed in PR #85 (2026-09-29)
area:simepic:e09milestone:m1mw-e09.2Compute player visibility from light, stance, motion and distance
Description
Why
Stealth cannot be crouching plus an eye icon. Visibility must emerge from light and darkness, stance, movement and distance so players can reason about it.
What
As a thief, I want how visible I am to depend on the light on my body, my stance, how fast I move and what I wear so that shadows and patience are my tools.
visibility(target, observer)= lightTerm (light-field sample at target sample points, adjusted by observer dark-vision) × stanceTerm (prone/crouch/stand) × motionTerm (speed band) × profileTerm (equipment brightness, disguise/cloak modifiers) × LOS fraction × distance falloff. A per-target self-visibility (observer-independent) is also exposed for the HUD.Scope
- In: Pure function + components, all weights from tuning data
- In: Self-visibility for UI
- In: Contrast rule: a silhouette against a bright background raises visibility
- Out: Sight cone / accumulation (E11)
- Out: Light-field implementation (e03-light-field)
Notes
Terms multiply so any zero term hides the target. Keep the function pure: inputs are sampled values, not entity lookups, for trivial unit testing.
Acceptance criteria
- AC-1 unit Given light level 0.05 at all sample points, crouched and still at 10 m, when computed with default tuning, then visibility ≤ 0.1.
- AC-2 unit Given light level 0.9, standing and sprinting at 10 m, when computed, then visibility ≥ 0.8.
- AC-3 unit Given identical inputs except distance 5 m vs 20 m, when computed, then the 20 m result is strictly lower per the falloff curve.
- AC-4 unit Given an observer with dark-vision 1.0, when the target stands in 0.0 light, then the light term is 1.0 (darkness gives no benefit).
- AC-5 unit Given a dark target silhouetted against a background light ≥ 0.7 along the observer ray, when computed, then the contrast bonus applies and visibility rises by the configured amount.
- AC-6 unit Given LOS fraction 0, when computed, then visibility is exactly 0 regardless of other terms.
- AC-7 content Given the stealth tuning file, when a weight is outside 0–2, then validation fails.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e02.10 Expose stance and movement profiles for stealth noise and visibility done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.15 Query light level at any position from all light sources in sim done
- mw-e09.1 Multi-point line-of-sight queries with partial cover and see-through done
Closed
Completed in PR #157 (2026-10-03)
area:simclass:thiefepic:e09milestone:m1mw-e09.3Propagate noise events through rooms, doors and walls with occlusion
Description
Why
Sound propagation, surfaces and doors are constitution-mandated stealth inputs. Guards should hear a dropped bottle through an open door but not through thick stone.
What
As a thief, I want noise to travel like it does in a building so that closing a door or taking the long way round actually keeps me quiet. Noise events {source, loudness dB at 1 m, kind, tags} propagate over a room/portal graph built from level data: distance attenuation (inverse-square approximation in dB), portal attenuation by door state (open 0 dB, ajar −6 dB, closed −20 dB, configurable per door material), wall transmission for adjacent rooms (−30 dB default, per wall material), floor/ceiling transmission for verticality. Listeners receive perceived level and the perceived position (the last portal the sound came through when routed indirectly).
Scope
- In: Room/portal graph build from level data
- In: Propagation (Dijkstra over portals, bounded)
- In: Perceived position routing
- In: Event bus API for emitters (footsteps, impacts, shouts, spells)
- In: Hook for e28-audio-engine to reuse the occlusion result
- Out: Footstep emission rules (e09-footstep-noise)
- Out: Actual audio playback (e28-audio-engine)
Notes
Perceived position is what makes the no-omniscience rule hold for sound. Bounded search: stop when level < global floor (10 dB).
Acceptance criteria
- AC-1 unit Given a 60 dB noise and a listener 10 m away in the same room, when propagated, then perceived level is 40 dB ± 0.5 (−20 dB for 10 m).
- AC-2 unit Given the same geometry with a closed door on the only path, when propagated, then perceived level is 20 dB lower than with the door open.
- AC-3 unit Given a noise in room A heard in room B via a doorway, when propagated, then the perceived position is the doorway, not the source.
- AC-4 unit Given two paths (open corridor 25 m, closed door 8 m), when propagated, then the louder of the two routes is used.
- AC-5 unit Given a noise on the floor above through a wooden floor (−15 dB), when propagated to a listener below, then perceived level reflects the floor transmission loss.
- AC-6 unit Given a door that changes from open to closed between two noise events, when the second event propagates, then it uses the new door state (graph edges update on door events).
- AC-7 unit Given a noise emitted outside any room (open terrain), when propagated, then pure distance attenuation applies without error.
- AC-8 perf Given 20 noise events per tick in a 60-room level, when propagated, then cost p95 ≤ 0.5 ms per tick.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
Closed
Completed in PR #159 (2026-10-03)
area:simclass:thiefepic:e09milestone:m1mw-e09.22Install noise propagation at scene load and route AI emit-noise through noiseEmitted
Description
Why
mw-e09.3 built sound propagation in the sim, but nothing in the running game installs it, and AI behaviours' emit-noise step raises its own AiNoiseEmitted instead of the shared noiseEmitted channel, so guards' noises are not propagated.
What
When a scene loads, the game builds its sound graph (soundGraphFromScene) and installs noise propagation with the shipped stealth noise tuning and door materials; a level change swaps the graph. The AI emit-noise primitive raises noiseEmitted (kind ai, source the agent) so its noises propagate like any other.
Scope
- In: Game wiring at scene load and unload
- In: AI emit-noise on the shared channel
- Out: Listener perception (mw-e11.5)
- Out: Audio occlusion playback (mw-e28.10)
Notes
Replay fixtures may need re-recording if the AI change alters event order.
Acceptance criteria
- AC-1 integration Given the mechanism room loaded in the game, when a pot breaks in the west closet with its door shut, then a listener in the hall receives noiseHeard via west-doorway.
- AC-2 unit Given a behaviour with an emit-noise step, when it runs, then a noiseEmitted with the agent as source is raised.
Closed
Completed in PR #181 (2026-10-03)
area:gamearea:simclass:thiefepic:e09milestone:m1milestone:m2
M2 Class fantasies 2
mw-e09.4Tag surfaces with acoustic materials that change footstep noise
Description
Why
Surfaces are a constitution stealth input: carpet muffles, gravel crunches, a creaky board betrays you, a puddle splashes.
What
Acoustic material tags on walkable colliders and level regions (
stone,wood,wood-creaky,carpet,gravel,metal-grate,water-shallow,grass,leaves,glass-shards): loudness offset dB, per-step variance, and sfx set id. Materials can change dynamically via world properties (awetorfrozensurface changes its offset; scattered glass shards becomeglass-shards). Query APIsurfaceAt(position).Scope
- In: Material table (content data), collider/region tagging, dynamic modifiers from world properties
- In: Grey-box painter support: tag via level JSON
- Out: Footstep emission rule (e09-footstep-noise)
- Out: Final footstep audio (asset beads)
Notes
Creaky boards: probability per step of an extra +12 dB creak (seeded).
Acceptance criteria
- AC-1 unit Given a position on a
carpetregion, whensurfaceAtis queried, then material iscarpetwith offset −10 dB (default table). - AC-2 unit Given a
woodsurface that gains thefrozenproperty, when queried, then the frozen-surface modifier applies per the table. - AC-3 unit Given overlapping regions, when queried, then the higher-priority region wins deterministically.
- AC-4 unit Given a position with no surface tag, when queried, then the default
stonematerial is returned and a dev warning is logged once per region. - AC-5 content Given the material table, when validated, then each material has an offset in −20..+20 dB and a sfx set id.
- AC-6 content Given the material table, when compared with the footstep surface set in audio bible §7.3, then the material ids match exactly (a test fails on any id present in only one list).
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.2 Provide data-defined material presets for common world objects done
- mw-e03.31 Consolidate world-property vocabulary assumed by other epics done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
Closed
Completed in PR #355 (2026-11-09)
area:contentarea:simclass:thiefepic:e09milestone:m2mw-e09.5Emit footstep noise from speed, stance, surface and equipment weight
Description
Why
Silent movement must be a skill: running on gravel in plate armour should wake the keep; creeping barefoot over carpet should not.
What
As a thief, I want my footsteps to be as loud as my choices make them so that moving slowly and choosing my path pays off. Each footstep (from controller gait cadence) emits a noise event: base dB by gait (sneak/walk/run/sprint) + surface offset + equipment noise (sum of equipped item
noisevalues, e.g. plate clank) + landing impulse for jumps/falls (by fall height). Also emitted for any creature with footsteps, so the player can hear guards.Scope
- In: Footstep emission rule shared by player and creatures
- In: Landing and falling impacts
- In: Equipment noise aggregation (reads item data)
- Out: Progression perks that reduce noise (e10-silent-movement)
- Out: Audio playback (e28)
Notes
Emit at step cadence, not per tick. Defaults: sneak 25, walk 40, run 55, sprint 65 dB at 1 m.
Acceptance criteria
- AC-1 unit Given the player sneaking on carpet with no noisy equipment, when a step occurs, then the event loudness is 15 dB (25 − 10).
- AC-2 unit Given sprinting on gravel wearing items with total noise +10 dB, when a step occurs, then loudness is 65 + gravel offset + 10.
- AC-3 unit Given a fall of 3 m onto stone, when landing, then an impact event with loudness per the fall-height curve is emitted once.
- AC-4 unit Given the player standing still, when 5 s pass, then no footstep events are emitted.
- AC-5 integration Given a scenario where the player sneaks past a guard 6 m away on carpet, when simulated, then the guard stays Unaware; the same path sprinting on gravel makes the guard Suspicious within 1 s.
Waits on
- mw-e02.10 Expose stance and movement profiles for stealth noise and visibility done
- mw-e02.11 Emit footstep events tagged with surface material and gait done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e04.16 Armor weight sets roll class, stamina regen, poise and noise done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e09.4 Tag surfaces with acoustic materials that change footstep noise done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #357. Friendly creatures ignore the player's footsteps and show no marker. (2026-11-09)
area:simclass:thiefepic:e09milestone:m2
E10Thief ToolkitLockpicking, pickpocketing, thrown distractions, extinguishing lights, climbing, hiding bodies, disguises, silent movement, takedowns, traps and escape tools produce a complete thief fantasy built on shared world systems (properties, light, sound, AI perception).1/17 done · 6%
M2 Class fantasies 1
mw-e10.9Climb tagged surfaces, ledges and ropes with stamina
Description
Why
Climbing is how the thief reaches the window, the rooftop and the rafters — the class-specific alternate route.
What
As a thief, I want to climb rough walls, vines, ladders and ropes so that I can take routes other classes cannot. Surfaces with property
climbable(with difficulty) allow climbing; ledges allow shimmy and mantle; placed ropes (from items) create temporary climbable lines. Climbing drains stamina per difficulty; running out causes a fall (noise + damage by height). Class/progression gates higher difficulties. Climbing is quiet (−10 dB vs walk) but you are silhouetted on walls (contrast rule).Scope
- In: Climb state machine on top of controller climb hooks
- In: Stamina drain + fall
- In: Rope placement creating climbable volume + nav link for climbers
- Out: Core controller physics (e02-player-controller)
- Out: Climbing animations (placeholders)
Notes
Climb routes also produce off-mesh links usable by climber creatures (e11-navigation).
Acceptance criteria
- AC-1 unit Given a
climbabledifficulty-2 wall and a thief with max climb difficulty 3, when climb is initiated, then the player enters Climbing state. - AC-2 unit Given a knight with max climb difficulty 1, when attempting the same wall, then climb is refused with a reason prompt.
- AC-3 unit Given stamina reaches 0 while climbing 4 m up, when the tick runs, then the player falls, takes fall damage per curve and a landing noise is emitted.
- AC-4 unit Given a rope placed on an anchor point, when complete, then a climbable volume and an off-mesh climb link exist until the rope is retrieved.
- AC-5 e2e Given the stealth course vertical hall, when the player climbs to the rafters, then the player stands on the rafter platform within 10 s of input with no console errors.
Waits on
- mw-e02.13 Climb ladders, ivy, ropes and rough walls marked climbable done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
Closed
Completed in PR #418 (2026-11-12)
area:gamearea:simclass:thiefepic:e10milestone:m2
E11Enemy AIEnemies patrol, perceive, investigate, communicate, fight, retreat, search and return to believable routines through a deterministic, data-driven behaviour runtime and the six-state alert machine (Unaware → Suspicious → Investigating → Searching → Alerted → Combat). Enemies never know the player position except through their senses.14/26 done · 54%
M1 Grey-box slice 14
mw-e11.1Spike: choose AI architecture (utility AI, behaviour trees, HFSM)
Description
Why
Every enemy behaviour sits on this choice. It must be deterministic, data-driven, debuggable and cheap enough for 24+ agents in a browser.
What
Time-box 2 days. Question: which decision architecture (utility AI, behaviour trees, hierarchical FSM, or hybrid — e.g. HFSM for the 6 alert states with utility selection inside states) best fits deterministic sim, data authoring, debug visualisation and the alert state machine? Build the same scenario (patrol → hear noise → investigate → return) in two candidates inside
spikes/ai-architecture/, measure tick cost and authoring effort, write an ADR.Scope
- In: Two prototype implementations in
spikes/ - In: Micro-benchmark: 50 agents, cost per tick
- In: ADR
docs/adr/NNNN-ai-architecture.mdwith recommendation and data format sketch - Out: Production runtime (e11-behaviour-runtime)
- Out: Navigation (e11-navigation)
Notes
Evaluation criteria: determinism (no hidden time/random), data-authorability (JSON), debuggability (current node/scores visualisable), perf, testability at 100% coverage, fit with personality traits and needs as utility inputs.
Acceptance criteria
- AC-1 manual Given the spike is complete, when the ADR is reviewed, then it records the decision, at least 2 rejected alternatives with reasons and the measured numbers.
- AC-2 perf Given each prototype, when 50 agents run the scenario headless for 60 s, then per-tick cost is recorded (mean and p95) in the ADR.
- AC-3 unit Given each prototype, when the scenario replays twice with the same seed, then the agent state hash sequences are identical.
- AC-4 manual Given the ADR, when read, then it includes a JSON sketch of a behaviour definition for
fixture-guardin the chosen format.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
Closed
Completed in PR #133 (2026-10-02)
area:designarea:simepic:e11milestone:m1- In: Two prototype implementations in
mw-e11.2Implement the deterministic behaviour runtime chosen by the ADR
Description
Why
A single, deterministic behaviour runtime lets all creatures share patrol/investigate/search/combat behaviours authored as data.
What
Production implementation of the ADR architecture in
src/sim/ai/: behaviour definitions loaded as content (behaviour:*), per-agent blackboard (typed keys), action primitives (move-to, look-at, wait, play-cue, emit-noise, attack), condition primitives (state, stimulus, timer, trait), time-slicing so each agent thinks at a configurable rate (default 10 Hz) with deterministic ordering by entity id.Scope
- In: Runtime, primitives, blackboard, behaviour schema + loader
- In: Deterministic scheduler with think budgets
- In: Introspection API for debug visualisation
- Out: Individual behaviours (later E11 items)
- Out: Visual editor (post-MVP, E33)
Notes
Actions return Running/Success/Failure (or utility equivalents); all timing uses sim ticks. Introspection returns current node/score table per agent.
Acceptance criteria
- AC-1 unit Given two agents with think rate 10 Hz on a 60 Hz sim, when 60 ticks run, then each agent thinks exactly 10 times and their think ticks are staggered deterministically by entity id.
- AC-2 unit Given a behaviour definition referencing an unknown primitive, when loaded, then loading fails with the behaviour id and primitive name.
- AC-3 unit Given an action that fails (move-to unreachable), when the runtime ticks, then the failure propagates per the ADR semantics and a fallback branch runs.
- AC-4 unit Given an agent is despawned mid-action, when the runtime ticks, then no further callbacks run for it and no exception is thrown.
- AC-5 perf Given 50 agents running a patrol behaviour, when the sim runs headless, then AI think cost p95 ≤ 1.0 ms per tick on the reference machine.
- AC-6 unit Given an agent, when introspected, then it returns the active node path (or top-3 utility scores) as serialisable data.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e11.1 Spike: choose AI architecture (utility AI, behaviour trees, HFSM) done
Closed
Completed in PR #137 (2026-10-02)
area:simepic:e11milestone:m1mw-e11.3Scenario harness for scripted AI tests with asserted states over time
Description
Why
Stealth and AI rules must be trustworthy without reading code; readable, deterministic scenario tests are how we prove guard behaviour.
What
A test DSL in
src/sim/testing/scenario.ts: load a grey-box layout (JSON), spawn fixtures with routes, script a player path (waypoints, stance, speed, actions likethrow,extinguish), and assert over time (at(12.0).expect(guard1).state("Investigating"),during(0, 5).expect(guard1).notState("Combat")). Runs headless on the replay harness; failures print a timeline of state changes and stimuli.Scope
- In: DSL, headless runner, failure timeline output
- In: Snapshot hash recording for regression
- In: Example scenario using fixture-guard
- Out: The scenario suite itself (e09-infiltration-scenarios)
- Out: Browser e2e (Playwright)
Notes
Built on e00-replay-harness; scripted player drives the same input actions as a real player where possible.
Acceptance criteria
- AC-1 unit Given a scenario asserting
state("Suspicious")at t=3.0 s, when the guard is actually Unaware at 3.0 s, then the test fails and output lists every state transition with timestamps. - AC-2 unit Given a scenario, when run twice, then the recorded state hash sequence is identical.
- AC-3 integration Given the example scenario (player walks through guard view in light), when run, then the guard transitions Unaware→Suspicious within 1.5 s and the test passes.
- AC-4 unit Given a layout JSON referencing an unknown fixture id, when the scenario loads, then it fails before ticking with a clear error.
- AC-5 perf Given a 120 s scenario with 6 agents, when run headless in CI, then it completes in ≤ 3 s wall time.
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e12.3 Author three grey-box creature fixtures for AI and stealth tests done
- mw-e12.4 Spawn data-defined creatures into the sim and grey-box scenes done
Closed
Completed in PR #140 (2026-10-02)
area:qaarea:simarea:toolsepic:e11milestone:m1mw-e11.4Navmesh generation and deterministic pathfinding for creatures
Description
Why
Patrol, investigate, search, flee and flank all need reliable paths, including doors, ladders and climb links that make verticality matter.
What
Navmesh baked from grey-box and level geometry at content build time (Recast-style tool or chosen library, decided within this item), stored as data. Sim-side A* over the navmesh with per-creature capability masks (from e12-locomotion-types), dynamic obstacles (closed/locked doors toggling links), off-mesh links (ladders, jumps, climb). Path smoothing and a request queue with a per-tick budget.
Scope
- In: Bake step + data format
- In: Pathfinding in sim, deterministic
- In: Door state integration, off-mesh links
- In: Local steering/avoidance between agents (simple)
- Out: Flying 3D navigation beyond height-offset flight (post-MVP)
- Out: Crowd simulation
Notes
If a WASM library is used, the query layer must still be deterministic and wrapped behind a sim interface with a pure-TS fake for unit tests. Related: level geometry format from E00 greybox kit.
Acceptance criteria
- AC-1 unit Given a baked grey-box navmesh, when a path is requested between two connected points, then the returned path is valid (every segment on-mesh) and identical across runs.
- AC-2 unit Given a closed locked door between two rooms, when a walker requests a path through it, then no path through that door is returned; after
unlock + open, the path is found. - AC-3 unit Given a climb link, when a walk-only creature and a climber request the same path, then only the climber’s path uses the link.
- AC-4 unit Given an unreachable target, when requested, then the result is
Unreachablewith the closest reachable point, not an exception. - AC-5 perf Given 30 path requests in one tick, when the budget is 0.5 ms, then excess requests are deferred to later ticks and all complete within 10 ticks.
- AC-6 integration Given the grey-box testbed, when the bake runs, then a navmesh file is produced in ≤ 10 s and content validation passes.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e12.6 Support walker, climber, flier, swimmer and burrower locomotion data done
Closed
Completed in PR #162 (ADR-0006 proposed; awaiting owner acceptance) (2026-10-03)
area:simepic:e11milestone:m1mw-e11.5Creature perception: sight cones, hearing and special senses
Description
Why
Enemies must perceive the world honestly — through senses that the player can understand, predict and exploit — rather than knowing everything.
What
As a thief, I want enemies to only know what their senses tell them so that darkness, silence and cover genuinely protect me. Each agent evaluates senses from its resolved sense profile: sight (cone test with primary/peripheral zones, vertical limit, range falloff, LOS via e09-line-of-sight, target visibility score via e09-visibility-model, dark-vision factor), hearing (receives noise events from e09-sound-propagation above its threshold), smell/special senses via registered channel handlers. Output is a list of typed stimuli {source, kind, position, strength 0–1, certainty} per think, fed to awareness.
Scope
- In: Sight, hearing, special-sense channel registry
- In: Stimulus types: seen-target, heard-noise, seen-anomaly, sensed-life
- In: Time-slicing so expensive LOS checks respect a budget
- Out: Awareness accumulation (e11-awareness-accumulation)
- Out: Alert state logic (e11-alert-states)
- Out: Anomaly definitions (e09-anomaly-detection)
Notes
Perception never reads the player position directly outside senses — enforced by an API boundary (agents receive only stimuli). This is the no-omniscience guarantee.
Acceptance criteria
- AC-1 unit Given a guard with primary half-angle 35° and range 20 m, when a fully visible target stands at 10 m and 20° off-axis, then a seen-target stimulus is produced with strength > 0.5.
- AC-2 unit Given the same target at 10 m and 120° off-axis (outside peripheral), when evaluated, then no sight stimulus is produced.
- AC-3 unit Given a target in the cone but with LOS fully blocked, when evaluated, then no sight stimulus is produced.
- AC-4 unit Given a target in the cone with visibility score 0.1 (deep shadow, crouched), when evaluated at 15 m, then stimulus strength is below the Suspicious contribution threshold per tuning data.
- AC-5 unit Given a noise event of 45 dB at the listener and threshold 30 dB, when evaluated, then a heard-noise stimulus carries the noise’s propagated position, not the true source if the noise was occluded and routed through a doorway.
- AC-6 unit Given the perception API, when an agent attempts to access the player entity transform outside a stimulus, then the type system disallows it (compile-time) and an architecture test asserts no such import exists.
- AC-7 perf Given 24 agents and 1 target, when perception runs at 10 Hz, then sim perception cost p95 ≤ 0.8 ms per tick on the reference machine.
Waits on
- mw-e09.1 Multi-point line-of-sight queries with partial cover and see-through done
- mw-e09.2 Compute player visibility from light, stance, motion and distance done
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
- mw-e12.2 Add sense profile data: sight cones, hearing, smell, special senses done
Closed
Completed in PR #165 (2026-10-03)
area:simepic:e11milestone:m1mw-e11.6Accumulate and decay awareness from stimuli per perceived target
Description
Why
Detection should build gradually so players have time to react; a glimpse at the edge of vision is not the same as walking into a torch-lit room.
What
Per agent, per perceived source: awareness 0–1 accumulated each think as
strength × senseRate × traitModifiers × dt, with decay when unstimulated after a grace period. Thresholds from tuning data (e.g. 0.3 Suspicious, 0.6 Investigating, 1.0 Detected) drive alert transitions. Tracks the stimulus that contributed most, used by UI to explain why the player was noticed.Scope
- In: Accumulation/decay curves
- In: Per-source records with dominant-cause tracking
- In: Instant-detection rule for very close contact (touch/bump)
- Out: State machine (e11-alert-states)
- Out: UI (e09-awareness-indicators)
Notes
Curves and thresholds come from e11-ai-tuning-data; this item ships defaults.
Acceptance criteria
- AC-1 unit Given a constant sight stimulus of strength 1.0 and rate 1.0/s, when 0.6 s passes, then awareness is 0.6 ± 0.01.
- AC-2 unit Given awareness 0.5 and no stimuli, when grace 2 s then decay 0.1/s applies, then awareness is 0.5 at t=2 s and 0.3 at t=4 s.
- AC-3 unit Given the player bumps into an Unaware guard, when contact is registered, then awareness jumps to 1.0 immediately regardless of light.
- AC-4 unit Given sight and hearing stimuli in the same think, when accumulated, then the dominant cause recorded is the one with the larger contribution.
- AC-5 unit Given a source that despawns, when the next think runs, then its awareness record is removed without error.
Waits on
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e31.14 Inject a DifficultyConfig of tunable multipliers into the sim done
Closed
Completed in PR #171 (2026-10-03)
area:simepic:e11milestone:m1mw-e11.7Six-state alert machine: Unaware to Combat with timed decay
Description
Why
The constitution mandates Unaware → Suspicious → Investigating → Searching → Alerted → Combat. Readable states let players understand and manipulate guards.
What
As a thief, I want guards to escalate and de-escalate through recognisable states so that I can recover from mistakes. A hierarchical state machine per agent: Unaware, Suspicious (stops, looks toward stimulus), Investigating (moves to stimulus position), Searching (lost the target, searches around last-known position), Alerted (knows an intruder exists, active hunt, may raise alarm), Combat (has a confirmed target in perception). Transitions driven by awareness thresholds, timers and events (ally alarm, damage taken). Each state has a data-defined timeout and fallback. De-escalation goes to a
postAlertflag (heightened baseline) rather than straight to fully relaxed.Scope
- In: States, transition table as data, timers, events emitted on every transition
- In: Damage-from-unseen-source → Alerted (not Combat) since the attacker is unknown
- In: Heightened baseline after de-escalation (faster accumulation)
- Out: Behaviours inside states (investigate/search/combat items)
- Out: Group communication (e11-communication)
Notes
Transition table example lives in tuning data. Combat → Searching when target lost for > N s; Searching → Unaware(postAlert) after search timeout. Never Combat → Unaware directly.
Acceptance criteria
- AC-1 unit Given an Unaware guard, when awareness crosses 0.3, then it enters Suspicious and emits
AlertStateChanged{from: Unaware, to: Suspicious, cause}. - AC-2 unit Given a Suspicious guard, when no stimulus is received for 6 s, then it returns to Unaware with
postAlert=falseand resumes its routine. - AC-3 unit Given a guard in Combat, when the target leaves perception for 5 s, then it enters Searching (never Unaware directly) with the target’s last observed position as LKP.
- AC-4 unit Given a Searching guard, when the search timeout (60 s default) elapses without re-acquisition, then it enters Unaware with
postAlert=trueand awareness accumulation rate ×1.5 for 300 s. - AC-5 unit Given an Unaware guard hit by a projectile from an unperceived source, when damage is applied, then it enters Alerted (not Combat) and the LKP is estimated from the projectile direction, not the shooter position.
- AC-6 unit Given the transition table, when fuzzed with 10,000 random event sequences, then no transition ever occurs that is absent from the table and state is always one of the six.
- AC-7 content Given a tuning file with a timeout ≤ 0, when validated, then validation fails.
Waits on
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
- mw-e11.6 Accumulate and decay awareness from stimuli per perceived target done
Closed
Completed in PR #173 (2026-10-03)
area:simepic:e11milestone:m1mw-e11.8Target memory and last-known position without omniscience
Description
Why
Enemies must not magically know the player’s position after losing sight. Last-known position is what lets players slip away, double back and set up ambushes.
What
As a thief, I want guards to hunt where they last saw me, not where I actually am, so that breaking line of sight is meaningful. Agent memory stores per target: LKP, last observed velocity, time last seen, confidence decaying over time, and a bounded prediction (LKP + velocity × min(t, 1.5 s)). Memory updates only from perception stimuli or ally communication. Memories of anomalies (bodies, open doors) persist with timestamps.
Scope
- In: Memory records, confidence decay, bounded prediction
- In: Update sources: own stimuli, ally reports (tagged second-hand, lower confidence)
- In: Forgetting after configured duration
- Out: Search behaviour choosing where to look (e11-search-behaviour)
- Out: Player-facing LKP ghost (e09-lkp-ghost)
Notes
Architecture test: memory module has no access to ground-truth transforms. Prediction cap prevents agents tracking the player through walls.
Acceptance criteria
- AC-1 unit Given the player is last seen at P moving 3 m/s east, when 10 s pass without stimuli, then the predicted position is P + 4.5 m east (capped at 1.5 s) and never updates further.
- AC-2 unit Given the player, unseen, moves 30 m away, when the guard queries memory, then the returned LKP is unchanged (no leak of true position).
- AC-3 unit Given an ally shouts a report of the player at Q, when the listener updates memory, then LKP becomes Q with confidence × 0.7 and source
second-hand. - AC-4 unit Given confidence decay 0.02/s from 1.0, when 50 s pass, then the memory is forgotten and queries return none.
- AC-5 integration Given a scenario where the player breaks LOS around a corner then climbs to a ledge, when the guard arrives at the LKP, then it has no knowledge of the ledge position unless it perceives the player there.
Waits on
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
Closed
Completed in PR #179 (2026-10-03)
area:simepic:e11milestone:m1mw-e11.9Patrol routes, guard posts and idle routines authored as data
Description
Why
Believable routines are what a thief observes, times and exploits; they are also where guards return after an alert.
What
As a designer, I want to author patrol routes and posts in level data so that guards have readable, exploitable routines. Route types: loop, ping-pong, random-weighted graph; waypoints with dwell time, look-direction, and optional idle action (lean, warm hands at fire, check door). Posts: stationary with scan arcs. Routines can be scheduled by time windows. Agents remember their routine and resume the nearest sensible waypoint after an alert.
Scope
- In: Route/post schema in level data, runtime behaviour
- In: Idle action hooks as cue ids
- In: Resume-nearest-waypoint logic
- Out: Day/night schedules beyond simple windows (Related: world clock)
- Out: Animation assets (placeholder cues only)
Notes
Random-weighted routes use seeded RNG per agent.
Acceptance criteria
- AC-1 unit Given a loop route A→B→C with 2 s dwell at each, when simulated, then the guard visits A, B, C, A in order with dwell ±1 tick.
- AC-2 unit Given a ping-pong route, when the guard reaches the last waypoint, then it reverses direction.
- AC-3 unit Given a guard returning from Searching, when it resumes, then it paths to the nearest waypoint on its route by nav distance, not the next index.
- AC-4 unit Given a route waypoint that becomes unreachable (door locked), when the guard attempts it, then it skips to the next reachable waypoint and emits a
RouteBlockedevent (door-check anomaly hook). - AC-5 content Given a route referencing a nonexistent waypoint id, when validated, then validation fails.
Waits on
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
Closed
Completed in PR #176 (2026-10-03)
area:contentarea:simepic:e11milestone:m1mw-e11.13Combat behaviour: engage, space, select attacks and share targets
Description
Why
Combat is intrinsically fun only if enemies fight readably: spacing, telegraphing, taking turns and punishing mistakes.
What
As a knight, I want enemies to circle, pressure and take turns so that fights reward reading behaviour. In Combat: maintain preferred range per creature, strafe/circle, pick attacks from
attacksby preconditions + weights + personality aggression, respect an attack token budget per target (max N simultaneous attackers, default 2), react to player block/parry (stagger response), reposition when target is out of reach (path/wait/ranged fallback).Scope
- In: Engage/space/attack-select loop
- In: Attack token manager
- In: Out-of-reach handling (e.g. player on ledge)
- Out: Retreat/flee (e11-retreat-flee)
- Out: Boss-specific patterns (E13/E26)
Notes
All selection deterministic with seeded RNG. Losing the target transitions via alert states to Searching.
Acceptance criteria
- AC-1 unit Given 4 hostile guards in Combat vs one player with token budget 2, when simulated for 10 s, then at no tick are more than 2 guards in an attack windup/active phase.
- AC-2 unit Given an attack whose precondition (distance ≤ 2 m) is false, when selecting, then it is never chosen.
- AC-3 unit Given the player stands on a ledge unreachable by nav, when a melee-only guard is in Combat, then it moves to the closest point, waits and after 8 s transitions to a taunt/wait behaviour rather than jittering (≤ 1 path request per second).
- AC-4 integration Given the grey-box fight room, when the player fights 2 fixture guards, then both guards attack at least once within 10 s and the fight resolves without AI errors.
- AC-5 unit Given a guard is staggered, when the stagger ends, then it re-evaluates attack selection rather than resuming the cancelled attack.
Waits on
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.5 Data-driven creature attack definitions bound to the damage model done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
Closed
Completed in PR #184 (2026-10-03)
area:simepic:e11milestone:m1mw-e11.17AI debug overlay: sight cones, hearing, states, LKP and paths
Description
Why
AI is untuneable if you cannot see what it thinks. Developers need to see every agent’s senses, state and memory live.
What
As a developer, I want a toggleable AI overlay so that I can understand and tune behaviour in seconds. Per agent: sight cone (primary/peripheral, tinted by alert state), hearing radius, awareness bars per source, state label + time in state, active behaviour node / top utility scores, LKP marker with confidence, current path, claimed search points. Global: noise event rings with loudness and propagation route, light level probe under cursor. Console:
ai.debug on|off|select <id>,ai.freeze,ai.step.Scope
- In: Overlay rendering from sim introspection data
- In: Console commands incl. freeze/step
- In: Selection by clicking an agent
- Out: Player-facing stealth UI (E09 UI items)
- Out: Visual behaviour editor (post-MVP)
Notes
Render layer reads introspection snapshots; sim must not depend on overlay. Overlay code excluded from unit coverage per coverage-exclusions.md; introspection data builders are 100% covered in sim.
Acceptance criteria
- AC-1 unit Given an agent, when the introspection snapshot is built, then it contains state, timeInState, awareness per source, LKP, path and cone parameters.
- AC-2 e2e Given the grey-box testbed with 3 guards, when
ai.debug onis entered, then overlay elements for 3 agents render with no console errors. - AC-3 integration Given
ai.freeze, when 60 frames render, then the sim tick counter does not advance;ai.stepadvances exactly 1 tick. - AC-4 perf Given the overlay on with 24 agents, when measured, then added frame time p95 ≤ 2 ms on the reference machine (High).
- AC-5 unit Given the overlay is off, when the sim runs, then no introspection snapshots are built (zero overhead check via counter).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #178 (2026-10-03)
area:gamearea:renderarea:toolsepic:e11milestone:m1mw-e11.19Lint behaviour definitions: unreachable states, dead activities, no exit
Description
Why
ADR-0003 makes creature behaviour pure data (HFSM alert states + utility activities). Data mistakes that load fine but strand a guard (a state nothing reaches, an activity no state lists, a state with no way out) would only show up in playtests; a thief exploiting a guard stuck in Suspicious forever is a bug, not a story.
What
As a designer, I want behaviour definitions checked for structural mistakes when content is validated so that a broken behaviour fails CI instead of a playtest. A content check over every behaviour file (and the tuning it resolves) that reports, with the behaviour id and the offending name: states unreachable from
initial; states with neither a timeout nor any outgoing transition (except Combat, which must have a lost-target exit); activities not listed by any state; listed activities whose considerations can never score above 0 (a step or linear curve that is 0 over the input's declared range);done/failedconditions naming an activity the state does not list.Scope
- In: Content check run by
pnpm content:schemas/content tests, input ranges declared by the input registry - In: Fixture behaviours that trigger each error
- Out: Runtime loading errors for unknown names (mw-e11.2 AC-2)
- Out: Fuzzing the transition table at runtime (mw-e11.7 AC-6)
Notes
Related: docs/adr/0003-ai-architecture.md (Consequences, runtime semantics). Pure functions over the parsed definition, 100% covered like the rest of src/content.
Acceptance criteria
- AC-1 content Given a behaviour whose Searching state is listed but no transition or timeout leads to it, when validated, then validation fails naming the behaviour id and "searching" as unreachable.
- AC-2 content Given a non-Combat state with no timeout and no transitions, when validated, then validation fails naming the state as having no exit.
- AC-3 content Given an activity defined but listed by no state, when validated, then validation fails naming the activity.
- AC-4 unit Given an activity whose only consideration is a step curve returning 0 across the input's declared range, when checked, then it is reported as never selectable.
- AC-5 content Given a transition
{ "done": "investigate" }in a state that does not listinvestigate, when validated, then validation fails naming both. - AC-6 content Given every shipped and fixture behaviour definition, when validated, then no error is reported.
Waits on
- mw-e11.2 Implement the deterministic behaviour runtime chosen by the ADR done
Closed
Completed in PR #217 (2026-10-04)
area:contentarea:simepic:e11milestone:m1- In: Content check run by
mw-e11.21Run creature AI on the scene's navmesh in the game loop
Description
Why
mw-e11.4 delivers navmesh paths and navmeshNavigation (an AiNavigation port), but the game does not run creature AI in scenes yet, so nothing in play uses them.
What
When a scene with a baked navmesh loads, build a NavMesh from content navmesh/<scene>, door states from the loaded door spawns (worldNavDoors), and install AI with navmeshNavigation so patrols and investigations walk real paths through doors and up climbs.
Scope
- In: scene-load wiring in src/game, unload/reload, a Playwright check that a patrolling creature goes through a doorway
- Out: new behaviours (mw-e11.9+)
Notes
Related: src/sim/nav/navigation.ts, src/sim/nav/doors.ts, src/game/scene/scene-loader.ts.
Acceptance criteria
- AC-1 integration Given the testbed with a patrolling creature whose route crosses a doorway, when the scene runs 30 s headless, then the creature passes through the doorway and never leaves the navmesh.
- AC-2 e2e Given the grey-box testbed in the browser, when a creature patrols, then no console errors occur and its position stays on the navmesh.
Closed
Completed in PR #181 (2026-10-03)
area:gamearea:simepic:e11milestone:m1mw-e11.23Run creature perception in the game loop on the scene's sight lines, light and sound
Description
Why
mw-e11.5 built perception in the sim and the AI scenario harness runs on it, but nothing in the running game evaluates creature senses, so creatures in grey-box scenes never notice the player.
What
When a scene loads with creatures, the game adds the perception system (perceptionSystem) with line of sight over the scene's Rapier sight world, the scene's light field, the shipped stealth visibility tuning and the player as its target, ordered after the player and light field and before AI. Its percepts feed awareness (mw-e11.6) once that exists.
Scope
• In: Game wiring at scene load and unload; system order
• Out: Noise propagation install (mw-e09.22); awareness (mw-e11.6); AI in the game loop (mw-e11.21)
Notes
Perception adds each hearing creature's noise.listener component, which is a save key: coordinate with world-state saves (mw-e27.4).
Acceptance criteria
• AC-1 integration Given the testbed with a spawned fixture guard facing the lit player 10 m away, when the game steps 6 ticks, then a perceived event carries a seen-target percept for the player.
• AC-2 e2e Given the testbed, when it boots with creatures, then no console errors and the perf overlay shows perception within its budget.
Waits on
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
Closed
Completed in PR #181 (2026-10-03)
area:gamearea:simepic:e11milestone:m1
E12Creature FrameworkCreatures are data: unique senses, locomotion, attacks, property-driven vulnerabilities, factions/relationships, dispositions, fears/morale, personalities, needs and non-hostile interaction affordances are all declared in validated content and consumed by AI and stealth systems — ready for the bestiary (E13) and social creatures (E14).10/20 done · 50%
M1 Grey-box slice 6
mw-e12.1Define zod creature schema with senses, locomotion, attacks and needs
Description
Why
The constitution says enemies are creatures, not target dummies, and creatures must be data-driven. Every AI, stealth and bestiary feature needs one shared, validated creature definition.
What
A
CreatureDefzod schema insrc/content/schemas/creature.tswith typed sections:id,family,tags,stats(health, poise, mass, size class),senses(ref to sense profile or inline),locomotion(list of locomotion modes),attacks(list of attack refs),properties(world-property tags such asflammable,undead,conductive),vulnerabilities/resistances,faction,dispositiondefaults,fears,personality,needs,behaviour(behaviour profile id + tuning overrides),interactions(non-hostile affordances),lootref,presentation(placeholder mesh id, sfx set id). Every section exceptid,family,stats,senses,locomotionis optional with documented defaults. A loader registers creatures into the content registry and resolves refs.Scope
- In: Schema, TypeScript types inferred from zod, ref resolution, default filling
- In: Clear validation errors naming the file and JSON path
- In: Schema version field for future migrations
- Out: Specific creatures (E13 Bestiary / world-content group)
- Out: Runtime behaviour for each section — later E12/E11 items consume the fields
- Out: Dialogue content (E22/E14)
Notes
Section sub-schemas are owned by later items (e12-sense-profiles, e12-locomotion-types, e12-attack-definitions, …); this item creates the envelope with stub sub-schemas they extend. Keep refs string-typed (
attack:goblin-slash) and resolved at load. Related: E13 Bestiary and E14 Social Creatures consume this schema.Acceptance criteria
- AC-1 content Given a creature file containing only
id,family,stats,sensesandlocomotion, when validated, then it passes and all optional sections are filled with the documented defaults. - AC-2 content Given a creature file missing
stats.health, when validated, then validation fails with an error message containing the file path andstats.health. - AC-3 unit Given a creature referencing
attack:does-not-exist, when the content registry resolves refs, then loading fails listing every unresolved ref (not just the first). - AC-4 unit Given two creature files with the same
id, when loaded, then loading fails with a duplicate-id error naming both files. - AC-5 unit Given a valid creature def, when round-tripped through serialize → parse, then the result deep-equals the input.
- AC-6 content Given the schema, when
pnpm content:docsruns, then a field reference table is generated atdocs/content/creature-schema.mdwith every field, type and default.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
Closed
Completed in PR #40 (2026-09-28)
area:contentarea:simepic:e12milestone:m1mw-e12.2Add sense profile data: sight cones, hearing, smell, special senses
Description
Why
Creatures should perceive differently — a spider feels vibration, a wraith senses life, a goblin sees in the dark. Unique senses make stealth a puzzle rather than one global rule.
What
A
SenseProfilesub-schema and data files:sight(near/far range m, primary and peripheral half-angles, vertical half-angle, dark-vision factor 0–1, detection speed multiplier),hearing(sensitivity dB threshold, range cap),smell(range m, wind-sensitivity flag, tracks scent trails flag), and extensiblespecialsenses (tremor,life-sense,magic-sense) each with range and gating properties. Profiles are reusable (senses: "sense:humanoid-guard") and overridable per creature.Scope
- In: Sub-schema + validation
- In: Three baseline profiles used by test fixtures: humanoid, beast, undead
- In: Merge semantics for per-creature overrides
- Out: Runtime evaluation of senses (e11-perception)
- Out: Smell trail simulation beyond hooks (post-MVP)
Notes
Units are metres, degrees and seconds everywhere. Special senses are declared as data with a
channelname so e11-perception can register handlers without schema changes.Acceptance criteria
- AC-1 content Given a sense profile with
sight.primaryHalfAnglegreater thansight.peripheralHalfAngle, when validated, then validation fails with a message naming both fields. - AC-2 unit Given a creature referencing
sense:humanoidand overridingsight.farRange: 30, when resolved, then far range is 30 and every other sight field equals the base profile. - AC-3 content Given the baseline humanoid profile, when validated, then it defines sight far range between 15 and 30 m and hearing threshold between 20 and 40 dB (documented tuning band).
- AC-4 content Given a profile declaring an unknown special sense channel, when validated, then validation fails listing the registered channels.
- AC-5 unit Given a profile with negative range values, when validated, then validation fails.
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #48 (2026-09-28)
area:contentarea:simepic:e12milestone:m1mw-e12.3Author three grey-box creature fixtures for AI and stealth tests
Description
Why
AI and stealth tests need stable, deliberately boring creatures that are not bestiary content so tuning the real bestiary never breaks rule tests.
What
Three test-fixture creature defs in
src/content/fixtures/creatures/:fixture-guard(humanoid, sight-dominant, patrols, can communicate),fixture-hound(beast, hearing + smell dominant, fast),fixture-sentinel(undead, life-sense, never sleeps, ignores darkness). Each uses placeholder capsule meshes and the baseline sense profiles.Scope
- In: Fixture data + README explaining that fixtures are frozen for tests
- In: Excluded from shipping content bundles
- Out: Real bestiary creatures (E13)
- Out: Art (capsules only)
Notes
Changing fixture values requires updating dependent scenario tests; add a header comment saying so.
Acceptance criteria
- AC-1 content Given the three fixture files, when validated against
CreatureDef, then all pass. - AC-2 unit Given a production content build, when bundled, then no fixture creature id appears in the output manifest.
- AC-3 unit Given
fixture-sentinel, when its resolved senses are inspected, then dark-vision factor is 1.0 and it declares thelife-sensechannel. - AC-4 content Given each fixture, when its content smoke test runs, then it spawns, ticks 600 sim frames without error and despawns.
Waits on
- mw-e12.2 Add sense profile data: sight cones, hearing, smell, special senses done
Closed
Completed in PR #63 (2026-09-28)
area:contentarea:qaepic:e12milestone:m1mw-e12.4Spawn data-defined creatures into the sim and grey-box scenes
Description
Why
Designers and tests must be able to put any creature into the world from data alone, fast.
What
As a designer, I want to spawn any creature by id at a position (from level data, debug console or tests) so that I can iterate on encounters without code. The spawner builds the entity/component set from a resolved
CreatureDef(transform, health, senses, locomotion, behaviour, faction, needs), binds a placeholder capsule render proxy insrc/game, and supports despawn and respawn. Debug console:spawn <creatureId> [count] [at-cursor],despawn all.Scope
- In: Sim-side factory (pure) + game-side render binding
- In: Level-data spawn points with optional patrol route and faction override
- In: Console commands
- Out: Spawn waves / encounter director (E26 content)
- Out: Final meshes (asset integration beads)
Notes
Factory must be deterministic: entity ids allocated from the sim entity store, any per-spawn variance from seeded RNG.
Acceptance criteria
- AC-1 unit Given
fixture-guardand a seed, when spawned twice in fresh sims with the same seed, then both entity component snapshots hash identically. - AC-2 unit Given an unknown creature id, when spawn is requested, then no entity is created and a typed
SpawnErroris returned (sim does not throw). - AC-3 integration Given the grey-box testbed, when
spawn fixture-hound 3is run in the debug console, then 3 hound entities exist with render proxies within 1 frame. - AC-4 unit Given a spawn point with a faction override, when spawned, then the entity faction equals the override, not the def default.
- AC-5 e2e Given the grey-box testbed, when a level with 4 spawn points loads, then 4 creatures are visible and the console shows no errors.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #126 (2026-10-02)
area:gamearea:simarea:toolsepic:e12milestone:m1mw-e12.5Data-driven creature attack definitions bound to the damage model
Description
Why
Combat is intrinsically fun only when attacks are readable and varied; creatures need telegraphed, data-defined attacks that behave consistently with player combat.
What
An
AttackDefschema: id, kind (melee|projectile|area|grab|special), range min/max m, windup/active/recovery frames, telegraph cue id, hitbox shape ref, damage packets (type + amount + poise), cooldown s, selection weight, preconditions (distance, target stance, own health band). A sim executor runs an attack’s phases and emits hitbox activations to the damage model.Scope
- In: Schema + executor for melee and projectile kinds
- In: Telegraph events for render/audio to consume
- In: Preconditions evaluated deterministically
- Out: Choosing which attack to use (e11-combat-behaviour)
- Out: Grab/special runtime beyond a stub event (post-MVP or bestiary items)
Notes
Frame counts are in fixed sim ticks. Telegraph cues are string ids so placeholder sfx/vfx can be swapped later.
Acceptance criteria
- AC-1 unit Given a melee attack with windup 12, active 4, recovery 10 ticks, when executed, then the hitbox is active exactly on ticks 13–16 and the attack completes on tick 26.
- AC-2 unit Given an attack executing, when the attacker is staggered during windup, then the attack is cancelled and no hitbox activates.
- AC-3 content Given an attack with
range.min>range.max, when validated, then validation fails. - AC-4 unit Given a telegraph cue id, when windup starts, then exactly one
AttackTelegraphevent is emitted on the event bus with the attacker id and cue id. - AC-5 integration Given
fixture-guardattacking a player hurtbox in range, when the active window overlaps, then the damage model receives one damage packet per packet definition.
Waits on
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #66 (2026-09-28)
area:contentarea:simepic:e12milestone:m1mw-e12.6Support walker, climber, flier, swimmer and burrower locomotion data
Description
Why
Verticality and alternate routes only matter if creatures move differently: a spider on the ceiling, a harpy over a chasm, a hound that cannot climb.
What
A
LocomotionModeschema (walk,climb,fly,swim,burrow,wallcrawl) with speeds per gait (sneak/walk/run), step height, jump height, max slope, and nav-area permissions. Creatures list one or more modes; the navigation layer receives a per-creature nav capability mask derived from them.Scope
- In: Schema + capability mask derivation
- In: Gait speeds used by patrol/investigate/combat
- In: Area-cost multipliers (e.g. swimmer prefers water)
- Out: Navmesh generation itself (e11-navigation)
- Out: Flight physics beyond kinematic movement (post-MVP)
Notes
Mask is a bitfield so nav queries stay cheap. Mode transitions (walk→climb) are nav link types.
Acceptance criteria
- AC-1 unit Given a creature with only
walk, when its capability mask is derived, thenclimb,flyandswimbits are 0. - AC-2 unit Given a creature with
walkandclimb, when the mask is derived, then climb-only nav links are traversable for it and not for a walk-only creature. - AC-3 content Given a locomotion entry with
runspeed lower thanwalkspeed, when validated, then validation fails. - AC-4 unit Given a creature with no locomotion modes, when validated, then validation fails (every creature must move or declare
stationary).
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #53 (2026-09-28)
area:contentarea:simepic:e12milestone:m1
M2 Class fantasies 4
mw-e12.14Persist creature runtime state across save and load
Description
Why
Failure creates stories only if consequences persist: a guard who is searching, a befriended hound or a knocked-out patrol must survive a reload.
What
A creature state serializer covering position/facing, health, alert state + timers, memory (last-known positions), morale, needs, disposition, current behaviour node/intent, carried inventory changes (pickpocketed items), and status (
alive,unconscious,dead,befriended). Integrates with the versioned save format and world state store.Scope
- In: Serializer + migration test scaffold
- In: Deterministic resume: a loaded sim continues identically to an unsaved one
- Out: Save UI (E30)
- Out: Global world facts (e27-world-state-store owns them; this item writes creature facts into it)
Notes
Resume determinism is the key property: save at tick N, load, run to N+M, hash must equal an uninterrupted run.
Acceptance criteria
- AC-1 unit Given a creature in Searching with 12.5 s remaining and an LKP, when saved and loaded, then state, remaining timer and LKP are identical.
- AC-2 integration Given a replay of 600 ticks with a save/load at tick 300, when compared with an uninterrupted replay, then sim state hashes at tick 600 are equal.
- AC-3 unit Given a save from creature-state schema v1 missing
morale, when migrated to v2, then morale defaults to 100 and the migration test passes. - AC-4 unit Given an unconscious creature, when saved and loaded, then it remains unconscious with its wake timer preserved.
Waits on
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #182 (2026-10-03)
area:simepic:e12milestone:m2mw-e12.8Faction table and relationship queries between creatures
Description
Why
Goblins that fight skeletons, wolves that avoid trolls and guards that tolerate a disguised thief all need one relationship model.
What
Faction data (
factions.json): id, default stance toward each other faction (ally,friendly,neutral,wary,hostile,prey,predator) and toward the player. Runtime APIrelation(a, b)combining faction stance, per-creature overrides and dynamic modifiers (e.g. the player attacked this faction). Relationship changes emit events.Scope
- In: Faction schema + symmetric/asymmetric stances
- In: Dynamic faction-wide modifiers stored in sim state (save-compatible)
- In: Query API used by perception/alert/combat
- Out: Individual disposition toward player (e12-dispositions)
- Out: Story-specific factions (E39/E13 content)
Notes
Asymmetry is allowed (wolves see sheep as prey; sheep see wolves as predator).
Acceptance criteria
- AC-1 unit Given faction A hostile to B, when
relation(a, b)is queried for members, then it returnshostile. - AC-2 unit Given an asymmetric definition A→B
preyand B→Apredator, when queried both ways, then each direction returns its own stance. - AC-3 unit Given a creature-level override making one member of A friendly to the player, when queried, then that creature returns
friendlyand other A members keep the faction default. - AC-4 content Given a faction referencing an undefined faction id, when validated, then validation fails.
- AC-5 unit Given the player kills a member of faction A witnessed by another A member, when the rule runs, then A’s dynamic stance toward the player worsens one step and a
FactionRelationChangedevent is emitted once.
Waits on
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
Closed
Completed in PR #67 (2026-09-28)
area:contentarea:simepic:e12milestone:m2mw-e12.9Per-creature disposition toward the player with event modifiers
Description
Why
Some creatures can be tricked, helped, befriended or angered; that needs a persistent per-individual attitude, not just a faction flag.
What
Disposition value −100..100 per creature toward the player, seeded from def + faction. Data-driven modifier rules map sim events to deltas (
attacked: -40,fed: +15,witnessed-theft: -20,helped-ally: +25). Thresholds map to stances (hostile < −50 ≤ wary < −10 ≤ neutral ≤ 10 < friendly ≤ 50 < devoted). Decay toward baseline is optional per creature.Scope
- In: Runtime value, modifier rules, thresholds, decay
- In: Events for UI/dialogue to consume
- In: Save serialization of values
- Out: Dialogue conditions using disposition (e22-dialogue-runtime)
- Out: Social creature content (E14)
Notes
Modifier rules are content data so E14 can add new event types without code changes.
Acceptance criteria
- AC-1 unit Given a creature at disposition 0, when the player attacks it, then disposition becomes −40 and stance becomes
wary. - AC-2 unit Given disposition 95, when a +15 modifier applies, then it clamps to 100.
- AC-3 unit Given decay rate 1/min toward baseline 0, when 10 sim minutes pass from 20, then disposition is 10.
- AC-4 unit Given a modifier rule for an event type that never fires, when the sim runs 10,000 ticks, then disposition is unchanged (no drift).
- AC-5 integration Given a saved game with a creature at −37, when loaded, then disposition is exactly −37.
Waits on
- mw-e12.8 Faction table and relationship queries between creatures done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #408 (2026-11-12)
area:simepic:e12milestone:m2mw-e12.15Auto-generate smoke tests for every creature data file
Description
Why
The test policy requires every creature to have at least one automated test; generating them keeps the bestiary cheap to extend.
What
A Vitest harness that discovers every
CreatureDefand runs a standard smoke battery in a headless sim: spawn, tick 10 s, perceive a scripted stimulus (alert state leaves Unaware), execute each declared attack once against a dummy, take lethal damage and die (or respect immunity), despawn. Creature files can opt into extra scenario tests.Scope
- In: Discovery, battery, readable per-creature test names
- In: CI integration
- Out: Balance testing (E13 / E34 telemetry)
Notes
Test names follow
creature:<id> smoke — <step>so failures point to data.Acceptance criteria
- AC-1 integration Given N creature files, when the suite runs, then N × (battery size) named tests execute.
- AC-2 integration Given a creature whose attack references a missing hitbox, when the suite runs, then only that creature’s attack step fails with the ref name in the message.
- AC-3 integration Given a creature immune to all damage types, when the lethal-damage step runs, then the step asserts survival instead of death.
- AC-4 unit Given a creature with no attacks, when the battery runs, then the attack step is skipped and reported as skipped, not passed.
Waits on
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.4 Spawn data-defined creatures into the sim and grey-box scenes done
- mw-e12.5 Data-driven creature attack definitions bound to the damage model done
Closed
Completed in PR #215 (2026-10-04)
area:contentarea:qaarea:toolsepic:e12milestone:m2
E13BestiaryThe ten MVP creature types from the story bible (the Forgotten, Rootcellar Goblins, Brother Horn, Loom Spiders, Briar Wolves, Hushlings, Tallow Ooze, Mimics, Hollow Sentinels, the Sleepless Abbot) exist as data on e12-creature-schema with behaviours, property-driven vulnerabilities, readable telegraphed attacks and at least one avoid/trick option each. Horn and the Abbot have authored boss encounters.1/19 done · 5%
M1 Grey-box slice 1
mw-e13.1Add the Forgotten skeleton with the e04 three-move set
Description
Why
The skeleton is the game's first creature and the m1 fight; it must be fun to fight with no reward.
What
As a player, I want a skeleton that fights with readable, parryable attacks so that my first fight feels intrinsically good.
Creature data forgotten-miner on e12-creature-schema: walker, sight + hearing, no smell; resistances blunt 1.5, pierce 0.5, poison 0 (per e04 example); adopts the e04-creature-attack-hooks greybox skeleton set exactly: overhead chop (parryable), 2-hit slash (parryable), lunging thrust (unparryable, blockable). Placeholder capsule-plus-bones mesh.
Scope
- In: Creature data, move references, resistance table, content test
- Out: Crumble/reassemble (e13-forgotten-reassembly)
- Out: Monk variant and habits (e13-forgotten-habits)
- Out: Model/animation (E37)
Notes
Adopts the e04-creature-attack-hooks greybox skeleton set verbatim. Related: e04-skeleton-fun-validation uses this creature.
Acceptance criteria
- AC-1 content Given forgotten-miner.json, when validated, then it passes CreatureDef and all three moves pass the readability rule (windup >= 18 ticks; the thrust flagged unparryable).
- AC-2 integration Given the knight parries the overhead chop in window, when resolved, then the skeleton enters Parried and a riposte is available.
- AC-3 unit Given a 30-blunt hit, when resolved against the skeleton, then 45 damage is applied; given 30 poison, then 0 and immune feedback (edge).
- AC-4 integration Given the creature arena, when spawned, then it ticks 600 frames, perceives a player at 10 m in lit space and enters Combat.
Waits on
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e12.4 Spawn data-defined creatures into the sim and grey-box scenes done
- mw-e12.5 Data-driven creature attack definitions bound to the damage model done
Closed
Completed in PR #144 (2026-10-02)
area:contentclass:allepic:e13milestone:m1
E15Puzzle FrameworkDesigners compose systemic puzzles from reusable world objects and data — not custom code. A puzzle is a data file naming world entities, a goal condition over live properties and world facts, declared solutions per capability set, and reset/hint policies. Tooling proves each declared solution is reachable, detects softlocks, and reports which classes can solve each puzzle, so "several solutions" is verified rather than hoped for.12/33 done · 36%
M2 Class fantasies 8
mw-e15.1Define the puzzle data schema with goals, solutions and policies
Description
Why
Puzzles are data, not code (contract §1). A schema is the contract between designers, the runtime and the validator.
What
A zod schema for content/puzzles/*.json: id, region, entity refs (by level entity id), goal condition (expression over live properties and world facts), declared solutions (name, required capability set, abstract step list for the validator), reset policy, hint ladder, tags (mvp, class focus), and rewards/fact outputs.
Scope
- In: Schema + example puzzle + schema tests
- In: Cross-reference validation against level entity ids
- Out: Runtime evaluation — see e15-puzzle-kit
- Out: Goal expression language — see e15-goal-conditions
Notes
Capability sets reference e19 capability ids (e.g. spell.frost, arrow.rope, climb.rough). Related: e19-progression-core defines capability ids.
Acceptance criteria
- AC-1 content Given the example puzzle file, when validated, then it passes and every field is documented.
- AC-2 content Given a puzzle referencing an entity id absent from its level, when validated, then validation fails naming the id and level.
- AC-3 content Given a puzzle tagged mvp with fewer than 2 declared solutions, when validated, then validation fails (edge).
- AC-4 content Given a solution requiring an unknown capability id, when validated, then validation fails with the id.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
Closed
Completed in PR #114 (2026-09-30)
area:contentepic:e15milestone:m2size:smw-e15.2Express puzzle goals as conditions over live properties and facts
Description
Why
Puzzle goals must describe outcomes ("all braziers lit", "a beam reaches the sensor", "≥ 40 kg on the plate") rather than steps, so any method that achieves the outcome counts.
What
As a designer, I want to write goals like all(entities tagged brazier-3, burning) so that any way of lighting them solves the puzzle.
A small, deterministic condition language: property predicates on entity refs or tag sets (all/any/count ≥ n), signal state, world facts (via e27 queries), spatial predicates (entity inside volume), boolean combinators, and sustained-for duration. Compiled once to a closure; evaluated each tick only when dependencies change.
Scope
- In: Grammar/JSON AST + compiler + dependency tracking
- In: Predicates: property, signal, fact, inVolume, sustained(seconds)
- In: Human-readable explain() for debug and hints
- Out: Fact store — see e27-world-state-store
Notes
Shares the fact predicate primitives from e27-fact-queries rather than duplicating them.
Acceptance criteria
- AC-1 unit Given goal all(tag:brazier, burning), when 2 of 3 braziers burn, then it is false; when the third ignites by any source, then it becomes true on that tick.
- AC-2 unit Given goal sustained(inVolume(any weight≥40, plateZone), 3s), when weight is held for 2.9 s then removed, then it stays false.
- AC-3 unit Given a goal whose dependencies did not change this tick, when the runtime ticks, then the goal is not re-evaluated (dependency tracking).
- AC-4 unit Given a malformed goal AST (unknown predicate), when compiled, then compilation fails with a path to the node (edge).
- AC-5 unit Given any goal, when explain() is called, then it returns a readable string listing each sub-condition and its current truth value.
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e15.1 Define the puzzle data schema with goals, solutions and policies done
- mw-e27.5 Query world facts with a shared condition API for all systems done
Closed
Completed in PR #375 (2026-11-10)
area:simepic:e15milestone:m2size:mmw-e15.3Run data-defined puzzles composed from reusable world objects
Description
Why
Designers can construct systemic puzzles from reusable world objects instead of custom-coding every puzzle (E15 outcome).
What
As a designer, I want to drop a puzzle file next to a level and have it tracked, solved, rewarded and persisted so that I never write TypeScript for a puzzle.
Puzzle runtime: loads puzzle definitions for the active level, binds entity refs, tracks lifecycle (dormant → discovered → in-progress → solved/failed), evaluates goal conditions, fires outputs (signals, world facts, rewards), detects which declared solution path was used where possible (by capability usage trail), and persists state via e27.
Scope
- In: Lifecycle state machine and discovery triggers (volume enter / first interaction)
- In: Outputs: signal emit, fact set, reward hook, one-shot vs repeatable
- In: Solution attribution from stimulus/capability trail
- In: Designer guide docs/design/puzzles.md
- Out: Prefab pieces — see e15-prefab-pieces
- Out: Validation tooling — see e15-solvability-validator
Notes
Unanticipated solutions are welcome: if the goal becomes true via an undeclared route, attribution records "undeclared" and emits a telemetry event — valuable designer feedback.
Acceptance criteria
- AC-1 unit Given a puzzle in dormant state, when the player enters its discovery volume, then it becomes discovered and emits puzzleDiscovered.
- AC-2 unit Given an in-progress puzzle, when its goal becomes true, then it becomes solved, emits configured signals and sets its solved fact exactly once.
- AC-3 unit Given a solved one-shot puzzle, when its goal later becomes false (e.g. brazier extinguished), then it remains solved and outputs are not re-fired.
- AC-4 unit Given the goal was reached using capabilities not matching any declared solution, when solved, then attribution = undeclared and a puzzleSolvedUndeclared event fires (edge).
- AC-5 integration Given a solved puzzle, when saved and reloaded, then it is still solved and its outputs (door open) are restored without re-firing rewards.
- AC-6 integration Given the greybox brazier puzzle, when the bot lights all braziers with a torch in one replay and a fire stimulus from range in another, then both replays solve it.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e15.2 Express puzzle goals as conditions over live properties and facts done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #391 (2026-11-10)
area:simclass:allepic:e15milestone:m2size:mmw-e15.4Ship reusable puzzle prefabs built only from world components
Description
Why
Fast content creation: designers compose from a kit rather than asking engineers for "a mirror that turns".
What
As a designer, I want a palette of puzzle prefabs so that I can assemble puzzles in minutes.
Prefabs (data only, composed from e03 components): rotatable statue (facing sensor), rotatable mirror, lens stand, weighted crate set (5/10/20/40 kg), brazier (flammable + receiver), water basin with valve and drain, counterweight lift, sliding block (grid pushable), bell, crystal receiver, beam sensor, scale plate pair, timed door, rune tile (sequence input).
Scope
- In: 15 prefabs + per-prefab tests exercising their property behaviour
- In: Prefab catalogue docs generated from data
- Out: Final models/art — see mw-e37 asset beads
Notes
Rule: a prefab may not introduce a new TypeScript component. If one is needed, it goes to e03 first as a general property/component.
Acceptance criteria
- AC-1 content Given each prefab, when validated, then it references only existing e03 components and passes schema.
- AC-2 unit Given the rotatable statue prefab facing north, when rotated 4 steps, then its facing sensor emits the facing value east/south/west/north in order.
- AC-3 unit Given the basin prefab with valve opened, when simulated, then water level drops and a submerged plate at its bottom becomes reachable.
- AC-4 unit Given a prefab instance with a required override missing (e.g. mirror without step angle), when loaded, then default values apply or validation fails if no default exists (edge).
- AC-5 content Given the prefab directory, when the docs generator runs, then docs/design/puzzle-prefabs.md lists all prefabs with their properties and outputs.
Waits on
- mw-e02.18 Push and pull heavy objects such as crates and statues done
- mw-e03.17 Redirect light beams with mirrors and lenses onto receivers done
- mw-e03.19 Activate pressure plates by total weight of anything resting on them done
- mw-e03.6 Simulate water volumes, splashes, wetness and drying done
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
Closed
Completed in PR #392 (2026-11-10)
area:contentclass:allepic:e15milestone:m2size:mmw-e15.6Prove declared puzzle solutions reachable with a headless solver
Description
Why
Multiple solutions must be verified, not hoped for. A headless solver catches broken puzzles before playtests do.
What
As a designer, I want to run a validator that proves each declared solution works for its capability set so that I know my puzzle is solvable before anyone plays it.
Two layers: (1) abstract search — a BFS/A* over an abstracted puzzle state (mechanism states, object cells, element states) with actions derived from the capability set (push, rotate, ignite, freeze, levitate, shoot, climb), proving the goal reachable within a step bound; (2) concrete confirmation — each declared step list is executed in the headless sim with scripted actions to confirm the goal. Reports unreachable solutions, minimum steps, and shortcut solutions found with fewer capabilities.
Scope
- In: Abstract state extraction from puzzle + level data
- In: Capability → action generators; bounded search with memoisation
- In: Headless concrete execution of declared step lists via e00-replay-harness
- In: CLI: pnpm puzzle:validate [id] + CI job
- Out: Softlock detection — see e15-softlock-guard
- Out: Class coverage report — see e15-solution-audit
Notes
Abstraction must be conservative; when the abstract solver says unreachable but the concrete run succeeds, report an abstraction gap. Time-box per puzzle 10 s in CI.
Acceptance criteria
- AC-1 unit Given a brazier puzzle with a declared solution requiring spell.fire, when validated, then the solver finds a path and reports minimum step count.
- AC-2 unit Given a puzzle whose only declared solution needs a crate that the level does not contain, when validated, then it reports the solution unreachable with the missing requirement.
- AC-3 unit Given a puzzle solvable with an empty capability set although tagged class-specific, when validated, then it reports a shortcut solution (informational warning).
- AC-4 integration Given each declared step list, when executed in the headless sim, then the goal becomes true; failures show the step index where it diverged.
- AC-5 unit Given a state space exceeding the search bound, when validated, then it stops at the bound within 10 s and reports inconclusive rather than hanging (edge).
- AC-6 integration Given CI, when any mvp puzzle has an unreachable declared solution, then the job fails.
Waits on
- mw-e00.17 Record inputs and replay them to identical state hashes in tests done
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
Closed
Completed in PR #393 (2026-11-10)
area:toolsepic:e15milestone:m2size:lmw-e15.7Detect puzzle softlocks and recover with diegetic resets
Description
Why
Failure creates stories, not dead saves. Burning the only crate or dropping the key object into a chasm must not permanently block progress (or must be a deliberate, alternate-route design).
What
As a player, I want puzzles to recover when I destroy or lose a key object so that experimenting never bricks my save.
Validator side: search for reachable irreversible states from which the goal is unreachable and require a declared recovery policy. Runtime side: recovery policies — respawn source (crate chute drops a new crate after N s), reset lever (restore mechanism states), reversible-only flag, or alternate route declared. Resets are diegetic and persisted.
Scope
- In: Softlock search extension to the solver (irreversible transitions: burn, break, fall out of bounds, melt)
- In: Runtime policies: respawner, reset lever/volume, out-of-bounds return
- In: Reset fact + telemetry event
- Out: Save rollback — out of scope; recovery is in-world
Notes
Objects falling out of the world are returned to their spawner via an out-of-bounds trigger (property-based: any entity with puzzleCritical=true).
Acceptance criteria
- AC-1 unit Given a weighted-plate puzzle whose only crate is flammable, when the solver explores burning it, then it reports a softlock unless a recovery policy exists.
- AC-2 unit Given a respawner policy of 10 s, when the critical crate is destroyed, then a replacement spawns at the chute after 10 s and emits puzzleRecovered.
- AC-3 unit Given a puzzleCritical object falls below the kill plane, when resolved, then it returns to its spawn point within 1 tick without breaking.
- AC-4 unit Given a reset lever, when pulled, then all mechanism states in the puzzle return to authored defaults and the puzzle state returns to in-progress (unless already solved).
- AC-5 unit Given a respawner whose chute is blocked by an object, when respawning, then it retries each 2 s and never spawns overlapping geometry (edge).
Waits on
- mw-e03.11 Break fragile objects and smash weakened walls into debris done
- mw-e15.6 Prove declared puzzle solutions reachable with a headless solver done
Closed
Completed in PR #395 (2026-11-10)
area:simclass:allepic:e15milestone:m2size:mmw-e15.9Build greybox reference puzzles for light, freezing and weight
Description
Why
The kit is only proven when real puzzles are built with zero code. These cover the first three constitution examples.
What
As a designer, I want three reference puzzles in greybox so that the kit is proven and future designers have templates.
(1) Sunbeam: redirect light via mirrors to a sensor — also solvable by igniting a brazier near the sensor or a light spell. (2) Frozen channel: cross a water channel — freeze it, float crates, levitate across or climb the ceiling beams. (3) Scales: two plates must hold equal weight — crates, bodies, an ice block, or standing on one yourself while levitating a crate on the other.
Scope
- In: 3 puzzle files + greybox rooms + scenario replays for each declared solution
- In: Each ≥ 2 validated solutions (target 3–4)
- Out: Final dungeon placement and art — see mw-e26
Notes
Zero new TypeScript permitted; any engine gap discovered becomes a bead in e03/e15.
Acceptance criteria
- AC-1 content Given the 3 puzzle files, when validated, then each passes schema and the solvability validator with ≥ 2 distinct solutions.
- AC-2 integration Given each declared solution, when its replay runs in CI, then the puzzle reaches solved.
- AC-3 integration Given the git diff of this item, when inspected by CI script, then no files under src/ are changed (data-only proof).
- AC-4 integration Given the scales puzzle, when the player removes weight after solving, then the puzzle stays solved (one-shot) and the door stays open (edge).
- AC-5 manual Given 3 playtesters, when each attempts the puzzles, then at least 2 distinct solutions are observed per puzzle across testers.
Waits on
- mw-e03.12 Let external forces hold, levitate and fling liftable objects done
- mw-e03.7 Freeze water and wet objects into ice and melt them with heat done
- mw-e15.4 Ship reusable puzzle prefabs built only from world components done
- mw-e15.6 Prove declared puzzle solutions reachable with a headless solver done
Closed
Completed in PR #397 (AC-5 manual playtest left for owner) (2026-11-10)
area:contentarea:designclass:allepic:e15milestone:m2size:mmw-e15.29Fix testbed-room-lever puzzle: declared solutions diverge in the headless run (fact testbed.room-lever.thrown stays false)
Description
Found by mw-e15.8's coverage report: testbed-room-lever (non-mvp) has 0 validated solutions; pnpm puzzle:validate testbed-room-lever shows all three declared solutions DIVERGED (every step ran, goal fact false). Not gating (not mvp) but its coverage row is inconclusive.
Closed
Duplicate of mw-e15.19 (filed by mistake) (2026-11-10)
area:simarea:toolsepic:e15milestone:m2
M3 MVP content 4
mw-e15.5Support ordered, timed and sound-clue puzzle mechanisms
Description
Why
"Following sounds" and "interpreting books" are named puzzle types; ordered/timed mechanisms generalise bell towers, rune floors and race-the-door puzzles.
What
As a player, I want to hear chimes behind a wall and replay their order on bells so that listening and observation are puzzle skills.
Prefab-level wiring using e03 logic nodes: sound-clue emitters (play a sequence of positional cues on trigger), sequence validators with tolerance, timed windows (open door for N seconds after plate), and book-clue binding (a readable item text references puzzle ids for hint attribution).
Scope
- In: Sound clue emitter component (cue list, spacing, positional audio via e28)
- In: Sequence + timed window prefab compositions
- In: Readable clue binding for attribution
- Out: Book reading UI — see mw-e17 / mw-e07
Notes
Accessibility: every sound clue has a subtitle/visual equivalent (E31) — e.g. subtitle "[high chime, left]".
Acceptance criteria
- AC-1 unit Given a sound clue emitter with cues [low, high, mid], when triggered, then three cue events fire in order at the configured spacing (1.0 s).
- AC-2 unit Given a sequence validator expecting [low, high, mid], when the player strikes bells low, high, mid, then success fires; any wrong bell resets and fires a failure cue.
- AC-3 unit Given a timed door of 8 s, when the plate is released and the player passes at 8.1 s, then the door is closed.
- AC-4 unit Given subtitles enabled, when a clue cue fires, then a subtitle event with its direction label is emitted (edge: accessibility).
Waits on
- mw-e15.4 Ship reusable puzzle prefabs built only from world components done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
Closed
Completed in PR #396 (2026-11-10)
area:simclass:allepic:e15milestone:m3size:smw-e15.8Report and enforce per-class solution coverage for puzzles in CI
Description
Why
"Problems support multiple solutions" and "Classes change possibilities" must be measurable across the MVP.
What
A report (docs/design/puzzle-coverage.md, regenerated in CI) listing each puzzle × {knight, archer, sorcerer, thief, any} with validated solvability, minimum capability sets and step counts. CI rule: every mvp puzzle ≥ 2 validated distinct solutions and solvable by at least 3 of 4 classes at MVP capability levels.
Scope
- In: Class capability profiles at MVP progression milestones (data)
- In: Report generator + CI gate + PR diff summary
- Out: Encounter-level route audits — see mw-e16
Notes
Related: mw-e16 class-specific solutions will reuse the class capability profiles; e19-progression-core supplies capability ids.
Acceptance criteria
- AC-1 integration Given all puzzles, when the audit runs, then the report lists every puzzle with per-class solvable yes/no/inconclusive.
- AC-2 integration Given an mvp puzzle solvable by only 2 classes, when CI runs, then the gate fails naming the puzzle and missing classes.
- AC-3 unit Given two declared solutions that reduce to the same capability set and step list, when counted, then they count as 1 distinct solution (edge).
Waits on
- mw-e15.6 Prove declared puzzle solutions reachable with a headless solver done
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
Closed
Completed in PR #398 (2026-11-10)
area:toolsepic:e15milestone:m3size:smw-e15.10Build greybox reference puzzles for statues, darkness and sound
Description
Why
Completes the six constitution puzzle archetypes, exercising statues, darkness navigation and sound following.
What
As a designer, I want three more reference puzzles so that all six constitution examples are proven.
(4) Watchers: rotate statues so all face the altar — push/rotate, pull remotely via levitation, or knock one over (breakable) to satisfy "no statue faces the door". (5) Dark crossing: navigate a pit room where light reveals safe tiles — torches, light spell, relighting braziers, or following the drafts in the smoke. (6) Chimes: reproduce a chime order heard behind a wall — or shoot the bells in order from a balcony, or find the order written in a book.
Scope
- In: 3 puzzle files + greybox rooms + replays for each declared solution
- Out: Final placement — see mw-e26
Notes
Watchers deliberately includes a destructive solution (breaking a statue) — confirm softlock policy covers it.
Acceptance criteria
- AC-1 content Given the 3 puzzle files, when validated, then each passes with ≥ 2 distinct solutions.
- AC-2 integration Given each declared solution replay, when run in CI, then the puzzle reaches solved.
- AC-3 integration Given the Watchers puzzle, when a statue is destroyed, then the puzzle remains solvable (validator) and no softlock is reported (edge).
- AC-4 integration Given the git diff, when inspected, then no files under src/ change.
Waits on
- mw-e03.16 Extinguish and relight torches, candles and braziers systemically done
- mw-e15.5 Support ordered, timed and sound-clue puzzle mechanisms done
- mw-e15.7 Detect puzzle softlocks and recover with diegetic resets done
- mw-e15.9 Build greybox reference puzzles for light, freezing and weight done
Closed
Completed in PR #399 (2026-11-10)
area:contentarea:designclass:allepic:e15milestone:m3size:mmw-e15.11Offer escalating diegetic hints when players are stuck on puzzles
Description
Why
Puzzles should create "aha", not abandonment. Hints must be in-world (companion remarks, glinting clues) to preserve discovery.
What
As a player, I want subtle in-world nudges after I have been stuck a while so that I stay engaged without being handed the answer.
Hint ladder per puzzle (data): tiers triggered by time-in-puzzle and failed attempts, each tier a hint action (player bark text, highlight pulse on a relevant object, NPC rumour unlock fact). Hints are class-aware (choose a tier text relevant to current capabilities). Disable-able in settings.
Scope
- In: Hint ladder runtime + data, class-aware selection, settings toggle
- In: Attempt counting from puzzle failure pulses
- Out: Hint UI styling — see mw-e31
Notes
Default thresholds: tier1 at 3 min or 3 failed attempts, tier2 at 6 min, tier3 at 10 min.
Acceptance criteria
- AC-1 unit Given a puzzle in progress for 3 min, when the tick runs, then tier-1 hint fires once and not again for that tier.
- AC-2 unit Given a sorcerer with spell.frost, when a tier with class variants fires, then the frost-relevant text is chosen.
- AC-3 unit Given hints disabled in settings, when thresholds pass, then no hint fires (edge).
- AC-4 unit Given a puzzle solved at 2:59, when time passes, then no hint fires.
Waits on
- mw-e15.3 Run data-defined puzzles composed from reusable world objects done
- mw-e31.1 Create typed settings store and options menu skeleton done
- mw-e31.10 Provide difficulty presets and granular assists read by the sim done
Closed
Completed in PR #400 (2026-11-10)
area:simclass:allepic:e15milestone:m3size:s
E16Class-Specific SolutionsEvery mandatory obstacle in the MVP has deliberate routes for knight, archer, sorcerer and thief (at least three of four, target four), documented in a route matrix, declared as data, proved by replays and enforced by a CI audit.1/7 done · 14%
M2 Class fantasies 1
mw-e16.1Write the MVP obstacle × class route matrix
Description
Why
Classes change possibilities; level designers need one table of every mandatory obstacle and its intended class routes before blockout.
What
docs/design/route-matrix.md: every mandatory obstacle on the critical path (mine gate, supply tunnel, goblin camp passage, Outer Ring gate, Ossuary crossing, Chapel, Horn's Hall/Fold access, Cradle, Sounding Shaft, belfry) the six mine entries including the Weeping Adit, and major optional ones (Vane House strongbox, Mooring's Watch, restricted shelf, and Wendmouth's customs strongroom, bay nine, the Gilded Tern and the Undertow — mandatory within their quests) × knight/archer/sorcerer/thief/any, naming capabilities used and the source of each route in the story bible §4.3.
Scope
- In: Matrix document, obstacle ids (kebab-case) reused by the registry
- Out: Registry data (e16-obstacle-registry)
- Out: Puzzle solution coverage (e15-solution-audit)
Notes
Obstacles vs puzzles: puzzles are audited by e15; obstacles here are traversal/infiltration/encounter gates.
Acceptance criteria
- AC-1 content Given the matrix, when reviewed, then every mandatory obstacle lists >= 3 class routes and at least one "any class" route or fallback.
- AC-2 content Given the matrix, when reviewed, then every route names capability ids that exist in the e19 capability data or are flagged "needs capability" with an owning epic.
- AC-3 content Given obstacles with only 2 class routes, when found, then each is listed in a "gaps" section with a proposed fix (edge).
Waits on
- mw-e19.2 Create capability registry with source-tracked grants done
- mw-e24.1 Design the world map: areas, loops, shortcuts and ability gates done
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #437 (2026-11-12)
area:designclass:allepic:e16milestone:m2
E17Inventory & EquipmentWeapons, armor, books, keys, consumables, quest objects and unusual artifacts are manageable without inventory busywork. Equipment changes what the player can *do* (new verbs, altered stealth/noise, light, traversal), not only numbers. All rules live in `src/sim` with data in `src/content`.13/19 done · 68%
M1 Grey-box slice 12
mw-e17.1Spike: decide encumbrance model (weightless vs light encumbrance)
Description
Why
The epic outcome forbids inventory busywork, but some weight is useful when it creates decisions (heavy armor = loud and slow for stealth). We need a decision before the inventory model hardens.
What
Time-box: 1 day. Question: which inventory capacity model best serves "no busywork" while keeping equipment choices meaningful? Evaluate (a) fully weightless with category tabs, (b) weightless inventory + equipped-armor weight that affects noise/stamina/movement verbs, (c) light encumbrance with generous soft cap, (d) grid/slot inventory. Also decide whether large world objects (crates, bodies, statues) are carried physically instead of stored.
Scope
- In: Comparison matrix against constitution pillars, reference games (QfG5, DOS2, Skyrim, Zelda BotW)
- In: ADR in docs/adr/ with recommendation and consequences for mw-e17/mw-e10/mw-e09
- Out: Implementation (e17-inventory-core)
Notes
Leaning: option (b) — inventory is weightless; only *equipped* armor/weapon weight class feeds e09 noise and stamina. Physically carried world objects handled by e03-physics-objects.
Acceptance criteria
- AC-1 manual Given the spike is complete, when the ADR is reviewed, then docs/adr/ contains an encumbrance ADR with status "Accepted", the options matrix, and the chosen model.
- AC-2 manual Given the ADR, when read, then it states explicitly how (if at all) equipment weight affects stealth noise, stamina and movement speed, with numeric ranges.
- AC-3 manual Given the ADR, when read, then it lists the edge cases the model must handle: picking up an item while "full" (if caps exist), quest items, stacks, and physically carried world objects.
Closed
Completed in PR #130 (2026-10-02)
area:designepic:e17milestone:m1mw-e17.2Define item definition schema and content validator
Description
Why
Items are data, not code (contract §1). Every other systems epic (loot, shops, quests, progression) references item IDs, so a single validated schema is the foundation.
What
A zod schema in src/content/items for item definitions:
id,category(weapon, armor, shield, ammo, book, key, consumable, tool, quest, artifact, currency, misc),nameKey/descKeytext ids,iconasset id,value,stackable+maxStack,weightClass(per encumbrance ADR),worldProperties(e03 tags so a dropped oil flask is flammable),grants(capability ids, see e19-capability-registry),equip(slot + class proficiency tags),use(effect list),flags(unique, questItem, noSell, noDrop).Scope
- In: Schema, TS types inferred from zod, loader integration, validator tests
- In: Cross-ref checks: icon asset ids exist in the asset manifest (placeholder allowed), capability ids exist, unique ids unique
- In: Five fixture items (one per major category) for tests
- Out: Inventory runtime (e17-inventory-core)
- Out: Actual game item content (world-content group)
Notes
Keep
useeffects referencing the shared effect vocabulary from e22-condition-effect-dsl once it lands; until then a minimal effect union is acceptable and migrated later.Acceptance criteria
- AC-1 content Given a valid item fixture for each category, when validated, then validation passes and the inferred type exposes category-specific fields.
- AC-2 unit Given an item with
stackable: falseandmaxStack: 5, when validated, then validation fails with a message naming the item id and field. - AC-3 unit Given two items with the same id, when the item catalogue is loaded, then loading fails listing both source files.
- AC-4 unit Given an item whose
grantsreferences an unknown capability id, when validated against the capability registry, then validation fails naming the capability. - AC-5 unit Given an item flagged
questItem: truewithoutnoSellandnoDrop, when validated, then those flags are defaulted to true.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e17.1 Spike: decide encumbrance model (weightless vs light encumbrance) done
- mw-e19.2 Create capability registry with source-tracked grants done
Closed
Completed in PR #146 (2026-10-02)
area:contentepic:e17milestone:m1mw-e17.3Implement inventory model with stacks, keys, books and quest items
Description
Why
Players need to carry the things they discover without busywork; every loot, shop, quest and puzzle interaction reads or writes the inventory.
What
As a player, I want picked-up items to land in a tidy inventory grouped by category so that I spend time adventuring, not sorting.
Pure sim module:
Inventoryholding item instances{instanceId, defId, count, flags: {stolen?, ownerId?, bound?}}. Operations: add (auto-stack; stolen and non-stolen never merge), remove, move-to-keyring/bookshelf views, query by category/tag/capability, gold as currency counter. Emits events (item.added,item.removed,item.flagChanged) on the sim event bus. Enforces capacity only as the encumbrance ADR dictates.Scope
- In: Inventory data structure, operations, events, category views (keys, books, quest objects, artifacts, consumables)
- In: Quest items cannot be removed except by effects with
force: true - In: Gold counter with overflow protection
- Out: Equip slots (e17-equipment-slots)
- Out: World pickup/drop (e17-world-pickup-drop)
- Out: Serialization (e17-inventory-persistence)
- Out: UI (e17-inventory-ui)
Notes
Instances rather than bare counts so that stolen/owner flags and unique artifacts are tracked individually. Deterministic instance ids from the sim id generator (no Math.random).
Acceptance criteria
- AC-1 unit Given an empty inventory, when 3 then 4 of a stackable item with maxStack 5 are added, then the inventory holds stacks of 5 and 2 and emits two
item.addedevents. - AC-2 unit Given an inventory holding 2 non-stolen apples, when 1 stolen apple is added, then there are two separate stacks and the stolen stack carries its ownerId.
- AC-3 unit Given a quest item in inventory, when a non-forced remove is requested, then removal is rejected with reason
quest-itemand the inventory is unchanged. - AC-4 unit Given an inventory, when querying category
key, then only key-category instances are returned in stable acquisition order. - AC-5 unit Given gold at 2^31-10, when 100 gold is added, then gold clamps at the configured maximum and a
gold.cappedevent is emitted. - AC-6 unit Given a remove of more units than held, when executed, then it fails atomically and no partial removal occurs.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e17.2 Define item definition schema and content validator done
Closed
Completed in PR #148 (2026-10-02)
area:simepic:e17milestone:m1mw-e17.4Add equipment slots with class proficiencies and equip events
Description
Why
Weapons, shields, armor and tools must be equipped to matter; equip state drives combat, stealth noise and granted verbs.
What
As a player, I want to equip weapons, armor and tools so that my character looks and plays differently.
Slots: mainHand, offHand, head, body, hands, feet, trinket×2, ammo (quiver), toolBelt×2. Two-handed items occupy both hands. Class proficiency is soft: non-proficient equips are allowed but apply a data-defined penalty (e.g. slower draw, louder) rather than an outright block, so improvisation is possible. Emits
equip.changedevents; exposes a derivedEquipmentState(weight class, noise modifier, light emitted, granted capabilities list).Scope
- In: Slot model, equip/unequip/swap, two-handed rules, proficiency penalties from data, derived state, events
- Out: What granted capabilities do (e17-equipment-verbs)
- Out: Paper-doll UI (e17-equipment-ui)
- Out: Combat stats consumption (mw-e04/mw-e05)
Notes
Derived noise/weight feed e09-visibility-model and e09-sound-propagation via the derived state; those epics consume it.
Acceptance criteria
- AC-1 unit Given a sword in mainHand and a shield in offHand, when a two-handed greatsword is equipped, then both previous items return to inventory and both hand slots reference the greatsword.
- AC-2 unit Given a sorcerer equipping heavy plate, when equipped, then the equip succeeds and the derived state includes the data-defined non-proficiency penalty.
- AC-3 unit Given an item without an
equipblock, when equip is requested, then it fails with reasonnot-equippable. - AC-4 unit Given any equip change, when it completes, then exactly one
equip.changedevent is emitted with before/after slot maps. - AC-5 unit Given the inventory ADR weight classes, when heavy body armor is equipped, then derived
weightClassisheavyandnoiseModifiermatches the data value.
Waits on
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #150 (2026-10-02)
area:simepic:e17milestone:m1mw-e17.5Auto-use keys from the keyring on matching locks
Description
Why
Keys are core to exploration loops and shortcuts; fumbling through menus to use them is busywork.
What
As a player, I want locked doors and chests to open automatically when I hold the right key so that finding the key is the reward.
Locks (e03-doors-locks-switches) expose
lockId; keys declareopens: [lockId]oropensTag. Interacting with a lock checks the keyring; single-use keys are consumed; master keys open by tag. Missing key produces a contextual prompt hint ("Locked. Perhaps someone in town has the key.") driven by lock data.Scope
- In: Key matching, consumption rules, prompt text hooks, events for quests (
lock.openedwith keyId) - Out: Lockpicking (mw-e10)
- Out: Forcing/smashing locks (mw-e03 / mw-e04)
Integration note: Promoted to P0/m1 — required by the E01 grey-box vertical slice.
Acceptance criteria
- AC-1 unit Given a door with lockId
tower-doorand a key whoseopensincludes it, when the player interacts, then the door unlocks and alock.openedevent records the key id. - AC-2 unit Given a single-use key, when it opens its lock, then it is removed from the inventory; a reusable key remains.
- AC-3 unit Given a lock with no matching key, when interacted with, then the lock stays locked and the prompt shows the lock's
lockedHintKeytext. - AC-4 integration Given the greybox testbed door with a key placed nearby, when the player picks up the key and interacts with the door, then the door opens without opening any menu.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #153 (2026-10-03)
area:simepic:e17milestone:m1- In: Key matching, consumption rules, prompt text hooks, events for quests (
mw-e17.6Use consumables from inventory and assignable quick slots
Description
Why
Potions, food, oil flasks and scrolls are tactical tools; using them mid-fight must be fast and systemic.
What
As a player, I want to drink, eat, apply or throw consumables from four quick slots so that I can react in combat without pausing.
Use pipeline: validate → apply
useeffects (heal, restore stamina/mana, apply status, coat weapon with oil, spawn thrown object) → consume one unit → emititem.used. Throwable consumables spawn a physics object carrying their world properties (a thrown oil flask shatters and leaves a flammable puddle via e03). Quick slots persist and auto-refill from same-def stacks; empty slots display as depleted.Scope
- In: Use pipeline, 4 quick slots, auto-refill, cooldown/animation-lock hook, throwables via world properties
- Out: Quick-slot HUD visuals (e17-inventory-ui)
- Out: Spell scrolls casting (e06-spell-pipeline consumes the effect)
Notes
Effects expressed via property/effect vocabulary, never item-specific scripts (contract §5: properties, not pairings).
Acceptance criteria
- AC-1 unit Given a healing draught in quick slot 1 and 3 in stock, when used, then health increases per data, stock becomes 2 and
item.usedis emitted. - AC-2 unit Given the last unit in a quick slot is used and another stack of the same def exists, when used, then the slot refills from that stack.
- AC-3 unit Given a throwable consumable with
worldProperties: [flammable, liquid], when thrown, then a spawn request is emitted carrying exactly those properties. - AC-4 unit Given a consumable used while a use-lock is active, when the second use is requested, then it is rejected with reason
busyand no unit is consumed. - AC-5 integration Given the greybox testbed, when the player throws an oil flask then ignites it, then the resulting area is on fire via e03 propagation (no flask-specific code).
Waits on
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #154 (2026-10-03)
area:simepic:e17milestone:m1mw-e17.7Pick up, drop and throw items as physical world objects
Description
Why
Items should exist in the world with properties (a dropped key can fall through a grate; a thrown bottle distracts a guard), reinforcing systemic play.
What
As a player, I want to pick up items I see, and drop or throw items from my inventory so that I can use them as bait, distractions or puzzle pieces.
World item entity: mesh placeholder by category, interact prompt "Take <name>", pickup adds to inventory and removes entity; drop/throw spawns an entity with physics and world properties. Pickup respects ownership (owned items marked stolen via e18-ownership-stolen when it lands).
Scope
- In: World item entity, pickup/drop/throw bindings, placeholder meshes per category, noise event on impact via e09 hook
- Out: Containers (e18-containers)
- Out: Ownership logic (e18-ownership-stolen)
Acceptance criteria
- AC-1 integration Given an item entity within interact range, when the player interacts, then the item is added to inventory and the entity is despawned in the same sim tick.
- AC-2 integration Given an item in inventory, when dropped, then an item entity with identical instance flags (e.g. stolen) spawns in front of the player.
- AC-3 unit Given a thrown item that hits a surface, when impact resolves, then a noise event is emitted with loudness from the item weight class.
- AC-4 unit Given a quest item, when drop is requested, then it is rejected with reason
no-drop. - AC-5 e2e Given the greybox testbed, when the player picks up, drops and re-picks the same item, then inventory count returns to the original and no duplicate entity remains.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
Closed
Completed in PR #151 (2026-10-02)
area:gameepic:e17milestone:m1mw-e17.8Serialize inventory, equipment and quick slots into saves
Description
Why
The m1 loop ends with save/reload; losing or duplicating items breaks trust immediately.
What
Register inventory, equipment and quick-slot state with the versioned save format. Includes migration stub, round-trip tests and property-based randomized inventories.
Scope
- In: Save section schema, serialize/deserialize, migration v1 stub, round-trip + fuzz tests
- Out: World item entities in the level (owned by e27 world-state persistence)
Acceptance criteria
- AC-1 unit Given 100 randomized inventories (seeded) with stolen flags, stacks and equipped items, when saved and loaded, then state is deep-equal to the original.
- AC-2 unit Given a save referencing an item def that no longer exists, when loaded, then the instance is dropped, a warning is logged, and the rest of the inventory loads.
- AC-3 unit Given a v1 inventory save, when the migration runner processes it at current version, then it loads without error.
Waits on
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
- mw-e17.6 Use consumables from inventory and assignable quick slots done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #156 (2026-10-03)
area:simepic:e17milestone:m1mw-e17.10Build inventory screen with category tabs, inspect and actions
Description
Why
The m1 loop includes loot; players need to see, inspect and use what they found with minimal friction.
What
As a player, I want an inventory screen organized by category where I can inspect, equip, use, drop and assign items to quick slots so that managing gear takes seconds.
Tabs: All, Weapons & Armor, Tools, Consumables, Books, Keys, Quest & Artifacts. Item card shows icon, name, flavour description (room for QfG-style humour), granted verbs listed as verbs (not numbers first), stolen marker, value. Keyboard+mouse, gamepad navigation; respects text-size setting. Quick-slot HUD strip on the game screen.
Scope
- In: Inventory screen, item inspect panel, context actions, quick-slot assignment, quick-slot HUD, placeholder icons by category
- Out: Paper-doll equipment view (e17-equipment-ui)
- Out: Final icon art (asset prompt beads)
Notes
UI assets assumed: item category placeholder icons, stolen marker icon, gold icon, quick-slot frame, inventory panel frame. Final icons via asset group.
Acceptance criteria
- AC-1 integration Given an inventory with items in 4 categories, when the Books tab is selected, then only book items are listed, sorted by acquisition time descending.
- AC-2 integration Given a stolen item, when its card is displayed, then the stolen marker is visible and the tooltip names the owner if known.
- AC-3 integration Given gamepad input only, when navigating the grid and pressing confirm on a consumable, then a context menu with Use/Assign/Drop opens and each action works.
- AC-4 unit Given text size set to 150%, when the inventory renders, then item names wrap without overlapping and no text is clipped (DOM overflow check).
- AC-5 e2e Given the greybox testbed, when the player opens inventory, assigns a potion to quick slot 2 and closes inventory, then pressing the quick-slot-2 action consumes the potion.
- AC-6 unit Given an empty inventory, when opened, then an empty-state message is shown instead of an empty grid.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e17.6 Use consumables from inventory and assignable quick slots done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #175 (2026-10-03)
area:uiepic:e17milestone:m1mw-e17.12Add debug console commands to give, remove and inspect items
Description
Why
Fast content iteration and bug reproduction require spawning items without playing to them.
What
Debug console commands:
item.give <id> [count] [--stolen owner],item.remove,item.list [category],equip <id>,gold <n>, with id auto-complete from the item catalogue.Scope
- In: Commands, autocomplete, error messages
- Out: Loot/quest commands (their own epics)
Acceptance criteria
- AC-1 unit Given the console, when
item.give healing-draught 3runs, then the inventory gains 3 and the console prints the new count. - AC-2 unit Given an unknown item id, when
item.giveruns, then the console prints the 3 closest ids by edit distance and changes nothing. - AC-3 unit Given
item.give silver-ring 1 --stolen mayor, when executed, then the added instance has stolen=true and ownerId=mayor.
Waits on
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #295 (2026-11-07)
area:toolsepic:e17milestone:m1mw-e17.15Record owner review of ADR-0003 to ADR-0005
Description
Why
The owner reviewed the three ADRs from the 2026-10-02 spikes and changed one number; the ADR files and dependent beads must match.
What
ADR-0003: non-knight armor capacity 23 kg (plate Heavy for all classes); fix heading numbering. ADR-0004: fix cross-references. ADR-0005: status Accepted. Update mw-e17.13 to 23 kg.
Acceptance criteria
- AC-1 content Given the ADRs, when read, then ADR-0003 uses 23 kg non-knight capacity with recomputed load tables, all three headings match their file numbers, and ADR-0005 is Accepted.
Closed
Completed in PR #134 (2026-10-02)
area:simepic:e17milestone:m1mw-e17.18Recover destroyed quest-critical objects at their anchor
Description
Why
mw-e17.14 (PR #304) returns quest-critical objects to their recovery anchor when they fall out of bounds, but a quest-critical object that is broken/destroyed is not respawned (the bead description asked for 'destroyed or out of bounds'). Also mw-e10.5 AC-1 still says x0.6 for bodies while carry uses x0.5.
What
On destruction of an entity with RecoveryAnchorComponent, respawn it intact at the anchor and emit object.recovered; update mw-e10.5 AC-1 to x0.5.
Acceptance criteria
AC-1 unit Given a quest-critical breakable object, when it breaks, then an intact copy reappears at its recovery anchor and object.recovered is emitted once.
Closed
Completed in PR #308 (2026-11-08)
area:simepic:e17milestone:m1
M2 Class fantasies 1
mw-e17.14Handle carried world objects on stagger, climbing, scene exit and save
Description
Why
ADR-0003 keeps crates, bodies and statues out of the inventory: they are carried physically. Carrying already exists as a verb (
mw-e02.17,mw-e10.5), but the edge cases the ADR lists (stagger, ladders, scene exits, saves, lost quest objects) have no owner, and failure should create a new situation rather than a stuck one.What
As a player, I want a carried crate or body to behave predictably when I am hit, try to climb, leave the area or save so that carrying never traps me or loses the quest.
- A hit that breaks poise or staggers the carrier drops the held object at their feet.
- Grabbing a ladder, rope, ivy or ledge while carrying is refused with "Hands full"; picking up a second world object while carrying is refused the same way; taking a small item goes to the pack.
- Exiting a scene while carrying drops the object at the threshold on the near side.
- A save written while carrying stores the object placed at the carrier's feet; on load the player is not carrying.
- Quest-critical world objects declare a
recoveryanchor; destroyed or out of bounds, they return there and emitobject.recovered. - Shoulder-carry (bodies,
bulky) moves at ×0.5 for every class; hand-carry at ×0.85 with no sprint. Multipliers stack with the armor load class.
Scope
- In: Drop-on-stagger, traversal and pickup refusals, scene-exit drop, save placement, recovery anchors and their validator check, shared carry speed values
- Out: Lift, throw and push mechanics (
mw-e02.17,mw-e02.18); hiding bodies (mw-e10.5)
Notes
Related:
mw-e10.5AC-1 states ×0.6 for bodies; ADR-0003 sets ×0.5 for all classes, so that AC is updated when this lands.Acceptance criteria
- AC-1 unit Given the player hand-carrying a crate, when a hit staggers them, then the crate is released at their feet in the same tick and the carry state ends.
- AC-2 unit Given the player carrying a body, when they interact with a ladder, then the grab is refused with reason hands-full and the body stays carried.
- AC-3 integration Given the player carrying a crate across a scene exit, when the new scene loads, then the crate exists in the origin scene at the threshold and the player is not carrying.
- AC-4 integration Given the player carrying a body, when the game is saved and loaded, then the body is a placed world object at the save position and the player is not carrying.
- AC-5 unit Given a quest-critical world object that falls below the scene kill plane, when resolved, then it reappears at its recovery anchor and emits object.recovered once.
- AC-6 content Given a world object flagged questCritical without a recovery anchor, when validated, then validation fails naming the entity and file.
- AC-7 unit Given a Heavy-load knight shoulder-carrying a body, when running, then speed is 2.25 m/s ± 0.01.
Waits on
- mw-e02.17 Pick up, carry, drop and throw liftable objects done
Closed
Completed in PR #304 (2026-11-08)
area:simclass:allepic:e17milestone:m2
E18Loot & TreasureExploration produces interesting, handcrafted discoveries rather than endless statistically interchangeable equipment. Loot is data (tables + placements), deterministic under seeded RNG, persistent in world state, and aware of ownership so theft is a real, systemic act.4/10 done · 40%
M1 Grey-box slice 4
mw-e18.1Implement loot table schema and deterministic seeded roller
Description
Why
Loot is data (contract §1) and must be deterministic for replay tests; tables let designers favour handcrafted guarantees over random stat-sticks.
What
As a designer, I want to author loot tables with guaranteed and weighted entries so that containers and creatures yield intentional discoveries.
Schema:
id,guaranteed: [{item, count}],rolls: {min,max},entries: [{item | table, weight, count:{min,max}, conditions?}],noDuplicates. Roller takes the seeded RNG stream + context (player class, world facts) and returns item stacks. Nested tables supported with depth limit 4. Unique items can only drop if not already in world state as obtained.Scope
- In: Schema + zod types, roller, uniqueness check against world state, conditional entries using simple predicates (class, fact) until the DSL lands
- Out: Containers (e18-containers)
- Out: Creature drops (e18-creature-drops)
- Out: Validator (e18-loot-validator)
Notes
Conditions migrate to the shared DSL (e22-condition-effect-dsl) when available; keep the predicate interface identical.
Acceptance criteria
- AC-1 unit Given a table with 2 guaranteed items and 0 rolls, when rolled with any seed, then exactly those 2 items are returned.
- AC-2 unit Given the same table, seed and context, when rolled 10,000 times in two runs, then both result sequences are identical.
- AC-3 unit Given a weighted table (weights 3:1) rolled 40,000 times with seeded RNG, then observed ratios are within ±2% of 75%/25%.
- AC-4 unit Given a unique item already marked obtained in world state, when a table containing it is rolled, then the entry is skipped and its weight redistributed.
- AC-5 unit Given an entry conditioned on class
thief, when rolled for a knight, then that entry never appears. - AC-6 unit Given nested tables exceeding depth 4 at runtime, when rolled, then rolling stops at depth 4 and an error event is emitted (no infinite loop).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e17.2 Define item definition schema and content validator done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #155 (2026-10-03)
area:simepic:e18milestone:m1mw-e18.2Validate loot tables: references, uniques, cycles, weights
Description
Why
Broken loot data silently produces empty chests or duplicate artifacts; CI must catch it.
What
Content validator run in CI over all loot tables and placements: all item/table references exist, weights > 0, count ranges valid, no cycles in nested tables, each unique item appears in at most one guaranteed placement across the world, every table is referenced by something (warning).
Scope
- In: Validator module + CLI entry + CI hook via content pipeline
- Out: Balance analysis (e18-treasure-audit-tool)
Acceptance criteria
- AC-1 unit Given a table referencing a missing item id, when validated, then validation fails naming the table, entry index and missing id.
- AC-2 unit Given tables A→B→A, when validated, then validation fails reporting the cycle path "A > B > A".
- AC-3 unit Given a unique artifact guaranteed in two placements, when validated, then validation fails listing both placements.
- AC-4 unit Given an unreferenced table, when validated, then a warning (not an error) is reported.
- AC-5 content Given the repository's loot content, when CI runs the validator, then it exits 0.
Waits on
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
Closed
Completed in PR #160 (2026-10-03)
area:contentepic:e18milestone:m1mw-e18.3Add lootable containers that persist their contents
Description
Why
The m1 loop requires loot; the constitution says bookshelves, chests and wells should contain things, and looted state must persist.
What
As a player, I want to open chests, barrels, bookshelves and corpses and take what's inside so that exploring pays off.
Container component:
contentseither fixed list orlootTablerolled once on first open (seed derived from container id + world seed so it is stable across reloads). Take one / take all, put items back. Hooks:lockId(e03),trapId(mw-e10),ownerId(e18-ownership-stolen). Contents persisted via world-state store after first open.Scope
- In: Container component, open/take/put, roll-once semantics, persistence, lock hook, empty-state
- Out: Container UI (e18-container-ui)
- Out: Trap mechanics (mw-e10)
Acceptance criteria
- AC-1 unit Given an unopened container with a loot table, when opened twice across a save/reload, then the contents are identical both times (rolled once).
- AC-2 unit Given a container, when the player takes all, then contents move to inventory, container becomes empty and
container.lootedfact is set. - AC-3 unit Given a locked container without a key, when opened, then it refuses with reason
lockedand does not roll its table. - AC-4 unit Given the player puts an item into a container, when saved and reloaded, then the item is still inside.
- AC-5 integration Given the greybox testbed chest, when the player loots it, saves, reloads and opens it again, then it is empty.
Waits on
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #170 (2026-10-03)
area:simepic:e18milestone:m1mw-e18.4Build container window and loot pickup notifications
Description
Why
Discoveries need a moment of presentation; looting should be one or two inputs.
What
As a player, I want a compact container window with Take / Take All and a short pickup toast so that looting is fast and discoveries feel noticed.
Container window lists contents with icons; Take All bound to a single key/button. Pickup toasts stack in a corner (max 4, merged by def). Unique artifacts get a distinct "discovery" toast with the item's flavour line.
Scope
- In: Container window, take/take-all, pickup toasts, artifact discovery toast, gamepad support
- Out: Item inspection details (e17-inventory-ui)
Notes
UI assets assumed: container window frame, pickup toast frame, artifact discovery banner.
Acceptance criteria
- AC-1 integration Given an open container with 3 items, when Take All is pressed, then all 3 are in inventory and the window closes within the same frame.
- AC-2 integration Given 6 items picked up within 1 second, when toasts display, then at most 4 toasts are visible and duplicates by def are merged with a count.
- AC-3 integration Given a unique artifact picked up, when displayed, then the discovery toast shows its flavour line and remains for ≥ 4 s.
- AC-4 unit Given an empty container, when opened, then the window shows "Empty" and Take All is disabled.
Waits on
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e18.3 Add lootable containers that persist their contents done
Closed
Completed in PR #180 (2026-10-03)
area:uiepic:e18milestone:m1
E19ProgressionCharacter advancement primarily unlocks capabilities (verbs), specialization and new play styles — never Firebolt I → II (+12%). The player picks a class at the start; each class earns new verbs through its own fiction: books (sorcerer), techniques from trainers (knight), arrows and trick-shot techniques (archer), tools and tricks (thief). Supporting stats are minimal and subordinate.6/19 done · 32%
M1 Grey-box slice 5
mw-e19.1Spike: capability-unlock progression model, stats and respec policy
Description
Why
The anti-grind rule demands progression that unlocks verbs, not numbers. We must decide the model before class, loot and shop data are authored against it.
What
Time-box: 1.5 days. Questions: (1) Is there XP/levels at all, or do unlocks come only from in-world sources (books, trainers, schematics, tools, deeds)? (2) Which minimal supporting stats exist (health, stamina, focus/mana) and how do they grow (found items, shrines, deeds)? (3) How does each class earn verbs: sorcerer books, knight trainer techniques, archer arrows + trick-shot techniques, thief tools + tricks? (4) Can classes cross-learn (a knight reading a simple spellbook)? (5) Respec policy: none (additive capabilities), loadout swapping, or paid retraining?
Scope
- In: ADR with recommendation, per-class unlock channel table, stat list with ranges, respec decision, anti-grind validation rules for content
- Out: Implementation (e19-capability-registry onward)
Notes
Leaning: no XP; unlocks are in-world acquisitions; supporting stats grow via rare found items/deeds; capabilities are additive so respec is limited to loadout choices (e.g. prepared spell slots) — revisit if playtests disagree.
Acceptance criteria
- AC-1 manual Given the spike, when complete, then docs/adr/ contains an Accepted progression ADR answering all five questions.
- AC-2 manual Given the ADR, when read, then it contains a table listing each class's unlock channels with at least 3 example verb chains per class following the Firebolt → Flame Jet → Fire Wall pattern.
- AC-3 manual Given the ADR, when read, then it lists the supporting stats (≤ 5) with numeric start values and caps.
- AC-4 manual Given the ADR, when read, then it defines content validation rules that reject pure numeric-upgrade unlocks unless flagged
supporting.
Closed
Completed in PR #130 (2026-10-02)
area:designepic:e19milestone:m1mw-e19.2Create capability registry with source-tracked grants
Description
Why
Dialogue, quests, puzzles, interaction prompts and equipment all ask "can the player do X?"; one registry avoids class checks scattered through code.
What
Capability definitions as data (
idlikeverb.climb.ledge,spell.ember,arrow.rope,tool.lockpick,sense.detect-magic;nameKey,descKey,icon,classaffinity,supportingflag). Runtime registry per actor:grant(capId, source),revoke(capId, source),has(capId),sources(capId), reference-counted by source (class,learned,equipment:<id>,temporary:<effectId>). Emitscapability.gained/capability.lostonly on 0↔1 transitions.Scope
- In: Capability schema + validator, registry runtime, events, query API
- Out: Unlock rules and prerequisites (e19-progression-core)
Acceptance criteria
- AC-1 unit Given a capability granted by two sources, when one source revokes it, then
hasis still true and nocapability.lostevent is emitted. - AC-2 unit Given a capability with zero sources, when granted, then exactly one
capability.gainedevent is emitted. - AC-3 unit Given a grant of an undefined capability id, when called, then it throws in dev/test builds and is a logged no-op in production builds.
- AC-4 content Given the capability data files, when validated, then ids are unique and match the pattern
^(verb|spell|arrow|tool|trick|technique|sense)\.[a-z0-9.-]+$.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e19.1 Spike: capability-unlock progression model, stats and respec policy done
Closed
Completed in PR #139 (2026-10-02)
area:simepic:e19milestone:m1mw-e19.3Implement capability-unlock progression with prerequisite chains
Description
Why
Progression should unlock new verbs (constitution §3). This is the rule engine every class channel plugs into.
What
As a player, I want each thing I learn to give me a new way to act, building on what I already know, so that my character grows in possibilities rather than numbers.
Unlock definitions:
{capability, prerequisites: [capIds], requirements: conditions, source channel}.learn(capId, channel)checks prerequisites and requirements and grants with sourcelearned. Supports verb chains (Firebolt → Flame Jet → Fire Wall) and upgrades that *replace or extend* a verb (Telekinesis → throw creatures). Learned set persists via save.Scope
- In: Unlock definitions schema, learn API, prerequisite resolution, "why can't I learn this" explanations, save section
- Out: Class-specific channels (e19-*-channel items)
- Out: UI (e19-capabilities-screen)
Acceptance criteria
- AC-1 unit Given
spell.flame-jetrequiringspell.ember, when a player without Ember tries to learn Flame Jet, then learning fails with reasonmissing-prerequisite: spell.ember. - AC-2 unit Given all prerequisites met, when learned, then the capability is granted with source
learnedandprogression.learnedis emitted with the channel. - AC-3 unit Given a capability already learned, when learned again, then the call is a no-op returning
already-known(no duplicate events). - AC-4 unit Given an unlock that
replacesa previous capability, when learned, then the old capability'slearnedsource is revoked and the new one granted atomically. - AC-5 unit Given a learned set, when saved and loaded, then the registry state is identical.
- AC-6 content Given all unlock definitions, when validated, then there are no prerequisite cycles and no unlock is a pure numeric upgrade unless flagged
supporting.
Waits on
- mw-e19.2 Create capability registry with source-tracked grants done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #143 (2026-10-02)
area:simepic:e19milestone:m1mw-e19.4Define the four classes as data: kit, capabilities, proficiencies
Description
Why
Classes change possibilities; defining them as data keeps class checks out of code and makes dialogue/quest gating uniform.
What
Class schema + four class files (knight, archer, sorcerer, thief):
startingCapabilities,startingKit(items, gold),proficiencies(weight classes, weapon tags),supportingStatsstart values,dialogueTag(for [Thief]-style options),unlockChannels(which progression channels apply),iconId,portraitId. Grey-box starting kits only; final kits tuned later.Scope
- In: Class schema, 4 class data files with placeholder kits, validator
- Out: Selection UI (e19-class-selection)
- Out: Final starting kits (tuned during m2/m3)
Integration note: Archer class data should grant
sense.tracking(used by the Sleepers' Path in E25 to reveal sleepwalker trails in daylight); if not granted, the path falls back to night-only visibility.Acceptance criteria
- AC-1 content Given the 4 class files, when validated, then each has ≥ 2 starting capabilities, a starting kit whose items all resolve, and a unique dialogueTag.
- AC-2 unit Given a class whose startingCapabilities include an id not in the registry, when validated, then validation fails naming the class and id.
- AC-3 unit Given a class file with no unlock channel, when validated, then validation fails (every class must be able to progress).
Waits on
- mw-e17.2 Define item definition schema and content validator done
- mw-e19.2 Create capability registry with source-tracked grants done
Closed
Completed in PR #149 (2026-10-02)
area:contentepic:e19milestone:m1mw-e19.5Add class selection at new game that applies kit and capabilities
Description
Why
Choosing a class is the game's core promise; the choice must be clear and apply fully at start.
What
As a player, I want to choose Knight, Archer, Sorcerer or Thief at the start and see what each can do so that I pick the fantasy I want.
New-game flow: class cards with short pitch, 3 signature verbs listed, starting kit preview. Confirm applies class: grants starting capabilities (source
class), adds kit to inventory, equips kit, sets stats. Class stored in save and exposed to conditions (player.class).Scope
- In: Selection screen (grey-box styling), apply-class sim function, save field, keyboard/gamepad navigation
- Out: Character customization/appearance (not MVP)
- Out: Final class card art (asset group)
Notes
UI assets assumed: 4 class card illustrations/portraits, 4 class emblem icons.
Acceptance criteria
- AC-1 unit Given the thief class applied to a fresh player, when complete, then the registry, inventory and equipment exactly match the thief data file.
- AC-2 integration Given the selection screen, when the player tries to confirm without a class highlighted, then confirm is disabled.
- AC-3 e2e Given a new game, when the player selects Sorcerer and enters the greybox testbed, then the HUD shows the sorcerer starting kit and
player.classissorcerer. - AC-4 unit Given apply-class is called on a player who already has a class, when executed, then it throws
class-already-set(no stacking of classes).
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e17.4 Add equipment slots with class proficiencies and equip events done
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
Closed
Completed in PR #163 (2026-10-03)
area:gameepic:e19milestone:m1
M3 MVP content 1
mw-e19.13Implement respec/loadout policy decided in the progression ADR
Description
Why
Experimentation is a pillar; if any capability choices are exclusive, players need a fair way to change them.
What
As a player, I want to adjust my loadout of prepared capabilities (per ADR) so that I can experiment without restarting.
Implements whatever the ADR chose: expected default is loadout slots (e.g. prepared spells / equipped techniques) swappable at rest points, with no destructive respec because learned capabilities are additive.
Scope
- In: Loadout slots per ADR, swap rules, rest-point integration, save section
- Out: Anything the ADR rejects
Acceptance criteria
- AC-1 unit Given a loadout of N slots full, when a new capability is slotted at a rest point, then the displaced one returns to the known pool (never lost).
- AC-2 unit Given the player is not at a rest point, when a swap is requested, then it fails with reason
not-at-restif the ADR requires rest. - AC-3 unit Given a slotted capability later revoked (e.g. source item lost), when revoked, then its slot empties and an event is emitted.
Waits on
- mw-e19.12 Build capabilities screen and "new verb learned" feedback open
- mw-e19.3 Implement capability-unlock progression with prerequisite chains done
- mw-e27.13 Rest at inns, campfires and shrines to reset facts and pass time open
Closed
Won't do: superseded by ADR-0004 (owner decision 2026-10-02) — capabilities are additive, no respec and no loadout slots (2026-10-02)
area:simepic:e19milestone:m3
E20Economy & ShopsGold has meaningful uses and merchants have identities, inventories and specialties (Quest for Glory merchants with personality; Elder Scrolls living shops). Players can buy, sell, haggle and steal, and prices respond to disposition, specialty and stolen goods.6/12 done · 50%
M2 Class fantasies 5
mw-e20.1Write economy design: gold income, meaningful sinks, price bands
Description
Why
Gold must have meaningful uses; without a budget the MVP will either drown the player in gold or starve the bookshop.
What
docs/design/economy.md: gold income sources (loot, quests, selling, theft), meaningful sinks (spellbooks, trainer techniques, arrow materials, thief tools, services, bribes, donations/deeds), price bands per item tier, target gold curve across the MVP (e.g. can afford 1 of 3 premium books on first bookshop visit), merchant markup ranges, disposition and stolen-goods modifier ranges, haggle limits.
Scope
- In: Doc with numeric targets consumed by e20-price-model and e20-economy-balance-tool
- Out: Merchant content (world-content group)
Acceptance criteria
- AC-1 manual Given the doc, when reviewed, then it lists every sink category with a price band in gold.
- AC-2 manual Given the doc, when reviewed, then it defines a target obtainable-gold : sink-cost ratio band for the MVP region (e.g. 0.6–0.8) used by the balance tool.
- AC-3 manual Given the doc, when reviewed, then it gives numeric ranges for disposition, specialty, stolen and haggle modifiers.
Waits on
- mw-e19.1 Spike: capability-unlock progression model, stats and respec policy done
Closed
Completed in PR #225 (2026-10-04)
area:designepic:e20milestone:m2mw-e20.2Define merchant schema with identity, specialty and stock rules
Description
Why
Merchants need identities, inventories and specialties (QfG5 personality, Elder Scrolls living shops) authored as data.
What
Zod schema
merchant:id,npcId(dialogue speaker),personalityTags(e.g. greedy, gossipy, suspicious),specialties(item categories/tags bought at better rates),buysCategories,stock: [{item | lootTable, count, restock?}],goldReserve+goldRestock,markup/buyRatebands,isFence,hours?(world time hook),barkSetid. Validator checks references and bands against economy doc.Scope
- In: Schema, validator, 2 grey-box fixture merchants (general goods, fence)
- Out: Bookshop specialisation (e21-bookshop-system extends this)
- Out: Briar Glen merchants (world-content group)
Acceptance criteria
- AC-1 content Given the 2 fixture merchants, when validated, then validation passes.
- AC-2 unit Given a merchant with markup outside the economy doc band, when validated, then validation fails naming merchant and field.
- AC-3 unit Given a merchant whose stock references a missing item or loot table, when validated, then validation fails with the reference.
- AC-4 unit Given a merchant with
isFence: falseandbuysStolen: true, when validated, then validation fails (only fences buy stolen goods).
Waits on
- mw-e17.2 Define item definition schema and content validator done
- mw-e18.1 Implement loot table schema and deterministic seeded roller done
- mw-e20.1 Write economy design: gold income, meaningful sinks, price bands done
Closed
Completed in PR #225 (2026-10-04)
area:contentepic:e20milestone:m2mw-e20.3Implement pure price model with disposition and stolen modifiers
Description
Why
Prices must respond to relationships and context, be deterministic, and be 100% tested (contract §3: economy at 100%).
What
Pure function
price({item, instance, merchant, direction: buy|sell, disposition, haggleModifier, worldFacts})→ integer gold with a breakdown of applied modifiers. Modifiers: base value, merchant markup/buyRate, specialty bonus, disposition band multiplier, stolen (fence discount; non-fence refuses), quest items unsellable, floor of 1 gold, sell price never exceeds buy price for the same merchant.Scope
- In: Price function, breakdown for UI, exhaustive tests incl. property tests
- Out: Transactions (e20-shop-system)
- Out: Haggle flow (e20-haggling)
Acceptance criteria
- AC-1 unit Given an item of value 100, merchant markup 1.2 and neutral disposition, when pricing a buy, then the price is 120 and the breakdown lists base and markup.
- AC-2 unit Given friendly vs cold disposition bands, when pricing the same buy, then the friendly price is lower by the economy doc multipliers.
- AC-3 unit Given any item/merchant/disposition combination (property test, 10,000 cases), when pricing, then sell price ≤ buy price and price ≥ 1.
- AC-4 unit Given a stolen item and a non-fence merchant, when pricing a sell, then the result is
refused: stolen. - AC-5 unit Given a quest item, when pricing a sell, then the result is
refused: quest-item.
Waits on
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
Closed
Completed in PR #225 (2026-10-04)
area:simepic:e20milestone:m2mw-e20.4Implement merchant buy/sell transactions with persistent stock
Description
Why
Gold needs meaningful uses and merchants need living inventories; transactions must never duplicate or lose items or gold.
What
As a player, I want to buy and sell with merchants whose stock and gold are finite and remembered so that shops feel like real places.
Atomic transactions: buy (player gold ≥ price, stock > 0) and sell (merchant gold ≥ price, merchant buys category, not refused). Buyback: last 10 sold items repurchasable at the sell price until restock. Stock, gold and buyback stored in world-state store. Emits
shop.bought/shop.soldevents for quests/telemetry.Scope
- In: Transaction engine, merchant runtime state, buyback, persistence, events
- Out: Restocking (e20-restock-rotation)
- Out: UI (e20-shop-ui)
- Out: Haggling (e20-haggling)
Acceptance criteria
- AC-1 unit Given player gold 100 and an item priced 80 with stock 1, when bought, then player gold is 20, the item is in inventory, merchant gold +80 and stock 0.
- AC-2 unit Given stock 0, when a buy is attempted, then it fails with
out-of-stockand no state changes. - AC-3 unit Given merchant gold 30 and a sell priced 50, when sold, then it fails with
merchant-cannot-afford(or offers 30 if the economy doc enables partial) and state is unchanged on failure. - AC-4 unit Given an item just sold, when bought back before restock, then the price equals the sell price.
- AC-5 unit Given 1,000 random buy/sell sequences (seeded), when executed, then total gold across player+merchant and total item count are conserved.
- AC-6 integration Given a transaction, when saved and reloaded, then merchant stock, gold and buyback are unchanged.
Waits on
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e20.3 Implement pure price model with disposition and stolen modifiers done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #226 (2026-10-04)
area:simepic:e20milestone:m2mw-e20.10Build shop UI with merchant identity, buy/sell and price breakdown
Description
Why
Merchants must feel like characters, and players must understand why prices are what they are.
What
As a player, I want a shop screen that shows the merchant's face and personality, clear buy/sell lists and why an item costs what it does so that trading is quick and characterful.
Layout: merchant portrait slot + greeting bark; tabs Buy / Sell / Buyback / Services; item rows with price, specialty badge, stolen marker (sell tab), comparison vs equipped; price tooltip shows modifier breakdown (disposition, specialty, haggle); Haggle button (when available); quantity selector for stacks; confirm for expensive (> 25% of gold) purchases.
Scope
- In: Shop screen, tabs, breakdown tooltip, quantity selector, confirmation, keyboard/gamepad, text-size support
- Out: Haggle flow UI (uses dialogue UI)
Notes
UI assets assumed: shop panel frame, merchant portrait frame, specialty badge icon, gold icon, stolen marker icon, buyback icon.
Acceptance criteria
- AC-1 integration Given a merchant with specialty "books", when the Sell tab lists a book, then a specialty badge shows and the tooltip lists the specialty modifier.
- AC-2 integration Given a purchase costing > 25% of player gold, when confirmed, then a confirmation dialog appears; cheaper purchases complete in one input.
- AC-3 integration Given a stolen item on the Sell tab at a non-fence, when displayed, then the row is disabled with the refusal reason.
- AC-4 e2e Given the greybox merchant, when the player buys an item and sells it back via Buyback, then gold and inventory match the pre-buy state.
- AC-5 unit Given text size 150%, when the shop renders, then price columns remain aligned and no row text is clipped.
Waits on
- mw-e17.10 Build inventory screen with category tabs, inspect and actions done
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #229 (2026-10-04)
area:uiepic:e20milestone:m2
M3 MVP content 1
mw-e20.5Restock merchant inventories on world-time ticks deterministically
Description
Why
Living shops change over time; rotating stock gives reasons to return (and underpins bookshop rotation in E21).
What
As a player, I want merchants to restock and occasionally carry new things so that revisiting shops is worthwhile.
On world day tick: restock entries flagged
restockup to their counts, rollrotatingloot-table entries with seed (merchantId, day), refill gold toward reserve, clear buyback. Unique items never restock. Condition-gated stock (e.g. after a quest outcome) appears when conditions become true.Scope
- In: Restock scheduler, rotation rolling, conditional stock, gold refill
- Out: Bookshop regional/rare/secret rules (e21-bookshop-system)
Acceptance criteria
- AC-1 unit Given a restock entry of count 3 with stock 0, when a day tick passes, then stock is 3.
- AC-2 unit Given the same merchant and day, when rotation rolls twice (e.g. across reload), then the rotating stock is identical.
- AC-3 unit Given a sold-out unique item, when any number of day ticks pass, then it never reappears.
- AC-4 unit Given stock gated on fact
bridge.repaired, when the fact becomes true, then the entry appears on the next tick.
Waits on
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
Closed
Completed in PR #241 using the minimal day clock (time.day/time.minute facts) from mw-ju8.6 and the existing condition schema; mw-e27.12 clock and mw-e22.2 DSL can call/replace the same hooks (Shops.restock(world, day)). (2026-10-05)
area:simepic:e20milestone:m3
E22NPC & Dialogue SystemCharacters support conversations with conditions and effects, disposition, meaningful choices, class-specific options, persuasion (Divinity: Original Sin 2 style), rumors that lead to places, and lightweight state tracking. Text only for MVP (no voice), with subtitle/text-size accessibility hooks. Dialogue is authored as validated data, never bespoke code.13/19 done · 68%
M2 Class fantasies 13
mw-e22.2Create shared typed condition/effect DSL for dialogue, quests, shops
Description
Why
Dialogue, quests, shops, loot and deeds all need "if X then do Y" without bespoke scripting; one validated DSL prevents five incompatible mini-languages and eval().
What
JSON-serializable, zod-validated expression language in src/sim/logic. Conditions:
all/any/not,fact,factCmp,hasItem,hasCapability,playerClass,disposition,questState,objectiveState,gold,timeOfDay. Effects:setFact,giveItem,takeItem,gold,disposition,startQuest,completeObjective,resolveQuest,failQuest,learnCapability,emit(sim event),revealRumor. Evaluator is pure over a context interface; effects apply as an atomic batch (all or nothing). Extensible registry so other epics add condition/effect kinds with their own validators.Scope
- In: Schema, evaluator, atomic effect applier, registry for extension, reference validation hooks, human-readable describe() for tooling/UI
- Out: Dialogue graph (e22-dialogue-runtime)
- Out: Quest graph (e23-quest-runtime)
Notes
No string expressions, no eval. Describe() output powers "requires: …" hints for locked options.
Acceptance criteria
- AC-1 unit Given
all[hasItem(key-a), not(fact(door.open))]and a context with key-a and door closed, when evaluated, then result is true. - AC-2 unit Given an effect batch [takeItem(x), giveItem(y)] where takeItem fails, when applied, then neither effect is applied and the failure reason is returned.
- AC-3 unit Given an unknown condition kind in data, when validated, then validation fails naming the kind and path.
- AC-4 unit Given a registered extension condition, when used in content, then it validates and evaluates like built-ins.
- AC-5 unit Given any condition, when describe() is called, then it returns a non-empty localized-key string (tested for every built-in kind).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e27.12 Run a deterministic in-game clock with time-of-day and schedules done
- mw-e27.5 Query world facts with a shared condition API for all systems done
Closed
Completed in PR #404 (2026-11-12)
area:simepic:e22milestone:m2mw-e22.3Define dialogue authoring format and schema
Description
Why
Designers and writers must author conversations as data quickly, with humour and branching, without touching code.
What
Dialogue file format (JSON or YAML compiled to JSON, decided here):
id,speakers,entrynodes (multiple, conditioned — e.g. first meeting vs return), node kindsline(speaker, textKey/text, next),choice(options with text, conditions,showWhenLocked, class tag, effects, next),branch(conditional routing),check(persuasion; e22-persuasion-checks),effect,jump(to other dialogue),end. Text inline for MVP with stable ids for future localization. Supportsonceoptions andvisitedCountconditions.Scope
- In: Format + zod schema, YAML→JSON compile step if chosen, 2 fixture dialogues, format doc section
- Out: Runtime (e22-dialogue-runtime)
- Out: Validator graph checks (e22-dialogue-validator)
Acceptance criteria
- AC-1 content Given the 2 fixture dialogues, when compiled and validated, then they pass.
- AC-2 unit Given a node with an unknown
kind, when validated, then validation fails with file, node id and line. - AC-3 unit Given duplicate node ids within one dialogue, when validated, then validation fails listing both.
- AC-4 unit Given a choice option lacking both
nextand anendflag, when validated, then validation fails.
Waits on
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
Closed
Completed in PR #407 (2026-11-12)
area:contentepic:e22milestone:m2mw-e22.4Track NPC disposition with bands, event modifiers and factions
Description
Why
How characters feel about the player should change prices, dialogue options and help offered — the core of a reactive world.
What
As a player, I want NPCs to remember how I treated them so that kindness, theft and bravado have social consequences.
Per-NPC disposition -100..100 with bands (hostile < -50, cold, neutral, friendly, devoted > 75). Sources: dialogue effects, gifts, witnessed crimes, quest outcomes, class affinity baseline. Optional light faction propagation (fraction of delta to faction members). Stored in world-state store. Emits
disposition.bandChanged.Scope
- In: Disposition store, band mapping, event-driven modifiers (crime.theft, quest outcomes), faction propagation, save
- Out: Creature-level befriending (mw-e14)
- Out: AI hostility decisions (mw-e11 consumes bands)
Acceptance criteria
- AC-1 unit Given disposition 45 (neutral) and a +10 effect, when applied, then disposition is 55, band becomes friendly and one
disposition.bandChangedevent fires. - AC-2 unit Given a +200 effect, when applied, then disposition clamps at 100.
- AC-3 unit Given faction propagation 0.5 and a -20 delta to one member, when applied, then other faction members receive -10.
- AC-4 unit Given a
crime.theftevent naming a witness who is the owner, when processed, then the owner's disposition drops by the configured theft amount scaled by item value tier. - AC-5 unit Given an NPC never met, when queried, then it returns the class-affinity baseline from data (default 0).
Waits on
- mw-e12.9 Per-creature disposition toward the player with event modifiers done
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #410 (2026-11-12)
area:simepic:e22milestone:m2mw-e22.5Implement dialogue graph runtime with conditions and effects
Description
Why
Characters support conversations, choices and lightweight state tracking without bespoke code (E22 outcome).
What
As a designer, I want authored dialogues to run deterministically with conditions, effects and memory so that NPCs react to what the player has done.
Runtime in src/sim/dialogue:
start(dialogueId, npcId)picks the first entry whose conditions pass; steps through nodes;getChoices()returns available options (+ locked ones with describe() reasons whenshowWhenLocked);choose(i)applies effects atomically and advances; tracks visited nodes/options per NPC in world state (onceoptions disappear);endor abort (combat interrupt) leaves consistent state. Emitsdialogue.started/nodeVisited/endedevents for quests.Scope
- In: Runtime, visited tracking, locked-option reasons, interrupt/abort semantics, events, save integration
- Out: Persuasion checks (e22-persuasion-checks)
- Out: UI (e22-dialogue-ui)
- Out: World hookup (e22-npc-conversation)
Acceptance criteria
- AC-1 unit Given a dialogue with entries [firstMeeting (not visited), returning], when started twice with the first finished, then the second start uses the
returningentry. - AC-2 unit Given a choice with a failing condition and
showWhenLocked: true, when choices are listed, then it appears locked with its describe() reason and cannot be chosen. - AC-3 unit Given a choice whose effect batch fails (e.g. takeItem missing), when chosen, then no effects apply, the runtime routes to the option's
onFailnode if present, else stays on the node with an error event. - AC-4 unit Given a
onceoption already chosen, when the dialogue is restarted, then the option is not offered. - AC-5 unit Given a conversation aborted mid-graph, when aborted, then effects already applied remain, no pending effects apply and
dialogue.ended{reason: aborted}is emitted. - AC-6 integration Given a conversation that sets a fact and gives an item, when saved and reloaded, then fact, item and visited state are preserved.
Waits on
- mw-e22.15 Roll back disposition effects on a failed logic batch (dispositionLogicHost checkpoint) done
- mw-e22.3 Define dialogue authoring format and schema done
- mw-e22.4 Track NPC disposition with bands, event modifiers and factions done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #415 (2026-11-12)
area:simepic:e22milestone:m2mw-e22.6Add class- and capability-gated dialogue options with tags
Description
Why
Classes change possibilities — including in conversation (QfG5 class-flavoured solutions; DOS2 tagged options).
What
As a thief, I want special dialogue options like "[Thief] I couldn't help noticing your ledger doesn't add up" so that my class opens social routes too.
Option
tagderived from its gating condition: class tag (from class datadialogueTag), capability tag (e.g. [Detect Magic]), item tag ([Show the signet ring]). Tags rendered with icons in UI. Designers can mark options hidden-when-locked so other classes don't see them (default hidden for class tags).Scope
- In: Tag derivation, default visibility rules, validator warning when a dialogue has class options for < 2 classes (encourages breadth, warning only)
- Out: Persuasion math (e22-persuasion-checks)
Acceptance criteria
- AC-1 unit Given an option conditioned on
playerClass: thief, when listed for a thief, then it is available with tagthief; for a knight it is hidden. - AC-2 unit Given an option conditioned on
hasCapability(sense.detect-magic)with showWhenLocked, when listed without the capability, then it appears locked with tagsense.detect-magic. - AC-3 unit Given an option conditioned on
hasItem(signet-ring), when listed with the ring, then its tag references the item's icon id. - AC-4 unit Given a dialogue with class-gated options for only one class, when validated, then a warning (not error) is reported.
Waits on
- mw-e19.2 Create capability registry with source-tracked grants done
- mw-e19.4 Define the four classes as data: kit, capabilities, proficiencies done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
Closed
Completed in PR #417 (2026-11-12)
area:simclass:allepic:e22milestone:m2mw-e22.7Add deterministic persuasion checks with fail branches
Description
Why
DOS2-style persuasion makes talking a real alternative to fighting; failures should create new situations, not reloads.
What
As a player, I want to persuade, intimidate or deceive characters, with my class, reputation and what I carry affecting the outcome, so that words can solve problems.
checknode: approach (persuade | intimidate | deceive | charm), difficulty from data, score = disposition-band modifier + class/capability modifiers + item/fact modifiers + NPC personality modifiers. No hidden dice (deterministic, per anti-save-scum and replay determinism); UI can show odds as difficulty labels. Success/failure route to different nodes; failure applies optional effects (disposition drop, alert).Scope
- In: Check node evaluation, modifier sources from data, difficulty labels, fail routing
- Out: Haggling specifics (e20-haggling reuses this)
Notes
Decision: deterministic thresholds (not RNG) so outcomes are learnable and saves do not encourage reroll. Revisit in playtest.
Acceptance criteria
- AC-1 unit Given difficulty 10 and computed score 12, when the check resolves, then the success branch is taken and the breakdown lists each modifier.
- AC-2 unit Given the same state evaluated twice, when resolved, then the outcome is identical.
- AC-3 unit Given a failed intimidate with
onFail: [disposition(-15), emit(alert)], when resolved, then both effects apply and the fail node is entered. - AC-4 unit Given a check node without a fail branch, when validated, then validation fails (failures must continue the story).
- AC-5 unit Given difficulty labels (easy/fair/hard/impossible), when computed for UI, then they map from score-minus-difficulty per data thresholds.
Waits on
- mw-e22.4 Track NPC disposition with bands, event modifiers and factions done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
Closed
Completed in PR #417 (2026-11-12)
area:simepic:e22milestone:m2mw-e22.8Validate dialogues: reachability, exits, references, text limits
Description
Why
Unreachable nodes and dead-ends are invisible bugs; CI must guarantee every conversation can be played through.
What
Static graph validator over all dialogue content: every node reachable from some entry; every non-end node has at least one exit; every choice node has at least one option available under *some* satisfiable context (not all options contradictory); no infinite loop without an exit option; all item/quest/fact/speaker/capability/dialogue references resolve; text length ≤ configured max per line (UI fit); rumor ids exist. Reports with file + node paths.
Scope
- In: Validator module, CLI, CI hook, clear error formatting
- Out: Runtime play-testing tool (e22-dialogue-preview-tools)
Acceptance criteria
- AC-1 unit Given a dialogue with a node not reachable from any entry, when validated, then validation fails naming the node.
- AC-2 unit Given a line node with no
nextand not marked end, when validated, then validation fails. - AC-3 unit Given a choice node whose every option requires
playerClass: knightANDplayerClass: thief, when validated, then it fails as unsatisfiable. - AC-4 unit Given a cycle A→B→A with no option leaving the cycle, when validated, then it fails with the cycle path.
- AC-5 unit Given a line exceeding the max length, when validated, then a warning is produced with the node id and length.
- AC-6 content Given all repository dialogue, when CI runs the validator, then it exits 0.
Waits on
- mw-e22.3 Define dialogue authoring format and schema done
- mw-e22.6 Add class- and capability-gated dialogue options with tags done
- mw-e22.7 Add deterministic persuasion checks with fail branches done
Closed
Completed in PR #420 (2026-11-12)
area:contentepic:e22milestone:m2mw-e22.9Start conversations with NPCs in-world and handle interruptions
Description
Why
Conversations must feel embedded in the living world: NPCs stop what they are doing, and danger interrupts talk.
What
As a player, I want to walk up to an NPC and talk so that conversations flow naturally from exploration.
Interact verb on an NPC with a
dialogueIdstarts the runtime; NPC pauses its routine and faces the player; camera shifts to a conversation framing (no cutscene); walking away beyond 6 m or entering combat/alert aborts gracefully; hostile NPCs refuse unless a dialogue entry for hostile state exists (e.g. parley).Scope
- In: Interaction binding, NPC routine pause/resume hook, conversation camera framing, abort conditions
- Out: Social creatures (mw-e14 uses this hookup)
Acceptance criteria
- AC-1 integration Given an NPC with a dialogue, when the player interacts, then the runtime starts and the NPC's routine state is
conversing. - AC-2 integration Given a conversation in progress, when a nearby creature enters Alerted/Combat state toward the player, then the conversation aborts within 1 sim tick and the NPC resumes AI control.
- AC-3 integration Given the player moves more than 6 m away, when detected, then the conversation ends with reason
walked-away. - AC-4 unit Given a hostile NPC with no hostile entry, when interaction is attempted, then no dialogue starts and the prompt says the NPC won't talk.
- AC-5 e2e Given the greybox NPC, when the player talks, picks two options and ends, then the NPC returns to its patrol route.
Waits on
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e02.5 Focus and interact with world objects via contextual prompts done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
Closed
Completed in PR #421 (2026-11-12)
area:gameepic:e22milestone:m2mw-e22.10Build accessible dialogue UI with choices, tags and history
Description
Why
Text-only dialogue for MVP means the UI carries all the character; it must be readable and accessible.
What
As a player, I want clear dialogue text with speaker names, selectable choices and a history log so that I can follow conversations comfortably at my own pace.
Dialogue box: speaker name + portrait slot, text reveal (skippable; speed from settings; instant option), choice list navigable by mouse, number keys and gamepad; class/capability/item tags rendered as icon + label; locked options greyed with reason; persuasion difficulty labels; conversation history (scrollback). Settings hooks: text size, dialogue background opacity, reveal speed, high-contrast.
Scope
- In: Dialogue screen, input handling, tags, locked reasons, history, settings bindings
- Out: Ambient barks/subtitles (e22-barks-subtitles)
Notes
UI assets assumed: dialogue box frame, portrait frame, class tag icons ([Knight] [Archer] [Sorcerer] [Thief]), persuasion approach icons (persuade/intimidate/deceive/charm), locked-option icon, generic silhouette portrait placeholder.
Acceptance criteria
- AC-1 integration Given 4 choices, when the player presses the "3" key, then the third choice is selected and confirmed.
- AC-2 integration Given reveal speed "instant" in settings, when a line displays, then the full text appears on the first frame.
- AC-3 integration Given text size 200%, when a 300-character line and 4 choices display, then all content is visible via wrapping/scroll with no clipping.
- AC-4 integration Given a locked option, when focused, then its describe() reason is shown and confirm does nothing.
- AC-5 integration Given a finished conversation, when the history panel is opened during a later line, then all previous lines and chosen options are listed in order.
- AC-6 unit Given dialogue background opacity 100%, when rendered, then the computed text/background contrast ratio is ≥ 7:1.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e22.6 Add class- and capability-gated dialogue options with tags done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #425 (2026-11-12)
area:uiepic:e22milestone:m2mw-e22.11Add rumor system that points players to places and secrets
Description
Why
NPC rumors can lead somewhere (constitution §2); rumors turn conversation into exploration.
What
As a player, I want NPCs to share rumors that I can follow to real places and secrets so that talking to people leads to discoveries.
Rumor data:
{id, textKey, sources (npc ids / tags like "tavern"), conditions, reveals: {fact, journalEntry, optional mapHint}, resolvesWhen: conditions}. NPCs draw from rumor pools in dialogue (revealRumoreffect or a generic "Heard any rumors?" option). Journal "Rumors" section lists heard rumors; a rumor auto-resolves (marked "Investigated") when its target fact is set.Scope
- In: Rumor schema + validator, rumor pool selection (deterministic, unheard first), journal integration hook, resolution tracking
- Out: Journal UI rendering (e23-journal-ui)
- Out: Specific rumors (world-content group)
Acceptance criteria
- AC-1 unit Given an NPC with a pool of 3 rumors, 1 already heard, when asked, then an unheard rumor is chosen deterministically (stable order by priority then id).
- AC-2 unit Given all pool rumors heard, when asked, then the NPC uses its
noRumorsline and no rumor effect fires. - AC-3 unit Given a heard rumor with
resolvesWhen: fact(well.tunnel.found), when that fact is set, then the rumor is marked investigated and arumor.resolvedevent fires. - AC-4 content Given rumor data, when validated, then every rumor has ≥ 1 source and a resolvesWhen that references an existing fact producer (warning if unproduced).
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #477 (2026-11-14)
area:simepic:e22milestone:m2mw-e22.19Conversation input: release pointer lock and withhold gameplay input while talking
Description
From PR #425 (mw-e22.10): while a conversation is open the pointer lock is not released (mouse clicks on options only work if the cursor is free), and gamepad A still reaches gameplay actions because gameplay frames are not withheld, so confirming a choice can also swing or interact. AC: opening a conversation releases pointer lock and restores it on end; no gameplay action fires from any device while the dialogue box has focus (integration test pressing pad A and mouse button during a choice).
Closed
Completed in PR #426 (2026-11-12)
area:simepic:e22milestone:m2mw-e22.15Roll back disposition effects on a failed logic batch (dispositionLogicHost checkpoint)
Description
dispositionLogicHost (src/sim/disposition/runtime.ts) implements disposition/adjustDisposition but not the DSL's checkpoint(): applyLogicEffects needs all-or-nothing. Rolling back needs Dispositions to snapshot/restore records (incl. component add/remove, faction-spread shares) and to hold or retract the DispositionChanged/bandChanged events already emitted. Found in mw-e22.4; do before mw-e22.5 relies on dialogue effect batches mixing disposition with other effects.
Closed
Completed in PR #412 (2026-11-12)
area:simepic:e22milestone:m2mw-e22.16Hold inventory/capability events until a logic batch commits
Description
From PR #416 (mw-e23.11): the game LogicHost restores inventory and capability state on rollback, but itemAdded/itemRemoved/capabilityGained/capabilityLost events emitted mid-batch stay emitted, so listeners (quest notifyItem, UI, journal) can react to changes that never happened. Buffer them until the outermost commit like Dispositions and the fact overlay do. AC: a rolled-back [giveItem, takeItem(missing)] batch emits no item event; a committed one emits each once in order.
Closed
Completed in PR #419 (2026-11-12)
area:simepic:e22milestone:m2
E23Quest FrameworkQuests support branching objectives and multiple resolutions (including non-combat) authored as data without bespoke scripting. Quest state lives in the world-state store and persists; failure transitions continue the story instead of dead-ending it. Players track progress in an in-character journal.6/13 done · 46%
M2 Class fantasies 6
mw-e23.1Define quest authoring format with objectives and resolutions
Description
Why
Quests must support branching objectives and multiple resolutions without bespoke scripting (E23 outcome).
What
Quest schema:
id,titleKey,giverNpcId?,startWhen(conditions) or started by effect,objectives: [{id, descKey, kind, params, optional?, hidden?, activeWhen}],resolutions: [{id, approach: combat|stealth|diplomacy|magic|trickery|other, when: conditions, effects, journalKey}],failures: [{id, when, effects, continuation: {startQuest | setStage | resolution}}],stages?for sequencing. All logic via e22-condition-effect-dsl.Scope
- In: Schema, 2 fixture quests, format doc section
- Out: Runtime (e23-quest-runtime)
- Out: Validator (e23-quest-validator)
Acceptance criteria
- AC-1 content Given 2 fixture quests, when validated, then they pass.
- AC-2 unit Given a quest with zero resolutions, when validated, then validation fails.
- AC-3 unit Given a failure without a
continuation, when validated, then validation fails (failure must continue the story). - AC-4 unit Given an objective with an unknown
kind, when validated, then validation fails naming it.
Waits on
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
Closed
Completed in PR #409 (2026-11-12)
area:contentepic:e23milestone:m2mw-e23.2Implement quest state machine with branching and multi-resolution
Description
Why
Quests with several valid resolutions (fight, sneak, talk, trick) are how the game expresses "problems support multiple solutions".
What
As a designer, I want quests to track objectives and resolve in whichever valid way the player achieves first so that players can solve problems their own way.
States:
unknown → active → resolved(<resolutionId>) | failed(<failureId>). Runtime subscribes to world-state/event changes and re-evaluates only affected quests (dependency index on facts/items). Objectives activate/complete per conditions; resolution chosen by first satisfiedwhen(priority order for ties). Effects applied atomically; outcome facts written (quest.<id>.resolution = <id>). State lives in world-state store (persistent). Emitsquest.*events for journal/telemetry.Scope
- In: State machine, incremental evaluation, objective states, resolution/failure selection, effects, events, persistence
- Out: Objective tracker kinds (e23-objective-trackers)
- Out: Journal (e23-journal-ui)
Acceptance criteria
- AC-1 unit Given an active quest with resolutions [slay, persuade], when the persuade conditions become true first, then the quest resolves as
persuade, its effects apply andslaycan no longer resolve it. - AC-2 unit Given two resolutions satisfied in the same tick, when evaluated, then the one earlier in authored priority wins deterministically.
- AC-3 unit Given an optional objective incomplete, when a resolution condition passes, then the quest still resolves and the objective is marked
skipped. - AC-4 unit Given a resolved quest, when a failure condition later becomes true, then nothing changes (terminal state).
- AC-5 integration Given a quest mid-objective, when saved and loaded, then quest state and objective states are identical and evaluation continues.
- AC-6 perf Given 50 active quests and 500 fact changes per second in a benchmark, when evaluated, then quest evaluation cost is ≤ 0.5 ms per sim tick on the reference machine.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e23.1 Define quest authoring format with objectives and resolutions done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #414 (2026-11-12)
area:simepic:e23milestone:m2mw-e23.3Bind quest objective kinds to sim events
Description
Why
Objectives must be satisfiable by any systemic means — a guard removed by sword, sleep spell or trapdoor all count.
What
As a designer, I want objectives that complete however the player achieves them so that non-combat solutions count automatically.
Objective kinds:
haveItem,deliverItem(to npc),fact,entityState(dead | befriended | fled | subdued | any-removed — outcome-agnostic so non-combat counts),enterArea(trigger volume),talkTo/dialogueNode,learnCapability,event(custom emitted event). Each kind registers its event subscriptions and condition translation in the DSL registry.Scope
- In: Objective kind implementations, event subscriptions, outcome-agnostic entity removal
- Out: New world events (owned by emitting epics)
Notes
Encourage designers to use
entityState: removedrather thandeadso stealth/diplomacy resolve combat-flavoured objectives.Acceptance criteria
- AC-1 unit Given objective
entityState(goblin-chief, removed), when the chief is killed, befriended into leaving, or flees permanently, then the objective completes in each case. - AC-2 unit Given objective
haveItem(silver-key), when the key is acquired then dropped, then the objective completes then reverts to incomplete (unlesslatch: true). - AC-3 integration Given objective
enterArea(mine-entrance)bound to a trigger volume, when the player enters it, then the objective completes. - AC-4 unit Given objective
deliverItem(ring, mirela)and the player lacks the ring, when the delivery dialogue option is evaluated, then it is unavailable.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
Closed
Completed in PR #443 (2026-11-13)
area:simepic:e23milestone:m2mw-e23.6Build in-character journal for quests, rumors and notes
Description
Why
Players need to remember leads and objectives without the game playing itself; an in-character journal keeps the tone.
What
As a player, I want a journal written in my character's voice listing active, resolved and failed quests, current objectives, heard rumors and notable discoveries so that I can pick up where I left off.
Tabs: Active, Completed/Failed, Rumors, Notes (book excerpts/clues). Entries appended as quest events occur (journalKey per objective/resolution). "New entry" HUD toast. Objectives listed as prose with checkmarks; optional objectives marked. Keyboard/gamepad; text size; pinned quest shown in a compact HUD tracker (toggleable).
Scope
- In: Journal screen, entry log model (sim) + UI, HUD toast, pinned tracker, settings hooks
- Out: Map/wayfinding markers (e23-wayfinding-hints)
Notes
UI assets assumed: journal book/parchment frame, tab icons (quest, rumor, note), quest state icons (active, resolved, failed), new-entry toast frame.
Acceptance criteria
- AC-1 integration Given a quest started, an objective completed and a rumor heard, when the journal opens, then Active shows the quest with 1 checked objective and Rumors lists the rumor.
- AC-2 integration Given a failed quest with a continuation, when viewed, then it appears under Completed/Failed with its failure entry and the successor appears under Active.
- AC-3 integration Given a new journal entry, when added, then a toast appears for ≥ 3 s and the journal tab shows an unread indicator until viewed.
- AC-4 unit Given text size 150%, when a 10-objective quest is displayed, then content scrolls without clipping.
- AC-5 unit Given the pinned tracker is disabled in settings, when a quest is pinned, then no HUD tracker renders.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e22.11 Add rumor system that points players to places and secrets done
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e31.1 Create typed settings store and options menu skeleton done
area:uiepic:e23milestone:m2mw-e23.11Wire the quest runtime into the game: installQuests, save/load refresh, dialogue host
Description
PR #414 (mw-e23.2) adds QuestRuntime/installQuests in src/sim/quest but the game does not construct it. Wire installQuests(world,{quests,host}) into game startup (before save load so quest fact keys are declared; production 'ignore' fact policy drops undeclared keys), call runtime.refresh()+settle() after load, and expose runtime.host as the logic host for dialogue/shop effects. AC: integration test starts a fixture quest via effect, saves mid-objective, reloads in a fresh game and resolves it; slice goldens unchanged.
Closed
Completed in PR #416 (2026-11-12)
area:simepic:e23milestone:m2mw-e23.12Event-bound quest objectives should count events from before their stage
Description
From PR #443 (mw-e23.3): event-bound objective kinds (entityState via Died/CreatureOutcome, enterArea, talkTo, dialogueNode, event, learnCapability) only complete if the event fires while the objective is active. A player who kills the goblin chief or enters an area before that stage begins never gets credit, a soft-lock risk for any-order beats (bible 4.1 beat 4). Fact and haveItem objectives already catch up. Fix: have bindings record durable facts (e.g. entity:<id>.state, area visits) and let objectives read them on activation, or latch events in the runtime.
Closed
Completed in PR #446 (2026-11-13)
area:simepic:e23milestone:m2
E24Briar GlenBriar Glen is a dense, lived-in settlement: every named building from the story bible exists and is enterable, merchants and the Lamplit Stacks have authored stock, named NPCs keep day/night schedules, rumours point at real places, rooftops and secrets reward thieves and the curious, and the first-night sleepwalk plays out through its streets.4/22 done · 18%
M2 Class fantasies 3
mw-e24.1Design the world map: areas, loops, shortcuts and ability gates
Description
Why
"Small world with surprising density": paths loop together and reveal shortcuts as players acquire abilities. Every level item needs this map first.
What
docs/design/world-map.md plus content/world/connectivity.json: nodes (town districts, Briarwood zones, Mooring's Watch, mine gate, Deepworks zones, priory rooms, Knot areas) and edges {from, to, kind: path|door|shortcut|one-way|secret, unlock: condition, classesHint}. Loops from bible §3.1: forest ↔ watchtower ↔ priory; watchtower supply tunnel ↔ upper mine; priory crypt/Sounding Shaft ↔ Knot; Glen Well ↔ cistern ↔ priory; jail drain ↔ cistern; river loops — Weeping Adit ↔ lower Deepworks, Stone Stair (cutting hall → quay, one-way), Millweir sluice walk ↔ Briarwood far bank; the towpath (always open) and the barge (mornings) link Briar Glen Quay ↔ Kestrel Lock ↔ Wendmouth. Includes a validator: every node reachable from the town spawn with base class capabilities; every shortcut's unlock references a real fact.
Scope
- In: Map doc with diagram, connectivity data, reachability validator
- Out: Scene transitions implementation (e01-area-transitions)
Notes
Mine decision: Deepworks interior is E26; E25 owns the gate exterior and the tower tunnel up to the collapse. Time-gated edges read the e27-world-clock phase.
Acceptance criteria
- AC-1 content Given connectivity.json, when validated, then every node is reachable from the town spawn using only edges with no unlock condition or unlocks available to all four base classes.
- AC-2 content Given every shortcut edge, when validated, then its unlock condition references a fact produced by some placed content (warning if unproduced).
- AC-3 content Given the doc, when reviewed, then it lists every land and river loop from bible §3.1, the barge/towpath link to Wendmouth, and at least one ability-gated shortcut per class.
- AC-4 content Given an edge referencing an unknown node, when validated, then validation fails naming it (edge).
- AC-5 content Given connectivity.json, when the main-quest critical path is computed, then it never requires a Wendmouth or barge edge (Wendmouth is optional).
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #405 (2026-11-12)
area:designepic:e24milestone:m2mw-e24.2Block out Briar Glen in grey-box with every named building shell
Description
Why
Density before breadth: the town's layout decides sightlines, rooftops and schedules for everything after.
What
As a player, I want to walk a compact valley town with recognisable landmarks so that I learn it within one visit.
Grey-box town (~180×140 m): mountain road entry, Market Green with notice board, the Sleeping Ox, Lamplit Stacks leaning on Brand's Forge, Fenn's Fletchery & Simples, Pell's Bakery, Watch House, Vane House on the rise, schoolhouse, Glen Well, lychgate cemetery; exits to Briarwood, mine road and priory road. Enterable shells with door transitions.
Scope
- In: Town scene, building shells with doors, navmesh, exits wired to connectivity nodes, landmark sightlines
- Out: Interiors (per-building items)
- Out: Rooftop routes (e24-rooftops-thief-routes)
Notes
Vane House visible from everywhere (it sits above the town — the power geography). The Stacks is three storeys and visibly leans.
Acceptance criteria
- AC-1 content Given the town scene, when validated, then every bible §3.5 town location has a named node and an entry transition or interactable.
- AC-2 integration Given the navmesh, when queried, then every building door is reachable from the road entry.
- AC-3 e2e Given the town tour replay, when run, then it visits each landmark and exits to Briarwood, mine road and priory road.
- AC-4 perf Given the grey-box town from Market Green, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e24.1 Design the world map: areas, loops, shortcuts and ability gates done
Closed
Completed in PR #422 (2026-11-12)
area:contentepic:e24milestone:m2mw-e24.21Lead the slice vestibule out onto the mountain road so a new game flows into the valley and town
Description
Why
A new game starts in the slice (game.startScene = slice) and ends in the vestibule with slice.complete = true, but nothing leads on: valley-01 -> valley-02 -> valley-03 -> briar-glen-lane (and its four interiors) is only reachable via ?scene= URLs. The owner wants the levels connected into one journey.
What
Add a transition region past the slice vestibule (beyond where the slice e2e/playthrough stops) to valley-01, plus a matching arrival spawn in valley-01 (e.g. arrive-from-slice) and a way back (valley-01 start -> slice vestibule) so the link is two-way. Reuse the existing scene 'transitions' format (min/max/scene/spawn) and the area-transition runtime from mw-e01.11 including its autosave. Keep slice golden hashes and the slice playthrough unchanged, or re-bless with justification.
Acceptance criteria
- AC-1 integration From a new game, walking past the vestibule after slice.complete loads valley-01 at its arrive-from-slice spawn.
- AC-2 integration Walking back through valley-01's start returns to the slice vestibule spawn, and slice facts (door open, chest looted) persist across the round trip.
- AC-3 e2e A Playwright spec walks slice vestibule -> valley-01 -> valley-02 -> valley-03 -> briar-glen-lane via transitions only (teleport allowed inside a scene), CI-paced.
- AC-4 unit Content validation fails if any scene transition names an unknown scene or spawn.
Closed
Completed in PR #406 (2026-11-12)
area:contentepic:e24milestone:m2milestone:m3
M3 MVP content 1
mw-e24.22Fact-gated NPC presence in scenes (appear/leave when a fact changes)
Description
From PR #427: Tansy Pell cannot be placed in town because she is in the Fold until rescued, and scenes have no fact-gated presence. Add a scene spawn field (e.g. presentWhen: condition) evaluated on load and on factChanged, spawning/despawning the NPC deterministically, saved via existing facts. AC: Tansy appears at the bakery once tansy.saved is true (integration), absent before; save/load keeps presence consistent; no hash change for scenes without the field.
Closed
Completed in PR #428 (2026-11-12)
area:contentepic:e24milestone:m3
E25Wilderness RegionThe Briarwood, Mooring's Watch, the River Wend and the Deepworks' exterior approach form an interconnected wilderness around Briar Glen: the Sleepers' Path, the Standing Horn, the wolf den, the woodcutter's hut, the millpond and drowned lane, the watchtower with its signal mirror and supply tunnel, and the boarded mine gate, plus the river (Briar Glen Quay and the Stone Stair, the Millweir sluice walk, the Weeping Adit into the lower mine, the towpath and Kestrel Lock, and barge travel to Wendmouth) — with loops and shortcuts unlocked by abilities.4/26 done · 15%
M2 Class fantasies 2
mw-e25.1Block out the Briarwood with paths, clearings and the priory road
Description
Why
The wilderness ties town, tower, mine and priory together; its paths must loop.
What
As a player, I want a thick old forest with readable paths and hidden side trails so that exploring it feels dense but navigable.
Grey-box Briarwood (~300×250 m): town edge, main trail, Sleepers' Path corridor, Standing Horn clearing, wolf den hollow, woodcutter's hut, millpond and drowned lane, priory road up the hill, spur trail to Mooring's Watch, mine road junction. Terrain with bramble walls (flammable, cuttable) forming soft boundaries. (The Briarwood → Sleeping Ox wake rule is authored in e24-sleeping-ox.)
Scope
- In: Terrain blockout, path network, bramble boundaries, connectivity nodes, region volume used by respawn rules
- Out: Per-location content (other E25 items)
- Out: Foliage art (e25-wilds-art-dress)
Notes
Bramble walls are systemic: burning them opens shortcuts (with fire-spread risk, Juniper's fear).
Acceptance criteria
- AC-1 content Given the scene, when validated, then every bible Briarwood location has a named node linked in connectivity.json.
- AC-2 integration Given a bramble wall, when it burns, then the navmesh link behind it opens and fire spread is capped at a 12 m radius (edge: no forest-wide fire).
- AC-3 integration Given the player stands anywhere in the Briarwood, when the region is queried, then it reports region briarwood (used by respawn rules).
- AC-4 perf Given the grey-box forest with placeholder trees, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e24.1 Design the world map: areas, loops, shortcuts and ability gates done
Closed
Completed in PR #413 (2026-11-12)
area:contentepic:e25milestone:m2mw-e25.6Block out Mooring's Watch: courtyard, tower floors, roof and tunnel
Description
Why
The abandoned watchtower is a vertical infiltration space and the gateway to the upper mine.
What
As a player, I want a ruined tower I can take floor by floor so that it rewards climbing, sneaking or holding ground.
Spur approach, goblin lookout camp below the walls, courtyard, four tower floors (armour stand hall, keeper's quarters with the log, bell cord shaft, roof with the signal mirror), outside climb routes (ivy, broken masonry), and the cellar entrance to the collapsed supply tunnel.
Scope
- In: Tower blockout, lookout camp area, climb routes, cellar tunnel entry, connectivity
- Out: Encounters (e25-mooring-watch-encounters)
- Out: Mirror puzzle (e25-signal-mirror)
Notes
Tower volume doubles as the Sentinel leash volume.
Acceptance criteria
- AC-1 content Given the scene, when validated, then the tower volume, roof, bell cord and tunnel entry exist as named entities.
- AC-2 integration Given the navmesh, when queried, then the roof is reachable by both the interior stair and at least one exterior climb.
- AC-3 perf Given the view from the roof over the valley, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e24.1 Design the world map: areas, loops, shortcuts and ability gates done
Closed
Completed in PR #429 (2026-11-12)
area:contentepic:e25milestone:m2
M3 MVP content 2
mw-e25.22Link Briar Glen to the Briarwood: woodside path and priory road
Description
Planned world-map edges market-green-briarwood-edge (docs/design/world-map.md, mw-e24.1): build the scene transitions between the town's upper lane and the Briarwood, in both directions, with spawns on each side.
Acceptance criteria
AC-1 integration Given a new game in Briar Glen, when the player walks the woodside path, then the Briarwood scene loads and the way back returns to the town spawn. AC-2 content world:connectivity edge market-green-briarwood-edge lists both transitions and pnpm world:validate passes.
Closed
Completed in PR #413 (2026-11-12)
area:contentepic:e25milestone:m3mw-e25.25Connect the Briarwood, Mooring's Watch and the priory road into the forest loop
Description
Planned world-map edges for the forest-watchtower-priory loop (bible 3.1, mw-e24.1): the Briarwood hub to Mooring's Watch courtyard, the watch to the priory road by the ridge path, and the priory road to Bellwater Priory.
Acceptance criteria
AC-1 integration Given the Briarwood, when the player takes each of the three paths, then they arrive at the matching scene and can return. AC-2 content the three loop edges list transitions.
Closed
Completed in PR #466 (2026-11-14)
area:contentepic:e25milestone:m3
E26First DungeonOne exceptional multi-route dungeon network — the Deepworks mine, Bellwater Priory and the Knot labyrinth (Outer Ring, Ossuary Galleries, Chapel of Echoes, the Fold, Horn's Hall, Sounding Shaft, the Cradle) — combining combat, stealth, the six MVP puzzles, creatures and secrets, ending in the Sounding Shaft climb and the ringing of the Vesper Bell, with level states for all four endings.5/28 done · 18%
M2 Class fantasies 5
mw-e26.1Design the dungeon network: areas, routes, puzzles and endings
Description
Why
"One fantastic dungeon with interconnected systems beats ten generic dungeons" — it needs a master plan.
What
docs/design/dungeon.md: area graph for the Deepworks, Bellwater Priory and the Knot; critical path vs optional; macro-routes per class; placement of the six MVP puzzles (Sunbeam, Frozen Channel, Scales, Watchers, Dark Crossing, Chimes); shortcut list; encounter pacing chart; Fold respawn logic; set pieces (sour bell, Horn's Hall, Cradle, climb, Vespers); level-state table for the four endings.
Scope
- In: Design doc, area graph additions to connectivity.json, puzzle placement table, ending state table
- Out: Puzzle mechanics (E15)
- Out: Quest graph (e39-main-quest-data)
Notes
Puzzle placement: Frozen Channel in the Deepworks flooded gallery; Sunbeam in the priory crypt stair; Watchers = choir statues; Scales at the Outer Ring Hornfolk gate; Dark Crossing in the Ossuary pit; Chimes in the Chapel of Echoes.
Acceptance criteria
- AC-1 content Given the doc, when reviewed, then each of the six puzzles is placed in a named area with its reference puzzle key (e15-reference-puzzles-a/b).
- AC-2 content Given the area graph, when validated with connectivity.json, then every area is reachable and the Cradle is reachable by >= 2 macro-routes.
- AC-3 content Given the ending table, when reviewed, then each of the four endings lists the world facts and the level variants it changes.
- AC-4 content Given the pacing chart, when reviewed, then no two combat-heavy areas are adjacent on the critical path without a quiet area between (edge: pacing).
Waits on
- mw-e16.1 Write the MVP obstacle × class route matrix done
- mw-e24.1 Design the world map: areas, loops, shortcuts and ability gates done
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #439 (2026-11-12)
area:designepic:e26milestone:m2mw-e26.2Block out the Deepworks: winch house, galleries, goblin hall, breach
Description
Why
The mine is the dungeon's first layer and the goblins' home.
What
As a player, I want a boarded-up glenstone and silver mine with levels, rails and a cage lift so that descending feels like a journey.
Winch house and cage lift, the old glenstone galleries (square pillared halls, half-cut blocks still in the walls) with mine-cart rails, the narrow silver galleries, the old cutting hall (goblin camp), the supply-tunnel arrival point, the flooded lower gallery (Frozen Channel site and the arrival point of the Weeping Adit river route), the Stone Stair head in the cutting hall, Vane's sealed lower gallery below the Bell Line, and the breach into the Knot's Outer Ring.
Scope
- In: Mine scene blockout, cage lift mechanism, rails, connectivity nodes, breach transition
- Out: Encounters (e26-deepworks-encounters)
- Out: Gallery/breach content (e26-vane-gallery-breach)
Notes
Mine decision: the Deepworks interior is E26; exterior gate and supply tunnel collapse are E25.
Acceptance criteria
- AC-1 content Given the scene, when validated, then every bible Deepworks location exists as a named node, including the Adit arrival and the Stone Stair head.
- AC-2 integration Given the cage lift, when its winch is operated or its pulley shot, then it descends and the lower level becomes reachable.
- AC-3 perf Given the cutting hall grey-box, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e03.18 Build doors, locks, levers and buttons as data-wired mechanisms done
- mw-e26.1 Design the dungeon network: areas, routes, puzzles and endings done
Closed
Completed in PR #450 (2026-11-13)
area:contentepic:e26milestone:m2mw-e26.5Block out Bellwater Priory: nave, choir, scriptorium, belfry, crypt
Description
Why
The ruined monastery is the lore heart and the top of the Sounding Shaft.
What
As a player, I want a roofless priory on the hill with a tall belfry so that climbing it feels like approaching the answer.
Priory road gate, roofless nave, choir with singing-monk statues and the hidden choir door, scriptorium, garden with the dead pear tree, belfry with the cracked Vesper Bell and the top of the Sounding Shaft, crypt (sealed from below), cistern end joining the Glen Well route.
Scope
- In: Priory blockout, belfry climb, crypt, cistern link, connectivity nodes
- Out: Room content (priory items)
Notes
Can be visited before Act II via the forest (bible beat 9).
Acceptance criteria
- AC-1 content Given the scene, when validated, then every bible priory location exists as a named node, including the cistern link stub that e24-glen-well-cistern connects to.
- AC-2 integration Given the crypt door, when approached from the priory, then it is sealed and has no route from this side until shortcut.crypt is set.
- AC-3 perf Given the view from the belfry, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e26.1 Design the dungeon network: areas, routes, puzzles and endings done
Closed
Completed in PR #441 (2026-11-12)
area:contentepic:e26milestone:m2mw-e26.9Block out the Knot's Outer Ring, Ossuary Galleries and Chapel
Description
Why
The labyrinth's outer half is where the Knot's rules are learned.
What
As a player, I want rune-lined corridors, bone galleries and an echoing chapel so that the labyrinth feels ancient and legible.
Outer Ring (looping corridors with rune bands, spider nests, the Scales gate), Ossuary Galleries (bone niches, Clatter's mushroom garden, the Dark Crossing pit), Chapel of Echoes (acoustic space, portcullis with counterweight, Chimes site). Multiple routes, vents and crawlspaces.
Scope
- In: Blockout of three areas, loops, vents, portcullis mechanism, connectivity
- Out: Inner Knot (e26-knot-inner-greybox)
- Out: Rune navigation rule (e26-knot-rune-navigation)
Notes
Budget: each area loads as a separate scene or streamed chunk to keep memory in budget.
Acceptance criteria
- AC-1 content Given the scenes, when validated, then Outer Ring, Ossuary and Chapel exist with >= 2 routes between each adjacent pair.
- AC-2 integration Given the Chapel portcullis counterweight hit by any projectile or cut, when it drops, then the portcullis closes.
- AC-3 perf Given the Ossuary grey-box, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e03.13 Support hanging objects on ropes that burn, cut or snap done
- mw-e26.1 Design the dungeon network: areas, routes, puzzles and endings done
- mw-e26.2 Block out the Deepworks: winch house, galleries, goblin hall, breach done
Closed
Completed in PR #464 (2026-11-14)
area:contentepic:e26milestone:m2mw-e26.10Block out the Fold, Horn's Hall, Sounding Shaft foot and the Cradle
Description
Why
The inner Knot hosts the reveal, the warden and the climax.
What
As a player, I want the heart of the labyrinth to feel sacred and strange so that the reveal and finale have a stage.
Horn's Hall (arena outside the Fold), the Fold (sealed vault with moss beds), the crypt passage up to the priory (sealed), the Sounding Shaft foot, the Cradle (round chamber, carved faces turned inward), plus a secret sorcerer route to the Cradle via Hornfolk inscriptions.
Scope
- In: Blockout of four areas, secret route, connectivity
- Out: Area content (inner Knot items)
Notes
The Sounding Shaft interior climb is e26-sounding-shaft-climb.
Acceptance criteria
- AC-1 content Given the scenes, when validated, then Horn's Hall, the Fold, the Shaft foot and the Cradle exist and connect per connectivity.json.
- AC-2 integration Given the secret route, when the inscription is read by a sorcerer with the fragments, then it opens; otherwise it is a wall (edge).
- AC-3 perf Given the Cradle grey-box, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e26.9 Block out the Knot's Outer Ring, Ossuary Galleries and Chapel done
Closed
Completed in PR #465 (2026-11-14)
area:contentepic:e26milestone:m2
E27Dynamic World StateMeaningful actions persist: doors opened, walls smashed, ropes burned, chests looted, creatures befriended, treasures stolen, characters helped. A typed, deterministic world-fact store plus per-entity deltas against authored baselines captures the world as the player left it and flows through e30 save serialization. Quests, dialogue, puzzles and level variants all read the same facts.10/16 done · 63%
M1 Grey-box slice 6
mw-e27.1Store persistent world facts in a typed deterministic fact store
Description
Why
Doors opened, characters helped, creatures befriended and treasures stolen must persist and be readable by every system (E27 outcome).
What
As a developer, I want a single typed fact store so that quests, dialogue, puzzles and levels read and write world state consistently.
Facts are namespaced keys (global: "quest.missing-miller.stage", entity-scoped: "entity:<levelId>/<entityId>.opened") with typed values (bool, int, enum, string id, timestamp in sim ticks). API: get/set/increment/has, change events, transactional batch writes within a tick, deterministic iteration and a stable content hash for replays.
Scope
- In: Fact store in src/sim with typed values, namespaces, change events
- In: Transaction batching per tick, deterministic hash
- In: Fact-setter receiver node for the e03 signal graph
- Out: Registry/schema of declared facts — see e27-fact-registry
- Out: Entity physical state persistence — see e27-entity-deltas
- Out: Serialization — see e27-save-integration
Notes
Timestamps are sim ticks from the injected clock, never wall clock. Keep the store small and flat; it is not a general database.
Acceptance criteria
- AC-1 unit Given an empty store, when set("door.cellar.opened", true) is called, then get returns true and one factChanged event fires with old=undefined/new=true.
- AC-2 unit Given a transaction that sets 3 facts and then throws, when rolled back, then no facts change and no events fire.
- AC-3 unit Given facts inserted in different orders on two runs, when hashed, then hashes are identical.
- AC-4 unit Given set with a value of the wrong type for an existing fact (string to bool), when called, then it throws a typed error and the value is unchanged (edge).
- AC-5 unit Given a signal-graph fact-setter receiver, when its input goes high, then the configured fact is set on that tick.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
Closed
Completed in PR #69 (2026-09-28)
area:simclass:allepic:e27milestone:m1size:mmw-e27.2Declare world facts in a content registry validated at load
Description
Why
Unregistered fact strings are typos waiting to break quests. A registry makes facts discoverable, typed and migratable.
What
content/facts/*.json registry: key pattern, type, default, persistence policy (see e27-persistence-policies), description, owner system. All quest/dialogue/puzzle/level content references are cross-validated against it. Undeclared writes fail in dev/test.
Scope
- In: Registry schema + loader + cross-reference validator for content files
- In: Dev/test strict mode: undeclared write throws; prod: logs + ignores
- Out: Quest/dialogue content — see e23-quest-runtime, e22-dialogue-runtime
Notes
Entity-scoped fact patterns ("entity:*.opened") are declared once as templates.
Acceptance criteria
- AC-1 content Given the registry, when validated, then every entry has type, default and description and keys are unique.
- AC-2 content Given a dialogue or puzzle file referencing an undeclared fact, when validated, then validation fails naming the file and fact.
- AC-3 unit Given strict mode, when code writes an undeclared fact, then it throws; in production mode it logs a warning and ignores the write (edge).
- AC-4 unit Given a template fact "entity:*.looted", when "entity:mine/chest-3.looted" is written, then it validates against the template type.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #71 (2026-09-28)
area:contentepic:e27milestone:m1size:smw-e27.3Persist entity changes as deltas from authored level baselines
Description
Why
Smashed walls, burned bridges, moved crates, looted chests and dead creatures must stay that way — without saving the entire world.
What
As a player, I want the world to be exactly as I left it when I come back so that my actions matter.
Each level has an authored baseline. Entities flagged persistent record deltas: transform (if moved beyond epsilon and at rest), destroyed/broken, burnt state, property overrides (wetness excluded if transient), mechanism state, container contents, spawned persistent entities (dropped items, debris flagged persistent), and actor life/disposition hooks. On level load, deltas apply after baseline spawn and before first tick.
Scope
- In: Delta capture on level unload/save, delta apply on load, spawned-entity records
- In: Per-component persistence declarations (which properties persist)
- In: Stable entity ids for authored entities; generated ids for spawned ones
- Out: Policies for transient states — see e27-persistence-policies
- Out: Actor AI state persistence — see mw-e11 (supplies its component serializer)
Notes
Authored entity ids must be stable across level edits; the level pipeline assigns GUIDs and a lint warns if a persistent entity id disappears (orphaned delta → dropped with warning).
Acceptance criteria
- AC-1 unit Given a crate moved 5 m and at rest, when deltas are captured, then one transform delta exists; a crate nudged 1 mm produces none.
- AC-2 unit Given a broken wall and a looted chest, when captured and re-applied to a fresh baseline, then the wall is broken (no collider) and the chest is empty.
- AC-3 unit Given a persistent dropped item spawned at runtime, when captured and re-applied, then it exists at the same position with the same item id.
- AC-4 unit Given a delta referencing an entity id no longer present in the level baseline, when applied, then it is skipped with a warning and loading continues (edge).
- AC-5 integration Given the greybox slice, when the player burns a rope bridge, leaves the level and returns, then the bridge is still gone and replay hashes match.
- AC-6 perf Given 5,000 deltas, when applied on level load, then application takes ≤ 30 ms on the reference machine.
Waits on
- mw-e03.10 Simulate movable, throwable and stackable physics objects in sim done
- mw-e03.11 Break fragile objects and smash weakened walls into debris done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #158 (AC-5 full slice round-trip split to mw-e27.15) (2026-10-03)
area:simclass:allepic:e27milestone:m1size:lmw-e27.4Serialize world facts and entity deltas through the save system
Description
Why
Save-game stability is a technical pillar; world state is the largest and most fragile part of a save.
What
As a player, I want saving and loading to restore the whole world exactly so that I can trust my progress.
Register fact store + per-level delta sets as save sections with the e30 versioned format; lazy-load deltas for unloaded levels; version and migration hooks for fact renames/removals and component persistence changes; round-trip and migration tests.
Scope
- In: Save section providers (facts, deltas per level), schema versions, migration functions
- In: Round-trip property tests with generated fact/delta sets
- In: Corruption handling: invalid section → recover what is valid, report
- Out: Storage/IndexedDB and save slots UI — see e30-save-serialization
Notes
Fact renames are declared in the registry (renamedFrom) so migrations are generated, not hand-written, where possible.
Acceptance criteria
- AC-1 unit Given 1,000 randomly generated facts and deltas (fast-check), when serialized and deserialized, then the result is deep-equal and the state hash matches.
- AC-2 unit Given a v1 save and a registry declaring "door.cellar.open" renamedFrom "cellar_door_open", when loaded, then the migrated fact holds the old value.
- AC-3 unit Given a save whose delta section for one level is corrupted, when loaded, then other sections load, that level falls back to baseline and a recoverable error is reported (edge).
- AC-4 e2e Given the greybox slice, when the player opens a door, loots a chest, saves, closes the browser tab and reloads the save, then the door is open and the chest empty.
- AC-5 integration Given a save created by the previous release fixture, when loaded by the current build, then it loads without error (migration test).
Waits on
- mw-e27.2 Declare world facts in a content registry validated at load done
- mw-e27.3 Persist entity changes as deltas from authored level baselines done
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #164 (AC-4 slice chest version split to mw-e27.16) (2026-10-03)
area:simclass:allepic:e27milestone:m1size:mmw-e27.5Query world facts with a shared condition API for all systems
Description
Why
Quests, dialogue, puzzles, shops and level variants all need "if the minotaur was befriended and the cellar is open". One predicate language avoids four inconsistent ones.
What
As a designer, I want to write conditions like all(fact(minotaur.befriended), not(fact(cellar.sealed))) in any content file so that every system gates on world state identically.
JSON predicate AST: fact eq/neq/gt/gte/lt/lte/has, template matches (count(entity:*.looted in level mine) ≥ 3), all/any/not, compiled with dependency lists for change-driven re-evaluation.
Scope
- In: Predicate AST schema, compiler, dependency extraction, explain()
- In: Subscription API: onConditionChanged(cond, cb)
- Out: Puzzle-specific live-property predicates — see e15-goal-conditions (extends this)
Notes
Related: e22-dialogue-runtime and e23-quest-runtime should consume this rather than inventing their own (integration pass).
Acceptance criteria
- AC-1 unit Given condition all(fact(a)=true, fact(b)≥3), when a=true and b=2, then false; when b becomes 3, then onConditionChanged fires true exactly once.
- AC-2 unit Given count(entity:mine/*.looted) ≥ 3, when three matching facts are true, then it evaluates true.
- AC-3 content Given a condition referencing an undeclared fact, when validated, then it fails (via registry).
- AC-4 unit Given a deeply nested condition (depth 64), when compiled, then it compiles without stack overflow; depth > 64 is rejected with a clear error (edge).
Waits on
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e27.2 Declare world facts in a content registry validated at load done
Closed
Completed in PR #111 (2026-09-30)
area:simepic:e27milestone:m1size:smw-e27.6Inspect, edit and diff world state from the debug console
Description
Why
Testing a late-game branch should not require replaying the game. Developer tooling pillar.
What
Debug console commands: facts.list [prefix], facts.get/set, deltas.list [level], state.diff <saveA> <saveB>, state.snapshot/restore (in-memory), plus a panel view.
Scope
- In: Console commands and panel
- In: Diff output grouped by facts and per-level deltas
- Out: Save slot management — see e30-save-serialization
Notes
Dev builds only.
Acceptance criteria
- AC-1 integration Given the console, when facts.set quest.x.stage 3 is executed, then the fact is set and dependent conditions re-evaluate.
- AC-2 integration Given two saves differing by one looted chest, when state.diff runs, then the output lists exactly that delta.
- AC-3 integration Given facts.set with an undeclared key, when executed, then the console reports the error without throwing (edge).
Waits on
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e27.3 Persist entity changes as deltas from authored level baselines done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #369 (2026-11-09)
area:toolsepic:e27milestone:m1size:s
M2 Class fantasies 4
mw-e27.7Apply permanent, rest-reset and transient persistence policies
Description
Why
Not everything should persist forever: fires burn out, ice melts, respawning herbs regrow, enemy camps may restock. Policies keep the world believable and saves small.
What
As a designer, I want to choose whether a change is permanent, resets on rest/time, or is transient so that the world feels alive without undoing player achievements.
Policies per fact/component: permanent; resetAfter(ticks) or onRest; transient (never saved: wetness, temperature, active fires by default); decaysTo(value) on reload (burning → burnt). Declared in registry/component metadata.
Scope
- In: Policy evaluation on save capture and on load/rest
- In: Rest event hook (from inn/campfire — owner elsewhere)
- In: Defaults: element transients not saved; burning resolves to burnt on reload
- Out: Offscreen time simulation — see e27-offscreen-catchup
- Out: Resting UI — see mw-e17 / mw-e24
Notes
Resolving "burning at save time" to burnt on reload avoids saving the element field while keeping outcome consistency (the fire would have finished anyway).
Acceptance criteria
- AC-1 unit Given a fact with policy resetAfter(72000 ticks), when 72000 ticks pass (loaded or not), then it returns to its default.
- AC-2 unit Given an entity burning at save time with policy decaysTo(burnt), when loaded, then it is in burnt state and not burning.
- AC-3 unit Given wetness (transient), when saved and reloaded, then wetness is 0.
- AC-4 unit Given a looted chest with permanent policy, when the player rests 10 times, then it stays looted.
- AC-5 unit Given a fact declared both transient and with a reset timer (invalid combination), when the registry validates, then it fails (edge).
Waits on
- mw-e27.2 Declare world facts in a content registry validated at load done
- mw-e27.4 Serialize world facts and entity deltas through the save system done
Closed
Completed in PR #372 (2026-11-09)
area:simclass:allepic:e27milestone:m2size:smw-e27.8Spawn fact-gated entity variants so the world reacts to choices
Description
Why
Helping the miller should put him back in his mill; befriending the minotaur should change the labyrinth. The world visibly reacting is what makes actions feel meaningful.
What
As a designer, I want to mark level entities as present/absent or swapped based on world conditions so that the world changes after player choices without scripting.
Level entities may declare spawnIf/despawnIf conditions (e27-fact-queries) and variant groups (exactly one variant active, chosen by first matching condition). Evaluated on level load and on condition change when not in player view (no pop-in within camera frustum unless flagged).
Scope
- In: spawnIf/despawnIf + variant groups in level schema
- In: Evaluation on load + deferred out-of-view swaps
- In: Interaction with deltas (a despawned entity keeps its delta)
- Out: NPC schedules — see mw-e22 / mw-e14
Notes
Example: boarded-up door variant appears after "cellar.sealed_by_guard"; the thief can still break the boards (they are breakable).
Acceptance criteria
- AC-1 unit Given an entity with spawnIf(fact(miller.rescued)), when the fact becomes true while the entity location is out of view, then it spawns; if in view, it waits until out of view.
- AC-2 unit Given a variant group of three with conditions, when multiple conditions match, then the first listed wins deterministically.
- AC-3 content Given a variant group where no variant has a default/fallback, when validated, then a warning lists the uncovered case.
- AC-4 unit Given a despawned entity with deltas, when it respawns later, then its deltas are re-applied (edge).
- AC-5 integration Given the greybox slice, when a fact flips, then the corresponding variant is active after save/reload.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e27.3 Persist entity changes as deltas from authored level baselines done
- mw-e27.5 Query world facts with a shared condition API for all systems done
Closed
Completed in PR #371 (2026-11-09)
area:contentarea:simclass:allepic:e27milestone:m2size:mmw-e27.9Track object ownership and record thefts as world facts
Description
Why
"Treasures stolen" must persist, and a thief's actions should have consequences (shopkeepers notice missing stock) — without a full crime system in this epic.
What
As a thief, I want stolen goods to be tracked as stolen so that fences, witnesses and owners can react later.
Owner tag property on items/containers/areas (owner faction or NPC id); taking an owned item without permission sets a stolen flag on the item instance and a theft fact (owner, item, location, witnessed=unknown until AI resolves). Returning or buying clears it.
Scope
- In: Ownership property on items/containers, permission predicate
- In: Stolen item flag + theft facts; clearing rules
- In: Event for witness resolution by AI
- Out: Witness detection and crime reactions — see mw-e11 / mw-e10
- Out: Fences and prices — see e20-shop-system
Notes
Related: e17-inventory-core stores item instance flags.
Acceptance criteria
- AC-1 unit Given an item owned by NPC Mirela and no permission, when the player takes it, then the item instance is flagged stolen and a theft fact with owner Mirela is recorded.
- AC-2 unit Given the player buys the item or is gifted it, when transferred, then no stolen flag is set.
- AC-3 unit Given a stolen item returned into its owner container, when placed, then the stolen flag clears and theft fact resolves to returned.
- AC-4 unit Given an unowned item (owner none), when taken, then no theft fact is recorded (edge).
- AC-5 integration Given a theft then save/reload, when inspected, then the stolen flag and theft fact persist.
Waits on
- mw-e17.3 Implement inventory model with stacks, keys, books and quest items done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #374 (2026-11-10)
area:simclass:thiefepic:e27milestone:m2size:smw-e27.12Run a deterministic in-game clock with time-of-day and schedules
Description
Why
Bookshops rotate stock "every few in-game days", quests have deadlines, the Sleepers' Path is visible only at night, rest advances time and jail sentences cost days. Several groups assumed a world clock that nobody owned; without one, each invents its own notion of time and saves disagree.
What
As a designer, I want one deterministic in-game clock with time-of-day phases and schedulable events so that restocks, deadlines, NPC schedules and night-only secrets all agree on what time it is.
- Game time derives from sim ticks at a data-configured rate (default: 1 game day = 60 real minutes). Clock = day number + minute of day; phases (dawn, day, dusk, night) with boundaries in data.
- Pause rules: the clock advances only while the sim steps. Menus that pause the sim pause the clock; dialogue, shop and inventory screens pause it by default (data flag per screen).
- Explicit advancement:
advanceTime(duration, reason)for rest, study, jail and travel; the only way time jumps. - Events:
time.hourChanged,time.phaseChanged,time.dayStarted;schedule.at(gameTime, eventId, payload)andschedule.every(period, …)with deterministic ordering by (time, priority, id). On a jump, subscribers chooseeach(fire once per crossed boundary, in order) orlatest(coalesce into one callback) — lazy consumers such as shop restock uselatestplus elapsed time. - Query API for other systems (
timeOfDay,phase,elapsedSince), used by the condition DSL and light-field sun/moon intensity. - Clock and pending schedule serialise through the world-state save section.
Scope
- In: Clock component, phase table data, pause rules, advanceTime, events, scheduler, save integration, debug commands (
time set,time advance) - Out: Sky/lighting visuals — render/E37 (subscribe to phaseChanged)
- Out: Rest points — see e27-rest-points
- Out: Specific schedules (restock, deadlines, NPC routines) — owned by consumers
Notes
Consumers: e20-restock-rotation, e21-rotating-stock, e23-failure-continuations (deadlines), e12-needs-hooks, e07-spellbook-learning (study time), e27-offscreen-catchup, e22-condition-effect-dsl (
timeOfDaycondition), e22-crime-bounty (jail sentences). e11-patrol-routines deliberately keeps simple time windows at m1; later schedules can read this clock.Acceptance criteria
- AC-1 unit Given day length 60 real minutes, a 60 Hz sim and a start of day 1 06:00, when 216,000 ticks elapse, then the clock reads day 2 06:00 and exactly one
time.dayStartedevent has fired. - AC-2 unit Given a sim-pausing menu open for 10 real minutes, when it closes, then game time has not advanced.
- AC-3 unit Given advanceTime(8 h) from day 1 22:00, when applied, then
time.hourChangedfires 8 times in order,time.phaseChangednight→dawn fires once,time.dayStartedfires once, and alatest-mode subscriber receives exactly one callback with elapsed = 480 game minutes. - AC-4 unit Given two events scheduled for the same game minute and registered in either order, when that minute is reached, then they fire in (time, priority, id) order identically across 100 seeded runs.
- AC-5 unit Given a save at day 3 14:30 with 2 pending scheduled events, when reloaded, then the clock and schedule are restored and the sim state hash equals the pre-save hash.
- AC-6 unit Given advanceTime called with a zero, negative or non-finite duration, when applied, then it is rejected with reason
invalid-durationand no events fire (edge).
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e27.4 Serialize world facts and entity deltas through the save system done
Closed
Completed in PR #269 (2026-10-24)
area:simclass:allepic:e27milestone:m2size:m
E28Audio & Combat FeedbackWeapons, spells, footsteps, creatures and environments have satisfying positional audio and feedback, driven by data from sim events, within the browser and performance baseline.12/20 done · 60%
M1 Grey-box slice 6
mw-e28.1Web Audio engine: registry, buses, voice pool, positional audio
Description
Why
Every weapon, footstep and creature needs a robust, browser-safe audio core before feedback can feel satisfying (E28 outcome).
What
A
src/audioengine: lazily createdAudioContext, resumed on first user gesture (autoplay policy) with a queue that plays requests younger than 2 s and drops older ones. Sound registry from a content manifest (id → url(s), variants, bus, spatial, loop, priority). Decode + LRU cache (cap 64 MB decoded). Bus graph: master → music, sfx (subgroups combat, footsteps, creatures), ambience, ui. Positional audio viaPannerNode(HRTF on High, equalpower on Low), inverse distance, ref 2 m, max 40 m. Listener follows the camera each render frame. Voice pool max 48 (Low 24) with priority + distance stealing. API:play(cueId, {position|entity, volume, pitch}) → handlewith stop/fade.Scope
- In: Context lifecycle, registry, cache, buses, panner, listener, voice pool, handle API
- In: Fake AudioContext test double for ≥ 90% coverage of src/audio
- Out: Mapping sim events to sounds (e28-cue-sheets)
- Out: Volume settings UI (e28-mixer-settings)
- Out: Sound asset creation (art/audio group)
Notes
Presentation only: nothing in src/audio may write to sim state. Related: stealth noise simulation is owned by e09-sound-propagation.
Acceptance criteria
- AC-1 unit Given a suspended AudioContext, when
playis called before any gesture, then the request is queued; on resume, requests younger than 2 s play and older ones are dropped. - AC-2 unit Given 48 active voices, when a higher-priority request arrives, then the lowest-priority, farthest voice is stolen; when a lower-priority request arrives, then it is rejected.
- AC-3 unit Given a sound attached to a moving entity, when the entity moves, then the panner position updates in the same frame.
- AC-4 unit Given an unknown cue id, when played, then a null handle is returned, one warning is logged per id, and nothing throws.
- AC-5 unit Given a corrupt audio file, when decoding fails, then the id is marked failed and dev builds substitute a placeholder beep; production stays silent.
- AC-6 e2e Given the greybox testbed in Chromium, Firefox and WebKit, when the page is clicked and a test cue plays, then
AudioContext.stateisrunningand no console errors occur. - AC-7 perf Given 48 positional voices on moving entities, when measured, then main-thread audio update ≤ 0.5 ms per frame p95 on the reference machine.
Waits on
- mw-e00.1 Scaffold TypeScript/Vite/pnpm repo with lint, typecheck and Vitest done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
Closed
Completed in PR #52 (2026-09-28)
area:audioclass:allepic:e28milestone:m1mw-e28.2Placeholder sound pack keyed by final asset ids
Description
Why
Gameplay is never blocked by final audio; placeholders named with final ids make swapping in real assets a file replacement.
What
A script (
tools/audio/gen-placeholders) synthesising simple placeholder sounds (filtered noise bursts, tones, clicks) or pulling CC0 clips, written under the final asset ids (e.g.sfx-knight-parry-metal-01). Manifest entries flaggedplaceholder: true. A dev report lists remaining placeholders so the asset group can prioritise prompt beads.Scope
- In: Generator script, initial placeholder set for combat/archery/footsteps/UI, manifest flags, placeholder report
- Out: Final SFX creation (art/audio group, e38)
Notes
Initial ids are listed in the combat plan summary for the asset group. Licences for any CC0 clips go in assets/CREDITS.md.
Acceptance criteria
- AC-1 content Given all cue sheets, when validated, then every referenced sound id resolves to a manifest entry (placeholder or final).
- AC-2 content Given the placeholder set, when measured, then total size ≤ 2 MB.
- AC-3 content Given any CC0-sourced placeholder, when validated, then it has a licence/provenance line in assets/CREDITS.md.
- AC-4 unit Given the placeholder report command, when run, then it lists every id with
placeholder: trueand a total count.
Waits on
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
Closed
Completed in PR #112 (2026-09-30)
area:sfxclass:allepic:e28milestone:m1mw-e28.3Data-driven cue sheets map sim events to sounds
Description
Why
Sounds must follow game events without hard-coded calls scattered across code, and must never influence the deterministic sim.
What
A game-layer bridge subscribing to sim events and resolving cue sheet rules (content data):
{event, match:{...tags}, cue, variants, pitchJitter, volumeJitterDb, cooldownMs, priority}. Most-specific match wins. A separate presentation RNG handles variation (never the sim RNG). Identical cues on the same entity within 30 ms are de-duplicated. The rule matcher is generic so e29-vfx-cue-sheets reuses it.Scope
- In: Rule schema, matcher, bridge, presentation RNG, de-dupe, initial rules for combat events
- Out: Specific sound sets (per-feature audio items)
Notes
Put the matcher in src/game/cues so VFX shares it.
Acceptance criteria
- AC-1 unit Given rules for
DamageAppliedgeneric andDamageApplied{weapon:"metal", target:"bone"}, when a metal-on-bone hit occurs, then the specific cue plays. - AC-2 unit Given two identical cue requests on one entity 20 ms apart, when processed, then only one plays.
- AC-3 integration Given a 60 s combat replay run with audio enabled and disabled, when compared, then sim state hashes are identical every tick.
- AC-4 unit Given a rule with 4 variants and pitch jitter ±5%, when triggered 1000 times, then all variants occur and pitch stays within ±5%.
- AC-5 content Given a cue sheet referencing an unknown sim event type, when validated, then it fails with the rule index.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
Closed
Completed in PR #86 (2026-09-29)
area:audioclass:allepic:e28milestone:m1mw-e28.4Combat impact audio matrix: hits, blocks, parries, whiffs
Description
Why
Sound carries half of combat weight; a parry must ring out and a heavy blow must thud (Combat is intrinsically fun).
What
As a knight, I want every hit, block and parry to sound distinct so that I can read the fight by ear.
Impact matrix: attacker material (blade, blunt-wood, blunt-metal, claw, bone, arrow) × target material (flesh, bone, metal armor, wood shield, metal shield, stone, wood) → cue. Distinct high-priority cues for block, parry (never stolen), guard break, riposte, wall recoil; whooshes by weapon class and light/heavy; stamina-rejected wheeze; charge-ready. Impact cues start on the first hit-stop frame; volume tier by light/heavy/critical.
Scope
- In: Cue sheet rules, material tags on moves/hurtboxes, parry priority, hit-stop sync
- Out: Final SFX (asset group)
- Out: Creature vocals (e28-creature-audio-hooks)
Notes
Uses placeholders from e28-placeholder-sounds until final assets integrate.
Acceptance criteria
- AC-1 integration Given a sword hitting a skeleton (bone), when
DamageAppliedfires, then the blade×bone cue plays at the victim position. - AC-2 integration Given a successful parry during a 48-voice saturation test, when it fires, then the parry cue plays (not stolen) and a lower-priority voice is dropped.
- AC-3 integration Given a heavy hit with 5-tick hit-stop, when it lands, then the impact cue starts within the first frozen frame (≤ 17 ms after the event).
- AC-4 unit Given a material pair without a specific rule, when resolved, then the attacker-material generic cue plays (no silence).
- AC-5 manual Given the sandbox, when three playtesters close their eyes during a dummy fight, then each can distinguish block, parry and clean hit by sound alone.
Waits on
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e28.2 Placeholder sound pack keyed by final asset ids done
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
Closed
Completed in PR #117 (2026-09-30)
area:audioclass:allepic:e28milestone:m1mw-e28.5Per-bus volume settings, tab-hidden mute and mono option
Description
Why
Players must control their mix; accessibility options such as mono downmix matter for hearing-impaired players (E31).
What
As a player, I want volume sliders per category and audio accessibility options so that I can hear the game the way I need.
Sliders for master, music, sfx, ambience, ui (0–100 mapped to a perceptual −60..0 dB curve, 0 = mute); mute when tab hidden (default on); mono downmix; "night mode" dynamic range compressor. Persisted through e31-settings-framework, applied with a 50 ms ramp (no clicks).
Scope
- In: Bus gain control, settings entries, persistence, ramps, mono and compressor options
- Out: Mix snapshots/ducking (e28-mix-snapshots)
- Out: Sound captions (e28-sound-captions)
Notes
Acceptance criteria
- AC-1 unit Given sfx slider set to 50, when applied, then the sfx bus gain equals the curve value for 50 and ramps over 50 ms.
- AC-2 unit Given slider 0, when applied, then the bus gain is exactly 0.
- AC-3 integration Given tab-hidden mute on, when
visibilitychangereports hidden, then master ramps to 0; on visible, it restores the previous value. - AC-4 integration Given settings changed and the page reloaded, when the audio engine boots, then saved bus volumes are applied before the first sound plays.
- AC-5 unit Given mono downmix on, when a hard-left panned source plays, then both output channels carry equal signal.
Waits on
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #310 (2026-11-08)
area:audioclass:allepic:e28milestone:m1mw-e28.16Parry and riposte cues that are never stolen
Description
Why
A parry must ring out above everything (Combat is intrinsically fun); mw-e28.4 shipped the impact matrix but the sim has no parry event yet.
What
When mw-e04.12 emits its parry event, bind it in src/content/cue-events.ts and add a combat cue-sheet rule playing sfx-knight-parry-metal at voice priority 100 (the riposte accent already plays on the riposte tag).
Scope
- In: cue event binding, cue rule, saturation test
- Out: final parry SFX (mw-e38.12)
Acceptance criteria
- AC-1 integration Given a successful parry during a 48-voice saturation test, when it fires, then the parry cue plays (not stolen) and a lower-priority voice is dropped (carried over from mw-e28.4 AC-2).
Waits on
- mw-e04.12 Parry window and riposte critical attack done
Closed
Completed in PR #125 (2026-10-02)
area:audioclass:allepic:e28milestone:m1milestone:m3
M2 Class fantasies 3
mw-e28.6Surface-aware footsteps modulated by stance and armor
Description
Why
Footsteps ground the character in the world and teach players how loud they are — the audible face of the stealth system.
What
As a player, I want footsteps that change with the surface, my stance and my armor so that I can hear where and how I am moving.
Foot plant events from animation, with a stride-distance fallback (every 0.7 m walking, 1.1 m running). Surface from the ground collider material tag (stone, wood, dirt, grass, shallow-water, metal, rug, gravel). Crouch −10 dB, sprint +3 dB; armor layer (chain jingle / plate clank) by e04-armor-weight class; landing sounds scale with fall height. Creatures use the same system with their own sets.
Scope
- In: Foot plant bridge, surface lookup, stance/armor modulation, landings, creature reuse
- Out: Sim noise emission for stealth (e09-sound-propagation) — audio reads the same surface table but never emits sim noise
Notes
Shared read-only surface table keeps audible loudness consistent with stealth noise values.
Acceptance criteria
- AC-1 integration Given the player walking from stone onto wood, when the next foot plant occurs, then a wood footstep variant plays.
- AC-2 unit Given a crouched player, when a footstep cue resolves, then its volume is 10 dB below the standing value.
- AC-3 unit Given no animation foot plant events (fallback mode), when the player walks 3.5 m, then exactly 5 footsteps play.
- AC-4 unit Given an unknown surface tag, when resolved, then the stone set plays and a dev warning names the tag once.
- AC-5 integration Given plate armor, when walking, then each footstep also plays the plate layer.
Waits on
- mw-e02.11 Emit footstep events tagged with surface material and gait done
- mw-e02.2 Move the player with a deterministic third-person kinematic controller done
- mw-e03.1 Define typed world-property components for systemic interactions done
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
Closed
Completed in PR #117 (2026-09-30)
area:audioclass:allepic:e28milestone:m2mw-e28.8Creature audio slots and audible attack telegraphs
Description
Why
Creatures feel alive through sound, and an audible telegraph before a big attack is key to Zelda-like readability.
What
As a player, I want creatures to sound distinct and warn me audibly before big attacks so that I can react without staring at them.
Creature schema audio slots: idle loop, footstep set, alert bark, attack telegraph, hurt, poise break, death. Telegraph cues play on
TelegraphStartedand must start ≥ 250 ms before active frames; unblockable moves use a distinct high-priority telegraph. Alert-state transitions (Suspicious, Alerted, Combat) expose hook events for stingers.Scope
- In: Schema audio slots, telegraph cue playback, alert-state hook events
- Out: Creature sound creation (asset group)
- Out: Alert-state logic (e11-alert-states)
Notes
Acceptance criteria
- AC-1 content Given creature data, when validated, then every creature declares all required audio slots (placeholder ids allowed).
- AC-2 integration Given a skeleton starting an unblockable move, when
TelegraphStartedfires, then the unblockable telegraph cue plays ≥ 250 ms before the move's first active tick. - AC-3 integration Given a creature transitioning Suspicious→Alerted, when the event fires, then the alert bark plays at its position and a
MusicStingerhook event is emitted. - AC-4 unit Given a creature dies while its idle loop plays, when
Diedis processed, then the loop stops within 200 ms and the death cue plays.
Waits on
- mw-e04.20 Creatures attack via the move system with readable telegraphs done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e12.1 Define zod creature schema with senses, locomotion, attacks and needs done
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
Closed
Completed in PR #328 (2026-11-08)
area:audioclass:allepic:e28milestone:m2mw-e28.9Mix snapshots and ducking for combat, stealth and menus
Description
Why
The mix should focus attention: a parry should cut through the music, and a pause menu should feel like a breath.
What
As a player, I want the mix to shift with the situation so that important sounds cut through when they matter.
Snapshots (explore, stealth, combat, menu, paused) define bus gains and filters (paused = low-pass 800 Hz on sfx/ambience). Transitions ramp 250–1500 ms. Ducking: music and ambience duck −6 dB for 400 ms on parry/critical; sfx ducks −4 dB under UI stingers. Snapshot selection follows game state (alert state via e11-alert-states, pause menu).
Scope
- In: Snapshot data, transition ramps, ducking rules, state bindings
- Out: Adaptive music layers (e28-adaptive-music)
Notes
Acceptance criteria
- AC-1 unit Given the explore snapshot, when the game enters Combat alert state, then bus gains ramp to combat values over the configured duration.
- AC-2 unit Given a parry event, when processed, then music and ambience are −6 dB for 400 ms and restore with a 200 ms ramp.
- AC-3 integration Given the pause menu opens, when the snapshot applies, then sfx/ambience are low-passed at 800 Hz and restore on close.
- AC-4 unit Given two overlapping duck requests, when processed, then attenuation takes the deepest value, not the sum.
Waits on
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e28.5 Per-bus volume settings, tab-hidden mute and mono option done
Closed
Completed in PR #312 (2026-11-08)
area:audioclass:allepic:e28milestone:m2
M3 MVP content 3
mw-e28.11Adaptive music controller with layered stems and stingers
Description
Why
Music that swells into combat and relaxes afterwards carries mood without despair (tone: warmer souls-like).
What
As a player, I want the music to follow the tension of the moment so that exploration, stealth and combat each feel distinct.
A music controller playing location tracks as synchronised stems (explore, tension, stealth, combat), cross-fading layers on bar boundaries using BPM metadata (within 2 bars). Combat layer enters on alert state Combat and exits 8 s after no hostile remains in Combat. Stinger hook events (from e28-creature-audio-hooks) play one-shots quantised to the next beat. Music streams via media elements to protect the initial download budget.
Scope
- In: Stem sync, bar-quantised crossfades, state bindings, stingers, streaming
- Out: Music composition in Suno (asset group, e38)
Notes
Placeholder stems: simple generated drones/pulses at 90 BPM until Suno tracks integrate.
Acceptance criteria
- AC-1 unit Given a 90 BPM track and a layer change requested mid-bar, when processed, then the crossfade starts on the next bar boundary (≤ 2.67 s).
- AC-2 integration Given combat ends, when 4 s pass with no creature in Combat state, then the combat layer fades out; if combat resumes at 3 s, then it does not.
- AC-3 integration Given all four stems playing, when measured after 5 minutes, then stem drift is ≤ 10 ms.
- AC-4 unit Given a music track fails to load, when the controller starts, then it continues silently and logs once without affecting sfx.
Waits on
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e28.9 Mix snapshots and ducking for combat, stealth and menus done
Closed
Completed in PR #334; exit delay and start point set by owner in PR #335. Dependency on mw-e38.9 removed by owner decision 2026-11-08; that bead still owns the data-driven mapping. (2026-11-08)
area:audioclass:allepic:e28milestone:m3mw-e28.13Ambient beds and randomised positional one-shots
Description
Why
Dripping caves and rustling forests make exploration rewarding even when nothing is fighting you (The world rewards curiosity).
What
As a player, I want each place to have its own living soundscape so that quiet exploration is atmospheric.
Per-zone ambient bed loops (2 s crossfade) plus randomised positional one-shots (drips, wind gusts, distant birds, creaks) with min/max interval and spawn radius, defined in zone data and placed via e03-triggers.
Scope
- In: Ambience zone data, bed crossfades, one-shot scheduler
- Out: Ambience assets (asset group)
Notes
Integration note: Promoted to P1 — the MVP ambience beds (town, wilds, dungeon, river, harbour) are P1 and play through this runtime.
Acceptance criteria
- AC-1 unit Given a one-shot with interval 4–9 s, when 10 minutes elapse, then every gap falls within [4, 9] s.
- AC-2 integration Given the listener crossing between two ambience zones, when processed, then beds crossfade over 2 s with no gap in loudness greater than 3 dB.
- AC-3 unit Given one-shot spawn radius 10–25 m, when spawned, then each position lies within that annulus around the listener.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
Closed
Completed in PR #321 (2026-11-08)
area:audioclass:allepic:e28milestone:m3mw-e28.14Directional sound captions for important cues
Description
Why
Deaf and hard-of-hearing players need footsteps behind them and attack telegraphs as much as anyone (E31 accessibility).
What
As a player, I want captions for important sounds with a direction hint so that I do not miss audio-only information.
Cue-sheet rules may flag
caption: <stringId>; captions show text such as "[Footsteps, behind left]" with a direction arrow relative to the camera, only for sources within 20 m, de-duplicated per source for 2 s, max 4 visible. Toggle and text size via e31-settings-framework.Scope
- In: Caption flag in cue data, caption UI, direction computation, settings
- Out: Dialogue subtitles (dialogue group)
Notes
Initial flagged cues: footsteps (creatures), creature telegraphs, alert barks, noise-arrow rattle, door open/close, breaking objects.
Acceptance criteria
- AC-1 unit Given a flagged cue from a source 135° to the camera's left-rear, when captioned, then the direction label is "behind left".
- AC-2 unit Given the same source triggering 5 times in 1 s, when captioned, then only one caption shows.
- AC-3 unit Given a flagged source 25 m away, when it plays, then no caption appears.
- AC-4 unit Given 6 simultaneous captions, when rendered, then the 4 highest-priority show.
- AC-5 e2e Given captions enabled in settings, when a creature telegraph plays in the sandbox, then the caption appears within 100 ms.
Waits on
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #315 (2026-11-08)
area:uiclass:allepic:e28milestone:m3
E29VFX & Magical SpectacleMagic, impacts, destruction, stealth feedback and environmental effects establish the game's visual identity, derived from sim events and world properties, within the particle and frame budgets.12/19 done · 63%
M1 Grey-box slice 6
mw-e29.1Data-driven VFX framework with pooling and particle budgets
Description
Why
Impacts, magic and destruction establish the visual identity; one budgeted framework keeps spectacle within the performance baseline (E29 outcome).
What
Renderer-agnostic VFX interface (implemented in src/render/vfx after e00-engine-decision). Effect definitions as content data: emitters (rate/burst, lifetime, velocity cone, size/colour/alpha curves, texture/flipbook id, blend mode), sockets, duration/loop, quality tiers. Instanced particle rendering with pooling (no per-spawn allocations after warm-up). Budget manager (pure TS in src/game/vfx, unit-testable): global cap 4000 particles High / 1500 Low, per-effect priority, distance culling at 60 m, LOD tiers. API:
spawn(effectId, {position | entity+socket, rotation, scale, params}) → handle. Presentation RNG only — never the sim RNG.Scope
- In: Effect schema, runtime, pooling, budget manager, socket attachment, debug stats overlay
- Out: Event→effect mapping (e29-vfx-cue-sheets)
- Out: Specific effects (per-feature VFX items)
- Out: Final textures (asset group)
Notes
Render-specific code is covered by Playwright smoke + perf; budget/pool logic sits in src/game for ≥ 90% unit coverage.
Acceptance criteria
- AC-1 content Given an effect definition with a negative lifetime or unknown blend mode, when validated, then it fails with the path.
- AC-2 unit Given the High cap of 4000 and a request that would exceed it, when evaluated, then lower-priority effects are culled first and the new effect is admitted only if its priority is higher.
- AC-3 unit Given a pool warmed to 64 instances of an effect, when 64 spawn and expire repeatedly for 1000 cycles, then no new allocations occur (pool counter unchanged).
- AC-4 unit Given an effect 70 m from the camera, when spawned, then it is culled without consuming budget.
- AC-5 unit Given an unknown effect id, when spawned, then a null handle returns, dev builds show a magenta marker, and nothing throws.
- AC-6 e2e Given the greybox testbed in Chromium, when 20 test effects spawn, then they render with no console errors and a screenshot sanity check passes.
- AC-7 perf Given 50 simultaneous effects at the High cap, when measured, then frame time p95 ≤ 16.7 ms on the reference machine.
Waits on
- mw-e00.13 Spike: choose Three.js vs Babylon.js and physics engine (ADR-0001) done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
Closed
Completed in PR #92 (2026-09-29)
area:renderclass:allepic:e29milestone:m1mw-e29.2Procedural placeholder VFX textures keyed by final asset ids
Description
Why
Effects must be tunable before final art exists; placeholders under final ids make integration a swap.
What
A tool (
tools/vfx/gen-placeholders) generating soft circle, spark streak, smoke puff (noise), ring, shard and flame flipbook textures, saved under final asset ids withplaceholder: truemanifest flags, plus a report listing remaining placeholders.Scope
- In: Generator, initial texture set, manifest flags, report
- Out: Final VFX textures via GPT image prompts (asset group, e37)
Notes
Ids listed in the combat plan summary for the asset group.
Acceptance criteria
- AC-1 content Given all effect definitions, when validated, then every referenced texture id resolves to a manifest entry.
- AC-2 unit Given the generator with a fixed seed, when run twice, then outputs are byte-identical.
- AC-3 content Given the placeholder set, when measured, then total size ≤ 1.5 MB.
- AC-4 unit Given the report command, when run, then it lists all
placeholder: truetextures with a count.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
Closed
Completed in PR #141 (2026-10-02)
area:renderclass:allepic:e29milestone:m1mw-e29.3VFX cue sheets map sim events to effects
Description
Why
Effects should follow events from data, sharing audio's matcher so sound and visuals stay in sync.
What
A game-layer bridge reusing the generic rule matcher from e28-cue-sheets:
{event, match:{...}, effect, socket, orientTo:"hitNormal"|"attackerForward", scaleBy}. Effects attached to an entity detach and finish gracefully when it is destroyed (no orphaned loops).Scope
- In: VFX rule schema, bridge, attachment lifecycle, initial rules
- Out: Specific effects (per-feature items)
Notes
Acceptance criteria
- AC-1 unit Given generic and material-specific rules for
DamageApplied, when a metal-on-metal hit occurs, then the specific effect spawns oriented to the hit normal. - AC-2 unit Given a looping effect attached to an entity, when the entity is destroyed, then emission stops and existing particles finish within their lifetime.
- AC-3 integration Given a 60 s combat replay with VFX on and off, when compared, then sim state hashes are identical every tick.
- AC-4 content Given a VFX rule referencing an unknown effect id, when validated, then it warns (placeholder allowed) with the rule index.
Waits on
- mw-e28.3 Data-driven cue sheets map sim events to sounds done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
Closed
Completed in PR #141 (2026-10-02)
area:gameclass:allepic:e29milestone:m1mw-e29.4Impact VFX by material: sparks, splinters, bone dust, parry flash
Description
Why
Visible impacts confirm hits instantly and give combat its crunch; the stylised, gore-free look fits the warmer tone.
What
As a knight, I want every hit to burst with a material-appropriate effect so that I can see what connected and how hard.
Effects: metal sparks, wood splinters, bone chips/dust, stylised ichor puff (no gore), stone chips; block sparks/wood chips; parry flash (radial star + brief bloom boost, never culled); guard-break shatter ring; critical/riposte burst. Enemy hit flash (emissive white 80 ms). Oriented to hit normal, scaled by damage tier.
Scope
- In: Effect definitions, cue rules, hit flash material param
- Out: Final textures (asset group)
- Out: Screen shake (e29-screen-feedback)
Notes
Acceptance criteria
- AC-1 integration Given a sword hitting a metal-armored target, when
DamageAppliedfires, then the metal-sparks effect spawns at the contact point oriented to the hit normal. - AC-2 integration Given a parry at the particle cap, when it fires, then the parry flash still spawns (priority highest) and lower-priority particles are culled.
- AC-3 integration Given any damaged creature, when hit, then its emissive flash lasts 80 ms ± one frame.
- AC-4 unit Given a material pair with no specific rule, when resolved, then a generic impact puff spawns (never nothing).
- AC-5 manual Given the sandbox, when three playtesters fight, then each can say which hits were blocked versus clean from visuals alone.
Waits on
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e29.2 Procedural placeholder VFX textures keyed by final asset ids done
- mw-e29.3 VFX cue sheets map sim events to effects done
Closed
Completed in PR #222 (AC-5 playtest filed as mw-mma) (2026-10-04)
area:renderclass:allepic:e29milestone:m1mw-e29.5Camera shake, damage vignette and low-health effects
Description
Why
Screen feedback sells impact and danger, but must respect motion sensitivity and photosensitivity.
What
As a player, I want the camera to react to big impacts and the screen to warn me when I am hurt so that danger is felt — and I want to turn it down if it bothers me.
Trauma-based shake (trauma 0–1, offset ∝ trauma², decay 1.5/s; light hit 0.15, heavy 0.3, guard break 0.5, explosions distance-scaled). Shake scale slider 0–100% and reduce-motion toggle via e31-settings-framework. Damage vignette for 300 ms scaled by % health lost; desaturation below 25% health. No full-screen flash more than 3 times per second (WCAG 2.3.1).
Scope
- In: Shake model, vignette, low-health grade, settings hooks, flash limiter
- Out: Camera rig itself (e02-camera)
- Out: Hit-stop timing (e04-hit-stop)
Notes
Acceptance criteria
- AC-1 unit Given trauma 0.3 added, when 200 ms pass with no new trauma, then trauma is 0.0 (0.3 − 1.5 × 0.2).
- AC-2 unit Given shake scale 0% or reduce-motion on, when trauma is added, then camera offset is exactly 0.
- AC-3 unit Given 5 flash requests within 1 s, when processed, then at most 3 full-screen flashes render.
- AC-4 unit Given a hit removing 40% of health, when processed, then vignette intensity is 0.4 of max and fades by 300 ms.
- AC-5 manual Given A/B shake settings, when three playtesters fight for 3 minutes, then none report nausea at the default and all rate impact ≥ 4/5.
Waits on
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #314 (AC-5 manual playtest pending owner) (2026-11-08)
area:renderclass:allepic:e29milestone:m1mw-e29.16Decide the live-particle caps: 2000/800 (style bible) or 4000/1500 (mw-e29.1)
Description
Why
Two sources disagree on the global live-particle cap. The style bible §13.1 per-frame scene budget (which e32-perf-budgets enforces and other beads such as mw-e37.73 cite, e.g. ≤ 800 on Low) says High ≤ 2000 / Low ≤ 800. The mw-e29.1 description says 4000 High / 1500 Low.
What
mw-e29.1 shipped VFX_PARTICLE_CAPS = { high: 2000, low: 800 } (src/game/vfx/budget.ts), following the style bible, and tested the budget rule at the 4000 cap the bead's AC-2 names. The runtime measured about 0.25 ms per frame p99 at 4000 particles in Node and a 2.9 ms frame p95 with 50 effects at the 2000 cap on an M1 Pro (headed Chromium), so either number fits the frame. The owner picks one; the constant, the style bible table and perf-budgets.json (e32) must then agree.
Scope
- In: the decision, VFX_PARTICLE_CAPS, style bible §13.1, the vfx-test-stress effect and the ?vfx=stress count if the cap changes.
- Out: auto-degrade (mw-e29.15).
Acceptance criteria
- AC-1 content Given the chosen caps, when the style bible §13.1 table and VFX_PARTICLE_CAPS are compared, then they match.
- AC-2 perf Given ?vfx=stress at the chosen High cap, when measured on the reference machine, then frame time p95 ≤ 16.7 ms.
Closed
Owner decided 2026-09-29: keep style bible §13.1 caps, 2000 High / 800 Low (already shipped in VFX_PARTICLE_CAPS, PR #92). Revisit only with e32 reference-machine measurements. (2026-09-29)
area:renderclass:allepic:e29milestone:m1milestone:m3needs-human
M2 Class fantasies 6
mw-e29.6Weapon swing trails and charge build-up glow
Description
Why
Trails make arcs and reach readable — useful for learning timing — and add spectacle to heavy blows.
What
As a knight, I want to see the arc of my swings and my charge building so that reach and timing are readable.
Ribbon trails from weapon sockets rendered only during active frames (from sim phase), tinted by dominant damage type; charge build-up glow stepping at 33/66/100% charge with a burst on
ChargeReady; faint trails on focused arrows. Socket samples smoothed with Catmull-Rom; max 8 concurrent trails.Scope
- In: Trail renderer, phase binding, tint mapping, charge glow
- Out: Weapon models (art)
Notes
Acceptance criteria
- AC-1 integration Given a light attack, when it runs, then the trail is visible only on frames whose sim tick lies in the active window.
- AC-2 integration Given a fire-damage weapon, when swung, then the trail uses the fire tint from data.
- AC-3 unit Given 9 trail requests, when processed, then the oldest trail is released and 8 remain.
- AC-4 integration Given a charged heavy reaching full charge, when
ChargeReadyfires, then the burst effect spawns at the weapon socket.
Waits on
- mw-e04.6 Knight light attack chain and shield block with guard break done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
Closed
Completed in PR #326 (visual check of ribbons/glow still to do in playtest) (2026-11-08)
area:renderclass:knightepic:e29milestone:m2mw-e29.7Magical material library: dissolve, glow, distortion, runes
Description
Why
"Magic should feel magical." A shared shader library gives spells, summons and deaths a consistent, spectacular look.
What
As a designer, I want a library of magical materials so that spells, summons and deaths share a spectacular, consistent look.
Reusable materials/shaders: dissolve (death, summon, illusion fade), fresnel glow, heat-haze/distortion (High only; Low falls back to none), rune decal projection, emissive pulse, soft particles with depth fade. Parameterised for tint and intensity. Creature death uses dissolve by default (skeletons collapse then dissolve).
Scope
- In: Shader/material set, parameters, Low-preset fallbacks, creature death dissolve
- Out: Spell-specific art (sorcerer/asset groups)
Notes
Renderer-specific; covered by Playwright screenshot sanity + perf.
Acceptance criteria
- AC-1 e2e Given a material test scene, when each shader renders, then screenshot sanity checks pass and no shader compile errors are logged in Chromium, Firefox and WebKit.
- AC-2 integration Given the Low preset, when distortion is requested, then it is skipped and no distortion pass runs.
- AC-3 integration Given a creature
Diedevent, when processed, then the dissolve runs over 1.2 s and the render entity is removed afterwards. - AC-4 perf Given 6 simultaneous distortion effects on High, when measured, then frame time p95 ≤ 16.7 ms on the reference machine.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
Closed
Completed in PR #345. AC-4 measured on the reference class MacBook Pro M1 Pro in headed Chrome at 2560x1440: 6 hazes p95 4.1 ms (budget 16.7) with other jobs running. (2026-11-09)
area:renderclass:allepic:e29milestone:m2mw-e29.8Visuals derived from element states: burning, wet, frozen, shock
Description
Why
Systemic visuals mean any object that catches fire looks on fire without per-object authoring — the visual half of the property engine.
What
As a player, I want anything burning, wet, frozen or electrified to look it so that I can read the world's state at a glance.
The VFX layer observes e03 element states on any entity:
burning→ flame emitter scaled to bounds + smoke;wet→ darkened material + drips;frozen→ frost shader + ice crust;electrified→ arcs; transitions (e.g. burning→wet) → steam puff; gas volumes → tinted fog. Pure presentation — states come from the sim.Scope
- In: State observers, bounds-scaled emitters, material parameter blending, transition effects
- Out: Element rules (e03-element-propagation)
- Out: Light emission from fire (e03-light-field)
Notes
Acceptance criteria
- AC-1 integration Given any entity gaining
burning, when observed, then a flame emitter sized to its bounds attaches within one frame; losingburningstops it. - AC-2 integration Given an entity transitioning from
burningtowet, when observed, then a steam puff spawns once. - AC-3 integration Given 30 burning objects, when the budget is exceeded, then farthest flames drop to a low LOD before any are culled.
- AC-4 unit Given an entity with both
wetandfrozen, when material params resolve, then frozen takes precedence per the documented order. - AC-5 manual Given a greybox room with mixed element states, when three playtesters are asked what state each object is in, then they identify ≥ 90% correctly.
Waits on
- mw-e03.5 Ignite, burn, spread and extinguish fire from heat and properties done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
Closed
Completed in PR #364 (AC-5 manual playtest left to owner; look follow-up mw-wmu) (2026-11-09)
area:renderclass:allepic:e29milestone:m2mw-e29.10Spell VFX slots and templates for the spell pipeline
Description
Why
Every spell should look spectacular from day one by composing templates, before bespoke art (Magic should feel magical).
What
As a designer, I want to give a spell cast, projectile, impact, area and beam visuals by choosing templates and tints so that new spells look good without custom VFX code.
Standard slots consumed by the VFX layer:
castWindup,castRelease,projectile(head + trail),impact,area(ring/volume sized to spell radius),beam(start/end sockets, width),sustainOnTarget,summon/dissolve. Templates for each with tint/scale params. Interface documented in docs/vfx/spell-hooks.md for the magic group.Scope
- In: Slot definitions, templates, docs, a demo spell in the testbed
- Out: Spell data and effects logic (e06-spell-pipeline)
- Out: Signature spell art (sorcerer/asset groups)
Notes
Related: e06-spell-pipeline should depend on this item for visuals; not expressed here to avoid blocking VFX on the magic group.
Acceptance criteria
- AC-1 content Given a spell VFX block using templates only, when validated, then it passes and resolves every slot to an effect.
- AC-2 integration Given an area spell with radius 4 m, when it resolves, then the area template renders a ring 4 m in radius.
- AC-3 integration Given a beam spell, when its caster moves, then the beam start follows the cast socket every frame.
- AC-4 unit Given a spell VFX block missing an optional slot, when cast, then that slot is skipped without error.
Waits on
- mw-e29.3 VFX cue sheets map sim events to effects done
- mw-e29.7 Magical material library: dissolve, glow, distortion, runes done
Closed
Completed in PR #366 (2026-11-09)
area:renderclass:sorcererepic:e29milestone:m2mw-e29.11Arrow VFX: trails, payload bursts, stuck and broken arrows
Description
Why
Trick arrows need readable visuals — a splash, an unfurling rope, a rattle ring — so players understand what each did.
What
As an archer, I want each arrow type to show clearly what it did so that trick arrows are readable.
Arrow trail (brighter for focused shots), flame on fire-arrow shafts, water burst + wet decal, rope unfurl ribbon, subtle ring pulse for noise arrows, dust for blunt impacts, deflect sparks, stuck-arrow wobble on impact, broken-arrow mesh swap when not retrievable.
Scope
- In: Effects and cue rules for arrow events
- Out: Arrow models (asset group)
Notes
Acceptance criteria
- AC-1 integration Given a water arrow impact, when processed, then the burst spawns and a wet decal appears matching the 1.5 m radius.
- AC-2 integration Given an arrow that sticks intact, when it impacts, then it wobbles for 400 ms; a broken one shows the broken mesh.
- AC-3 integration Given a deflected arrow, when processed, then deflect sparks spawn at the contact point.
- AC-4 unit Given a focused shot, when its trail spawns, then the focused trail variant is used.
Waits on
- mw-e05.2 Deterministic arrow ballistics with gravity, drag and sticking done
- mw-e29.3 VFX cue sheets map sim events to effects done
Closed
Completed in PR #348. AC-4 covered at unit level; focused shots plug in with mw-e05.4. Look follow-up mw-3j7. (2026-11-09)
area:renderclass:archerepic:e29milestone:m2mw-e29.12World-space awareness indicators and optional noise ripples
Description
Why
Stealth must be readable: players need to see who is suspicious and how noisy they were, without an all-knowing eye icon.
What
As a thief, I want to see how aware each creature is and, optionally, how far my noise carried so that I can make informed stealth decisions.
World-space awareness indicator above creatures reflecting e11 alert states (Unaware hidden → Suspicious fill → Investigating → Searching → Alerted → Combat), shape-coded as well as colour-coded for colour-blind players; optional last-known-position ghost marker (assist); optional noise ripple visualisation from e09 noise events (assist, off by default); extinguish puff when lights go out.
Scope
- In: Indicators, assist visuals, colour-blind-safe shapes
- Out: Alert-state logic (e11-alert-states)
- Out: Player visibility meter HUD (stealth group)
Notes
Related: coordinate with the stealth group's HUD so indicators and meters share a visual language.
Acceptance criteria
- AC-1 integration Given a creature entering Suspicious with suspicion 0.4, when rendered, then its indicator shows the Suspicious shape filled 40%.
- AC-2 integration Given a creature in Unaware state, when rendered, then no indicator is visible.
- AC-3 unit Given colour-blind simulation (deuteranopia filter), when states are compared, then every state remains distinguishable by shape alone.
- AC-4 unit Given the noise ripple assist off (default), when a noise event occurs, then no ripple renders.
Waits on
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e11.7 Six-state alert machine: Unaware to Combat with timed decay done
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
Closed
Completed in PR #349. Ripple visibility and noise sources tracked by mw-ye1 and mw-e09.5. (2026-11-09)
area:renderclass:thiefepic:e29milestone:m2
E30Save/LoadBrowser-safe saves reliably preserve character and world state across sessions, browser restarts and game updates; a corrupted or interrupted save never costs the player all their progress.9/15 done · 60%
M1 Grey-box slice 8
mw-e30.1Define versioned save format with section registry and migrations
Description
Why
Save-game stability is a constitution technical priority; one versioned format with a migration chain prevents "your save is incompatible" after every update.
What
Add
src/game/save/format(100% coverage layer): save envelope{formatVersion, saveSchemaVersion, gameVersion, buildSha, contentHash, createdAtTick, wallClockSavedAt (injected), metadata, sections: Record<sectionId, {version, data}>, checksum}. Systems register a section (registerSaveSection({id, version, schema, serialize(world), deserialize(world, data), migrations})). Serialization reuses the canonical encoder from e00-sim-state-hash; checksum over sections. Migration runner applies per-section migrations in order (vN→vN+1) before zod validation.Scope
- In: Envelope schema (zod), section registry, serialize/deserialize whole world
- In: Per-section migration chain + runner
- In: Checksum verification
- In: Round-trip property test: random world → save → load → identical state hash
- Out: Persistence to IndexedDB (e30-indexeddb-storage)
- Out: Fixture-based migration CI harness (e30-migration-fixtures)
- Out: World facts content (e27-world-state-store registers its own section)
Notes
- Sections allow e17 inventory, e27 world state, e19 progression, e23 quests to own their data without a central god-schema.
- Unknown sections from a newer build are preserved verbatim on re-save (forward-compat) but flagged.
Acceptance criteria
- AC-1 unit Given a world with 3 registered sections, when saved and loaded into a fresh world, then the sim state hash equals the original.
- AC-2 unit Given a section at data version 1 and code at version 3, when loaded, then migrations 1→2 and 2→3 run in order and the result validates against the v3 schema.
- AC-3 unit Given a save whose checksum does not match its sections, when loaded, then a typed
SaveCorruptErroris returned and the world is untouched. - AC-4 unit Given a save containing a section id not registered in this build, when loaded, then load succeeds, a warning lists the unknown section, and re-saving preserves it verbatim.
- AC-5 unit Given a missing migration step (v1 data, only 2→3 registered), when loaded, then a typed
MissingMigrationErrornames section and versions. - AC-6 unit Given
saveSchemaVersionnewer than the running build supports, when loaded, then it refuses withSaveFromNewerBuildErrorrather than attempting to parse.
Waits on
- mw-e00.16 Add canonical sim state serialization and hashing done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
Closed
Completed in PR #44 (2026-09-28)
area:simepic:e30milestone:m1mw-e30.2Persist saves to IndexedDB with atomic writes and fallbacks
Description
Why
Browser storage is fragile (quota, private mode, eviction); saves must survive or fail loudly, never silently.
What
Add
src/game/save/storage: aSaveStoreinterface with an IndexedDB implementation (dbmw-saves, storesslots,blobs) using single-transaction atomic writes (write new blob + update slot pointer in one transaction, keep previous blob as backup),navigator.storage.persist()request on first save, quota estimate, and an in-memory fallback when IndexedDB is unavailable with a visible "saves will not persist" warning.Scope
- In: IndexedDB store (idb wrapper or hand-rolled), in-memory store, shared contract tests run against both (fake-indexeddb in Node)
- In: Quota and persist-permission handling
- Out: Slot UX (e30-save-slots)
- Out: Cross-tab locking (e30-multi-tab-guard)
Notes
- None.
Acceptance criteria
- AC-1 integration Given the IndexedDB store, when a save is written then read back, then bytes are identical (contract test runs against both implementations).
- AC-2 integration Given a transaction aborted mid-write (simulated), when the slot is read, then the previous save is returned intact.
- AC-3 integration Given a QuotaExceededError on write, when saving, then the error surfaces as a typed
SaveQuotaError, the previous save remains, and the UI can show "free up space". - AC-4 e2e Given IndexedDB throws on open (private-mode simulation), when the game starts, then the in-memory store is used and a persistent warning banner is shown.
- AC-5 integration Given
navigator.storage.persistis unavailable, when first save occurs, then saving proceeds without error.
Waits on
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #47 (2026-09-28)
area:gameepic:e30milestone:m1mw-e30.3Gate save-schema changes on committed migration fixtures in CI
Description
Why
Contract §3: versioned migration tests for every save-schema change; forgetting a migration must be impossible, not merely discouraged.
What
Commit
test/save-fixtures/<saveSchemaVersion>/*.jsongenerated from representative worlds. A CI test loads every fixture through the current migration chain and asserts it validates and loads. A schema fingerprint (hash of each section's JSON Schema export) is committed insave-schema.lock.json; if a section schema changes without bumping its version and adding a migration + fixture, CI fails with instructions.pnpm save:fixturegenerates a fixture for the current version.Scope
- In: Fixture loader test, schema fingerprint lock + check, fixture generator script
- Out: Runtime migration logic (e30-save-serialization)
Notes
- None.
Acceptance criteria
- AC-1 integration Given fixtures for versions 1..N, when CI runs, then each loads under the current build and yields a world passing invariant checks.
- AC-2 unit Given a section schema field added without bumping the section version, when the lock check runs, then it fails naming the section and telling the developer to bump + add migration + fixture.
- AC-3 unit Given a version bump without a new fixture directory, when the check runs, then it fails.
- AC-4 unit Given a fixture that fails to load, when CI runs, then the failure names fixture path and the migration step that threw.
Waits on
- mw-e30.1 Define versioned save format with section registry and migrations done
Closed
Completed in PR #55 (2026-09-28)
area:qaepic:e30milestone:m1mw-e30.4Support manual save slots with metadata and thumbnails
Description
Why
Players experiment ("what if I anger the minotaur?"); multiple slots make experimentation safe.
What
As a player, I want several named save slots showing class, location, playtime and a thumbnail so that I can keep parallel playthroughs.
Slot model: 10 manual slots + autosave ring + quicksave; metadata (character name, class, area name, playtime ticks, saved-at, game version, 256×144 WebP thumbnail captured from the canvas). Operations: save, load, overwrite (confirm), delete (confirm), rename.
Scope
- In: Slot manager in game layer
- In: Thumbnail capture (render-side, excluded-coverage helper)
- Out: Menu UI screens (e30-save-load-ui)
Notes
- None.
Integration note: Promoted to P0/m1 — required by the E01 grey-box vertical slice.
Acceptance criteria
- AC-1 unit Given 10 filled manual slots, when the player attempts an 11th new save, then the slot manager requires choosing an overwrite target.
- AC-2 unit Given a slot saved with class knight in area "testbed-arena" after 3,600 ticks, when metadata is read, then it reports those values and playtime 60 s.
- AC-3 integration Given a thumbnail capture fails (canvas tainted or context lost), when saving, then the save still succeeds with a placeholder thumbnail.
- AC-4 unit Given a slot deletion, when confirmed, then the slot and its backup blob are both removed.
Waits on
- mw-e30.2 Persist saves to IndexedDB with atomic writes and fallbacks done
Closed
Completed in PR #60 (2026-09-28)
area:gameepic:e30milestone:m1mw-e30.5Autosave on area transitions and checkpoints into a rotating ring
Description
Why
Failure should create stories, not lost hours; autosaves keep risk fun rather than punishing.
What
As a player, I want the game to autosave at safe moments so that a crash or death never costs much progress.
Autosave triggers: area transition, checkpoint trigger volumes, after quest state changes, and every 5 minutes of playtime when safe. Never while in combat/alerted state, mid-air, or during dialogue — deferred until safe. Ring of 3 autosave slots, oldest overwritten. Unobtrusive HUD indicator.
Scope
- In: Autosave scheduler with "safe to save" predicate supplied by systems
- In: Ring buffer slots
- In: HUD indicator hook
- Out: Trigger volume implementation (e03-triggers)
- Out: Alert/combat state source (e11-alert-states)
Notes
- Safe predicate is a registry so combat/stealth/dialogue can veto without save code knowing them.
Acceptance criteria
- AC-1 unit Given 3 autosaves exist, when a 4th autosave occurs, then the oldest is replaced and the other two remain.
- AC-2 unit Given an autosave trigger fires while a registered veto (combat) is active, when the veto clears 20 s later, then exactly one autosave occurs at that point.
- AC-3 unit Given playtime passes 5 minutes with no other autosave, when safe, then a timed autosave occurs; given another autosave happened 1 minute ago, then the timer resets.
- AC-4 integration Given an autosave write fails, when the next safe moment arrives, then it retries once and surfaces a non-blocking HUD warning on second failure.
- AC-5 e2e Given the testbed with a checkpoint volume, when the player enters it, then an autosave appears in the slot list with the area name.
Waits on
- mw-e03.21 Wire trigger volumes and mechanisms with data-defined signal graphs done
- mw-e30.2 Persist saves to IndexedDB with atomic writes and fallbacks done
Closed
Completed in PR #70 (2026-09-28)
area:gameepic:e30milestone:m1mw-e30.7On death, offer reload of latest save or checkpoint
Description
Why
The m1 loop requires die → reload; it must be fast so failure invites another try.
What
As a player, I want a death screen offering "Load last save" (most recent of any slot type) and "Load…" so that I return to play within seconds.
Death screen appears after the death sequence; reload completes to playable in ≤ 3 s warm.
Scope
- In: Death screen flow, most-recent-save resolution, full world teardown/rebuild on load
- Out: Death conditions and animation (e04-damage-model and E02)
Notes
- None.
Acceptance criteria
- AC-1 unit Given an autosave at t=100 and a manual save at t=120, when resolving "last save", then the manual save is chosen.
- AC-2 e2e Given the player dies in the testbed, when "Load last save" is chosen, then the game is playable in ≤ 3 s and the sim state hash equals the save's state hash.
- AC-3 e2e Given no saves exist at death, when the death screen shows, then it offers "Restart area" instead and does not error.
- AC-4 integration Given load of a corrupt latest save, when chosen from death screen, then the recovery flow (e30-corruption-recovery) offers the backup.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e04.1 Deterministic damage model: damage types, resistances, poise, death done
- mw-e30.5 Autosave on area transitions and checkpoints into a rotating ring done
Closed
Completed in PR #136 (2026-10-02)
area:gameepic:e30milestone:m1mw-e30.11Build Continue / Load / Save menu screens
Description
Why
The m1 loop needs saves reachable from the title screen and pause menu.
What
As a player, I want Continue, Load and Save screens so that I can resume and manage my saves.
Title: Continue (most recent), New Game, Load. Pause: Save, Load. Slot list shows thumbnail + metadata; confirmation dialogs for overwrite/delete; keyboard and gamepad navigable.
Scope
- In: Title and pause menu save screens
- In: Empty/error states
- Out: Settings screens (e31-settings-framework)
Notes
- None.
Integration note: Promoted to P0/m1 — required by the E01 grey-box vertical slice.
Acceptance criteria
- AC-1 e2e Given one or more saves, when the title screen shows, then Continue is focused by default and loads the most recent save.
- AC-2 e2e Given no saves, when the title screen shows, then Continue is disabled with an accessible "no saves yet" reason and New Game is focused (reconciled with mw-e01.2 AC-2 and the slice spec, 2026-10-02).
- AC-3 e2e Given the slot list, when navigated by keyboard only (arrows/Enter/Esc), then every action (load, save, overwrite confirm, delete confirm) is reachable.
- AC-4 integration Given the storage in in-memory fallback mode, when the save screen opens, then the non-persistence warning is visible.
Waits on
- mw-e30.4 Support manual save slots with metadata and thumbnails done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #177 (2026-10-03)
area:uiepic:e30milestone:m1mw-e30.14Save slot description uses the player's real class, not a hardcoded Knight
Description
Why
Save slot metadata (describe) records the character as Knight regardless of class; class selection (PR #163) now sets player.class. Seen while building save menus (PR #177).
What
describe() reads player.class (and its display name) for the slot title/subtitle.
Acceptance criteria
AC-1 unit A save made as a thief shows Thief in its slot description.
Closed
Completed in PR #191 (2026-10-03)
area:gamearea:simepic:e30milestone:m1
M3 MVP content 1
mw-e30.8Recover from corrupt saves using backups and clear messaging
Description
Why
Nothing kills goodwill faster than a lost 10-hour save; recovery must be automatic and honest.
What
As a player, I want the game to fall back to a backup when a save is damaged so that I never lose everything.
On
SaveCorruptError/migration failure: try the slot's backup blob, then the next most recent save; show what was restored and its age; offer to export the corrupt file for a bug report.Scope
- In: Recovery chain, messaging, corrupt-file export
- Out: General export/import (e30-export-import)
Notes
- None.
Acceptance criteria
- AC-1 integration Given a slot whose primary blob fails checksum and whose backup is valid, when loaded, then the backup loads and the UI states "restored backup from <time>".
- AC-2 integration Given primary and backup both corrupt, when loaded, then the next most recent valid save across slots is offered.
- AC-3 integration Given all saves corrupt, when loading, then the player can start a new game and export the corrupt files; the game does not crash.
- AC-4 unit Given a migration that throws, when loading, then it is treated like corruption (recovery chain) and the error is logged to local telemetry.
Waits on
- mw-e30.4 Support manual save slots with metadata and thumbnails done
Closed
Completed in PR #65 (2026-09-28)
area:gameepic:e30milestone:m3
E31Accessibility & SettingsPlayers can tailor controls, text, camera, audio, visuals and difficulty so the game is comfortable and playable for a broad audience, meeting the basic tier of the Game Accessibility Guidelines.13/17 done · 76%
M1 Grey-box slice 2
mw-e31.1Create typed settings store and options menu skeleton
Description
Why
Every accessibility and comfort feature needs one place to live, persist and notify systems of change.
What
Add
src/game/settings: a zod-typed settings schema with defaults and categories (controls, camera, display, audio, accessibility, gameplay/difficulty), versioned persistence in localStorage (small, sync) with migration, change subscriptions (settings.on("camera.invertY", cb)), and "apply immediately" semantics. Options UI skeleton: tabbed categories, generic controls (toggle, slider, select, keybind placeholder), reset category to defaults, keyboard/gamepad navigable.Scope
- In: Schema, defaults, persistence + version migration, subscriptions
- In: Options menu shell with generic controls generated from schema metadata (label, help text, range)
- Out: Individual settings features (other e31 items)
- Out: Graphics presets implementation (e32-quality-presets)
Notes
- Settings are per-browser, not per-save. Difficulty assists are settings too but read by sim via an injected config (never global reads from sim).
Integration note: Promoted to P0 — input actions, HUD vitals and inventory UI (all P0/m1) depend on it.
Acceptance criteria
- AC-1 unit Given stored settings with an unknown key and an out-of-range value, when loaded, then the unknown key is dropped, the value is clamped/defaulted and a warning is logged.
- AC-2 unit Given a subscriber to
audio.master, when the value changes, then the callback fires once with the new value; unchanged sets do not fire. - AC-3 unit Given stored settings at schema v1 and code at v2, when loaded, then the migration maps renamed keys and preserves user values.
- AC-4 e2e Given the options menu, when a toggle is changed and the page reloaded, then the change persists.
- AC-5 e2e Given "Reset to defaults" in Camera, when confirmed, then only camera settings reset.
- AC-6 integration Given localStorage is unavailable, when the game starts, then defaults are used in-memory and no uncaught error occurs.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
Closed
Completed in PR #161 (2026-10-03)
area:uiepic:e31milestone:m1mw-e31.14Inject a DifficultyConfig of tunable multipliers into the sim
Description
Why
Combat and stealth rules (P0/m1) must read difficulty/assist multipliers from day one, but the assists UI (e31-difficulty-assists) lands at m3. A tiny config seam avoids a milestone inversion and keeps assists data-driven.
What
As a developer, I want a typed, immutable DifficultyConfig injected into the sim at world creation so that any rule can read multipliers (damageTaken, damageDealt, detectionSpeed, parryWindow, dodgeWindow, fallDamage, puzzleHints) without knowing about settings or UI.
Scope
- In: schema with defaults (all 1.0), validation (ranges), injection into sim context, inclusion in state hash + save, change-at-runtime event.
- Out: the assists UI and presets (e31-difficulty-assists), per-system consumption ACs (owned by each consumer).
Notes
Consumers: e04-damage-model, e04-parry-riposte, e04-dodge-iframes, e04-environmental-damage, e11-awareness-accumulation, e15 hints.
Acceptance criteria
- AC-1 unit Given no overrides, when a world is created, then every multiplier equals 1.0 and the config is frozen.
- AC-2 unit Given a multiplier outside its allowed range (e.g. detectionSpeed 0 or 10), when the config is validated, then validation fails with the field name and range.
- AC-3 unit Given two worlds differing only in damageTaken, when their state is hashed, then the hashes differ; given identical configs, they match.
- AC-4 integration Given a save made with detectionSpeed 0.5, when it is loaded, then the restored sim reads 0.5.
- AC-5 unit Given a runtime change event, when applied between ticks, then the new value takes effect on the next tick and is recorded in the replay log so replays stay identical.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e00.16 Add canonical sim state serialization and hashing done
- mw-e00.18 Load schema-validated content with a per-entry test harness done
Closed
Completed in PR #45 (2026-09-28)
area:simepic:e31milestone:m1
M3 MVP content 11
mw-e31.2Let players remap keyboard, mouse and gamepad bindings
Description
Why
Remapping is the single most-requested accessibility feature and a baseline expectation for PC action games.
What
As a player, I want to rebind every action for keyboard/mouse and gamepad so that controls fit my hands and hardware.
Bindings screen lists actions from the action map with primary/secondary bindings per device; "press a key" capture with Esc to cancel; conflict detection with swap/replace prompt; reset per action and all; bindings persisted in settings.
Scope
- In: Bindings UI over e02-input-actions API
- In: Conflict detection within the same context (e.g. exploration vs menu)
- Out: The action map itself (e02-input-actions)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given "Dodge" bound to Space, when rebound to Shift, then pressing Shift in the testbed triggers dodge and Space no longer does.
- AC-2 unit Given a new binding that conflicts with another action in the same context, when applied, then the user is prompted to swap or cancel and no silent duplicate exists.
- AC-3 unit Given an attempt to unbind the last binding for a required action (Pause/Menu), when applied, then it is refused with an explanation.
- AC-4 e2e Given capture mode, when Esc is pressed, then capture cancels without binding Esc.
- AC-5 e2e Given remapped bindings, when the page reloads, then bindings persist.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #317 (2026-11-08)
area:uiepic:e31milestone:m3mw-e31.3Offer hold vs toggle for sprint, crouch, block and aim
Description
Why
Sustained key holds are a common motor-accessibility barrier; stealth and knight play involve long holds.
What
As a player, I want to choose hold or toggle for sustained actions so that I do not need to keep keys pressed.
Per-action setting for sprint, crouch/sneak, block, aim/draw; toggle mode auto-releases on context change (e.g. entering dialogue).
Scope
- In: Setting + input-layer adapter that converts toggle into a held state
- Out: Action definitions (e02-input-actions)
Notes
- None.
Acceptance criteria
- AC-1 unit Given crouch in toggle mode, when the key is pressed and released once, then the crouch command stays active until pressed again.
- AC-2 unit Given block toggled on, when dialogue opens, then block is released and does not resume after dialogue.
- AC-3 e2e Given the setting changed mid-hold, when applied, then the action state resets to released without getting stuck.
Waits on
- mw-e02.1 Map remappable KB/M input to deterministic per-tick action frames done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #377 (2026-11-10)
area:gameepic:e31milestone:m3mw-e31.4Scale UI text 80–200% with readable defaults
Description
Why
Books are central to the sorcerer fantasy and dialogue is text-only; unreadable text excludes players.
What
As a player, I want to scale UI and book/dialogue text so that I can read comfortably at my screen distance.
Global UI scale and separate text size (80–200%), minimum default body size 18 px at 1080p, optional dyslexia-friendly font toggle, text background opacity for subtitles/dialogue.
Scope
- In: CSS variable-driven scaling (or engine UI equivalent)
- In: Layout reflow at 200% without clipping
- Out: Captions content (e31-captions)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given text size 200%, when the dialogue, book reader and inventory screens are opened at 1280×720, then no text is clipped (Playwright overflow check on text nodes).
- AC-2 unit Given a stored value of 350%, when loaded, then it is clamped to 200%.
- AC-3 e2e Given the dyslexia-friendly font toggled, when menus render, then the font family changes without reload.
- AC-4 manual Given default settings at 1080p on the reference machine, when a tester reads dialogue from 70 cm, then it is legible (body text ≥ 18 px).
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #378 (AC-4 manual 1080p read left to owner) (2026-11-10)
area:uiepic:e31milestone:m3mw-e31.5Caption gameplay-critical sounds with direction indicators
Description
Why
Stealth and combat depend on sound (footsteps, alerts, creature cues); deaf and hard-of-hearing players need that information visually.
What
As a player, I want captions for important sounds with direction so that I can react to threats I cannot hear.
Sound events tagged
caption(content data: text key, priority) display as short captions with an on-screen direction arrow relative to the camera; stacking/merging rules; size/background from text settings; toggle on/off.Scope
- In: Caption event pipeline from audio/sim noise events
- In: Direction indicator
- In: Content field
captionon sound definitions - Out: Audio engine (e28-audio-engine)
- Out: Noise event model (e09-sound-propagation)
Notes
- None.
Acceptance criteria
- AC-1 unit Given a captioned noise event 90° to the player's right, when displayed, then the indicator points right (±15°).
- AC-2 unit Given 5 identical "footsteps" events within 2 s, when displayed, then they merge into one caption with a refreshed timer rather than 5 lines.
- AC-3 unit Given more than 4 simultaneous captions, when displayed, then the lowest-priority captions are dropped first.
- AC-4 content Given every sound definition flagged gameplay-critical, when validated, then it has a caption text key that exists in the strings table.
- AC-5 e2e Given captions enabled in the testbed, when a scripted guard alert sound plays, then its caption appears within 100 ms.
Waits on
- mw-e09.3 Propagate noise events through rooms, doors and walls with occlusion done
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e28.14 Directional sound captions for important cues done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #385 (2026-11-10)
area:uiepic:e31milestone:m3mw-e31.6Add camera sensitivity, invert, FOV and auto-centre options
Description
Why
Camera discomfort drives players away from third-person action games; control over it is basic comfort.
What
As a player, I want to adjust camera sensitivity, inversion, FOV, distance and auto-centering so that the camera feels right to me.
Settings: mouse and stick sensitivity (separate X/Y), invert X/Y, FOV 60–100°, camera distance, auto-recentre on/off, lock-on switch sensitivity.
Scope
- In: Settings wired to camera parameters via subscriptions
- Out: Camera implementation (e02-camera)
- Out: Shake/motion (e31-motion-comfort)
Notes
- None.
Acceptance criteria
- AC-1 unit Given invert Y enabled, when mouse moves up by 100 px, then camera pitch changes in the opposite direction to the default.
- AC-2 unit Given FOV set to 120 via storage tampering, when loaded, then it clamps to 100.
- AC-3 e2e Given sensitivity changed in the options menu, when returning to play, then the new sensitivity applies without reload.
Waits on
- mw-e02.4 Follow the player with a colliding, orbiting third-person camera done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #379 (2026-11-10)
area:gameepic:e31milestone:m3mw-e31.7Add motion comfort and photosensitivity safeguards
Description
Why
Spectacular magic (constitution: magic should feel magical) must not trigger motion sickness or photosensitive seizures.
What
As a player, I want to reduce camera shake, motion blur, head bob and flashes so that the game is comfortable and safe.
Settings: camera shake 0–100%, motion blur off, reduce flashing (caps full-screen luminance change frequency to ≤ 3 flashes/s and limits intensity), reduce screen effects (vignette/chromatic aberration). Honour
prefers-reduced-motionas initial default.Scope
- In: Settings + a global "flash limiter" utility VFX must route through
- In: Default from prefers-reduced-motion
- Out: VFX system (e29-vfx-framework)
Notes
- None.
Acceptance criteria
- AC-1 unit Given reduce-flashing on, when VFX request 10 full-screen flashes within 1 s, then the limiter lets at most 3 through with intensity capped.
- AC-2 unit Given camera shake at 0%, when an impact requests shake, then camera offset remains zero.
- AC-3 e2e Given
prefers-reduced-motion: reduceemulated on first launch, when settings initialise, then shake defaults to 0% and reduce-flashing to on. - AC-4 manual Given the spell/VFX showcase with reduce-flashing on, when reviewed against the Harding test guidance, then no sequence exceeds the flash threshold.
Waits on
- mw-e29.1 Data-driven VFX framework with pooling and particle budgets done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #380 (AC-4 manual Harding review left) (2026-11-10)
area:renderepic:e31milestone:m3mw-e31.8Ensure no gameplay information relies on colour alone
Description
Why
Stealth awareness states, damage types and puzzle cues must be readable by colour-blind players (~8% of men).
What
As a player, I want gameplay cues distinguishable without colour so that colour-blindness does not block me.
Audit and fix HUD/indicators to pair colour with shape/icon/pattern (alert states, element types, interactables), plus optional palette presets (deuteranopia, protanopia, tritanopia) for UI indicator colours.
Scope
- In: UI indicator palette tokens + presets
- In: Design rule in docs/art/style-bible.md cross-reference
- Out: Full-screen colour filters (not planned; they degrade art)
Notes
- None.
Acceptance criteria
- AC-1 unit Given each awareness state indicator (Unaware→Combat), when rendered, then each has a distinct icon/shape independent of colour.
- AC-2 e2e Given each palette preset, when the HUD test page renders all indicators, then adjacent indicator colours differ by a contrast ratio ≥ 3:1 under simulated deficiency.
- AC-3 manual Given a colour-blind tester or simulator run through the testbed, when playing a stealth encounter, then all state changes are identified correctly.
Waits on
- mw-e31.1 Create typed settings store and options menu skeleton done
- mw-e37.1 Review and sign off the visual style bible and GPT style preamble done
Closed
Completed in PR #384 (AC-3 colour-blind tester left to owner) (2026-11-10)
area:uiepic:e31milestone:m3mw-e31.9Add per-bus volume sliders and mono audio
Description
Why
Players must balance music against gameplay-critical sound; single-sided hearing requires mono.
What
As a player, I want master/music/SFX/UI/ambience volume sliders and a mono toggle so that I can hear what matters.
Sliders map to audio engine buses (perceptual/log curve); mono downmix; mute when tab unfocused (optional).
Scope
- In: Settings wired to e28 bus API
- Out: Mixer implementation (e28-audio-engine)
Notes
- None.
Acceptance criteria
- AC-1 unit Given music slider at 0, when the music bus gain is read, then it is 0 (silent) and SFX unaffected.
- AC-2 unit Given slider value 50%, when mapped, then gain follows the documented perceptual curve (≈ −10 dB ±1).
- AC-3 integration Given mono enabled, when a sound positioned hard left plays, then both output channels carry equal signal.
Waits on
- mw-e28.1 Web Audio engine: registry, buses, voice pool, positional audio done
- mw-e28.5 Per-bus volume settings, tab-hidden mute and mono option done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #382 (2026-11-10)
area:audioepic:e31milestone:m3mw-e31.10Provide difficulty presets and granular assists read by the sim
Description
Why
Souls-like weight should not mean exclusion; granular assists let players keep the fantasy while tuning the challenge.
What
As a player, I want difficulty presets and individual assists so that combat and stealth challenge suit me.
A sim-side
DifficultyConfig(injected, part of save metadata): damage taken multiplier, parry/dodge timing window multiplier, enemy detection speed multiplier, stealth last-known-position search duration, puzzle hint availability, fall damage on/off. Presets Story/Adventurer/Veteran map to values; custom allowed. Changing mid-game allowed and recorded in local telemetry.Scope
- In: DifficultyConfig schema + injection into World
- In: Settings UI section
- In: Sim helper
difficulty.scale("damageTaken", x)for systems to use - Out: Systems applying the multipliers (each combat/stealth bead reads the config)
Notes
- Related: e04 and e09 systems must read these multipliers; integration pass should add ACs there.
Acceptance criteria
- AC-1 unit Given preset Story, when resolved, then damageTaken=0.5, parryWindow=1.5, detectionSpeed=0.6 per the documented table.
- AC-2 unit Given damageTaken multiplier 0.5, when a 20-damage hit is scaled, then 10 damage results (integer rounding rule documented and tested).
- AC-3 unit Given a config value outside allowed bounds (e.g. parryWindow 10), when injected, then it is rejected by schema validation.
- AC-4 unit Given difficulty changed mid-session, when saved and reloaded, then the new config is restored and the change event was emitted once.
- AC-5 integration Given two replays identical except difficulty preset, when run, then state hashes differ (config is an input) and each replays deterministically.
Waits on
- mw-e00.15 Build deterministic fixed-step sim loop, entity store and event bus done
- mw-e31.1 Create typed settings store and options menu skeleton done
- mw-e31.14 Inject a DifficultyConfig of tunable multipliers into the sim done
Closed
Completed in PR #387 (Adventurer/Veteran numbers placeholders for owner tuning) (2026-11-10)
area:simepic:e31milestone:m3mw-e31.11Make every menu keyboard- and gamepad-operable with visible focus
Description
Why
Menus that require a mouse block keyboard-only and gamepad players, and screen readers need labelled controls.
What
As a player, I want to navigate all menus with keyboard or gamepad and see where focus is so that I never need a mouse.
Shared focus manager (spatial navigation), visible focus ring meeting 3:1 contrast, Esc/B back, ARIA labels/roles on DOM menus, axe-core checks in Playwright.
Scope
- In: Focus manager, gamepad UI navigation, ARIA roles
- In: Playwright axe scan of each menu screen
- Out: In-game HUD screen reader support (post-MVP)
Notes
- None.
Acceptance criteria
- AC-1 e2e Given each menu screen, when traversed with Tab/arrow keys only, then every interactive element is reachable and focus is always visible.
- AC-2 e2e Given each menu screen, when axe-core runs, then there are zero serious or critical violations.
- AC-3 e2e Given an emulated gamepad, when D-pad/A/B are used, then the same navigation works and B always returns to the previous screen.
- AC-4 e2e Given a modal dialog open, when Tab reaches its last control, then focus wraps inside the modal (focus trap).
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e31.1 Create typed settings store and options menu skeleton done
Closed
Completed in PR #386 (2026-11-10)
area:uiepic:e31milestone:m3mw-e31.15Inventory item cards fail axe 4.14 label-content-name-mismatch
Description
axe-core 4.14 (Dependabot PR #249) flags inventory option cards: visible text (name + E/slot/count badge text) is not contained in aria-label. Render badge/count glyphs from data-glyph via CSS so visible DOM text equals the name.
Acceptance criteria
Inventory e2e axe test passes on @axe-core/playwright 4.13 and 4.14; unit test names the AC
Closed
Completed in PR #259 (2026-10-24)
area:uiepic:e31milestone:m3
E32Performance & Browser CompatibilityStable frame pacing and fast loading on the hardware baseline across current desktop Chrome, Edge, Firefox and Safari, protected by automated budgets so regressions are caught in PRs rather than playtests.4/19 done · 21%
M0 Foundation 2
mw-e32.1Build automated perf budget suite against the hardware baseline
Description
Why
Performance regressions are cheapest to fix the day they land; budgets make "good browser performance" (constitution §10) enforceable.
What
A Playwright-based perf suite (
pnpm perf) withperf-budgets.jsonciting the contract baseline: frame time p50/p95 per scene and preset, time to first frame / playable, initial transfer size, JS heap after N minutes, long tasks count. Two execution modes:- CI mode (GPU-less runners): enforces deterministic-ish budgets — transfer size, bundle size, heap, load time under network throttling (50 Mbps), long tasks > 200 ms, and relative regression of software-rendered frame time vs main (± 15%) as a warning.
- Reference mode (
pnpm perf:ref, manual on the M1 Pro reference machine): enforces absolute frame-time budgets and writesperf/results/<date>-<sha>.json; committed results feed the trend chart.
Includes a baseline stress scene in the greybox kit (N moving physics props + lights).
Scope
- In: Perf harness (frame-time sampling via rAF + PerformanceObserver long tasks, CDP heap), budget file, reporters (JSON + job summary)
- In: CI job
perf-budget - In: Baseline stress scene
perf-baseline - Out: Creature/combat stress scene (e32-combat-stress-scene)
- Out: Cross-browser runs (e32-browser-matrix)
Notes
- Budget numbers come only from backlog-contract §1 Hardware baseline; stories cite them rather than inventing new ones.
- Frame-time sampling excludes the first 3 s (warm-up) and uses ≥ 1,800 frames.
Acceptance criteria
- AC-1 perf Given the
perf-baselinescene in reference mode on the reference machine (High, 1440p-equivalent), when measured over 1,800 frames, then p95 frame time ≤ 16.7 ms or the run fails with a per-metric report. - AC-2 integration Given CI mode, when initial transfer size exceeds 50 MB or throttled load-to-playable exceeds 10 s, then
perf-budgetfails naming the metric and delta vs budget. - AC-3 unit Given a samples array, when percentiles are computed, then p50/p95/p99 match a committed reference implementation, and fewer than 300 samples yields an "insufficient samples" failure rather than a pass.
- AC-4 integration Given heap after 5 minutes idle in the testbed exceeds 1.5 GB, when measured, then the suite fails.
- AC-5 integration Given a PR whose software-rendered frame time regresses > 15% vs main, when CI runs, then a warning annotation appears (non-blocking).
- AC-6 unit Given
perf-budgets.jsonwith a budget lacking asourcefield citing the baseline, when validated, then it fails.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e00.3 Add CI with per-layer coverage gates, ratchet and Playwright smoke done
Closed
Completed in PR #186 (2026-10-03)
area:perfepic:e32milestone:m0mw-e32.15Run pnpm perf:ref on the M1 Pro reference machine and commit the first results
Description
Why
mw-e32.1 AC-1 (perf-baseline p95 frame time ≤ 16.7 ms at High, 1440p-equivalent) can only be verified on the reference machine named in the backlog contract §1 (MacBook Pro M1 Pro, 16 GB, Chrome current). The suite is built and tested; the first official reference run, and the first committed result that starts the trend, need the owner at that machine.
What
As the owner, I want to run
pnpm perf:refon the reference machine and commit the result so that AC-1 is verified and the perf trend has its first point.Steps (on the M1 Pro, plugged in, other apps closed, display awake):
git checkout main && git pull(after the mw-e32.1 PR is merged), thenpnpm install.pnpm exec playwright install chromiumonce (the run itself uses installed Google Chrome;VESPER_PERF_CHANNEL=chromiumfalls back to the bundled build).pnpm perf:ref— builds, serves on 127.0.0.1:4183, opens a headed Chrome window (1280×720 at DPR 2 = 2560×1440 drawing buffer, vsync off) on/?scene=perf-baseline, samples 1,800 frames after a 3 s warm-up. Don't touch the window while it runs (~15 s).- Check the printed table: frame-p50-high and frame-p95-high must say pass (≤ 16.70 ms). A fail lists each metric with its delta vs budget.
- Create a branch in a worktree,
git add perf/results/<date>-<sha>.json, commit with sign-off referencing this bead, open a PR, merge.
Scope
- In: one reference run on the reference machine, the committed perf/results file, closing this bead with the measured p50/p95.
- Out: the trend chart over perf/results (separate story under mw-e32), low-preset hardware runs, cross-browser runs (e32-browser-matrix).
Notes
The mw-e32.1 agent ran
pnpm perf:refon a machine reporting Apple M1 Pro / 16 GB / 3456×2234 while other agents loaded it: p50 2.80 ms, p95 3.70 ms over 1,800 frames (Chrome 154, ANGLE Metal). Indicative only; this bead is the official run.Acceptance criteria
- AC-1 manual Given the merged perf suite on the reference machine (MacBook Pro M1 Pro, 16 GB, Chrome current), when the owner runs pnpm perf:ref, then frame-p50-high and frame-p95-high pass (≤ 16.7 ms over 1,800 frames in perf-baseline at High, 1440p-equivalent), verifying mw-e32.1 AC-1.
- AC-2 manual Given that run, when it finishes, then perf/results/<date>-<sha>.json is committed to main via PR.
- AC-3 manual Given a failing run, when the table shows a metric over budget, then a bug bead is filed under mw-e32 with the per-metric report attached instead of committing a pass.
Waits on
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
Closed
Completed in PR #346: p50 2.80 ms, p95 3.90 ms on the M1 Pro reference machine (2026-11-09)
area:perfarea:qaepic:e32milestone:m0milestone:m4needs-human
M1 Grey-box slice 2
mw-e32.3Enforce JS bundle and asset size budgets per PR
Description
Why
Initial download ≤ 50 MB and load ≤ 10 s are easiest to protect byte by byte at PR time.
What
size-limit (or custom script) over
dist/: initial JS ≤ 1.5 MB gz, total initial payload ≤ 50 MB, per-asset caps by type (texture ≤ 4 MB, audio loop ≤ 3 MB). PR comment/job summary shows delta vs main; dev tools must not appear in the production bundle.Scope
- In: Size budget config + check job
- In: Assertion that
src/tools/**modules are absent from prod chunks - Out: Asset compression pipeline (e32-asset-loading)
Notes
- None.
Acceptance criteria
- AC-1 integration Given a PR increasing initial JS beyond 1.5 MB gz, when CI runs, then the size check fails showing the delta.
- AC-2 integration Given a production build, when chunk contents are inspected, then no module from
src/tools/is present. - AC-3 unit Given a single texture of 6 MB added, when checked, then it fails naming the file and the 4 MB cap.
Waits on
- mw-e32.1 Build automated perf budget suite against the hardware baseline done
Closed
Completed in PR #401 (2026-11-10)
area:perfepic:e32milestone:m1mw-e32.18Move src/tools demos and probes behind lazy imports so the release bundle has none
Description
Why
The size check (mw-e32.3) found 15 src/tools modules in the release bundle's main chunk (anim-demo, arrow-demo, awareness-demo, debug-camera, perf frame-probe and agitator, spell-vfx-demo, vfx-demo, tools index): src/main.ts imports them statically. They are listed as toolWaivers in perf/size-budgets.json.
What
Load each demo and probe with a dynamic import only when its scene or debug flag asks for it, with behaviour and world hashes unchanged, then delete the waiver prefixes.
Scope
- In: src/main.ts imports; perf/size-budgets.json toolWaivers
- Out: new tooling
Acceptance criteria
AC-1 integration Given a release build (VESPER_DEBUG_CONSOLE=off), when pnpm size:check runs with no toolWaivers, then no src/tools module is in any chunk.
AC-2 integration Given the slice playthrough and e2e smoke, when run, then they pass at unchanged golden hashes.
Closed
Completed in PR #402 (2026-11-12)
area:perfepic:e32milestone:m1milestone:m4
E33Developer/Content ToolsDevelopers and designers can create, tune and debug spells, creatures, loot, encounters, dialogue and puzzles in minutes, with visual insight into the systemic simulation and one-keypress bug capture.4/17 done · 24%
M1 Grey-box slice 4
mw-e33.1Add in-game debug console with commands, cheats and spawners
Description
Why
Fast content creation and debugging (constitution §10) need a way to poke the running game without editing code.
What
As a developer, I want a toggleable console with typed commands so that I can spawn, teleport, cheat and inspect instantly.
Backtick toggles an overlay console (dev builds and
?debug=1on playtest builds); command registry (registerCommand({name, args schema, run})) with autocomplete, history (persisted), help. Built-ins:help,spawn <contentId> [count],give <itemId> [n],god,noclip,kill,tp <x y z|bookmark>,timescale <n>,scene <id>,set <setting> <value>,seed. Commands that mutate sim state go through the sim command queue (deterministic, recorded in replays).Scope
- In: Console UI, registry, parser with zod arg validation, autocomplete against content ids
- In: Built-in commands
- In: Production stripping (dynamic import behind build flag)
- Out: Entity inspector (e33-entity-inspector)
- Out: Domain-specific commands (each system registers its own)
Notes
- None.
Acceptance criteria
- AC-1 unit Given
spawn testprop-crate 3, when executed, then 3 crate entities exist in the sim next tick and the command is present in the replay input log. - AC-2 unit Given
spawn does-not-exist, when executed, then the console prints "unknown content id" with the 3 closest matches and the sim is unchanged. - AC-3 unit Given
givewith a non-numeric count, when executed, then argument validation fails with usage help. - AC-4 e2e Given a dev build, when backtick is pressed, then the console opens, captures keyboard input (player does not move) and Esc/backtick closes it.
- AC-5 integration Given a production build without
?debug=1, when backtick is pressed, then nothing opens and console modules are absent from the main bundle. - AC-6 e2e Given typing
sp+ Tab, when autocomplete runs, then it completes tospawn.
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
Closed
Completed in PR #98 (2026-09-30)
area:toolsepic:e33milestone:m1mw-e33.15Move the debug console overlay onto the UI component kit
Description
Why
The debug console (mw-e33.1) shipped with its own minimal, inline-styled DOM because the HUD/menu component kit (mw-e00.23) was still being built. Tools should share one UI kit so focus handling, theming and the UI test harness apply to them too.
What
As a developer, I want the debug console built on the UI component kit so that it looks and behaves like the rest of the in-game UI and is testable with the same harness.
Replace the hand-built overlay in src/tools/console/view.ts with kit components (panel, log list, text input); keep the command registry, history and key behaviour unchanged.
Scope
- In: Console overlay on the kit, focus capture through the kit's focus/input-context API
- Out: New console commands; entity inspector (mw-e33.3)
Notes
- Existing e2e (e2e/debug-console.spec.ts) must keep passing unchanged.
Acceptance criteria
- AC-1 e2e Given the console open, when inspected, then it is composed of kit components and takes keyboard focus through the kit's input-context API (player does not move while it is open).
- AC-2 e2e Given the existing debug-console e2e suite, when run, then it passes unchanged.
Waits on
- mw-e00.23 Build HUD/menu UI layer, component kit, focus nav and UI test harness done
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Superseded: the console was built on the UI kit (mw-e00.23) within mw-e33.1, PR #98 (2026-09-29)
area:toolsepic:e33milestone:m1mw-e33.16Spawn debug-console props as physics objects
Description
Why
The debug console's
spawn(mw-e33.1) creates test props as plain scene-spawn entities: they are drawn but do not fall, stack or collide. Since mw-e03.39, scene props with abodyare physics objects, so console-spawned props should behave the same way when testing physics, stacking and fire.What
As a developer, I want
spawn testprop-crate 3to drop three physical crates so that I can test physics interactions without editing a scene.Spawners for props with a
bodygive the entity its material properties and make it a physics object (addPhysicsObject) through the debug command system. Material properties added during a tick are deferred, so the body is added once they are live (e.g. on the tick after the spawn); the renderer binds these props withreadPhysicsObjectTransform.Scope
- In: Physics-object spawning for props with a body, render binding for them
- Out: Spawning creatures (mw-e12.4)
Notes
- Keep it deterministic and recorded: everything happens inside
World.stepfrom the recorded spawn command.
Acceptance criteria
- AC-1 unit Given spawn testprop-crate 3 above the floor, when the sim runs, then the 3 crates are physics objects with wood properties and the crate's mass, and fall to rest on the floor.
- AC-2 integration Given a recorded session with a physics spawn, when replayed, then every checkpoint hash matches.
Waits on
- mw-e33.1 Add in-game debug console with commands, cheats and spawners done
Closed
Completed in PR #109 (2026-09-30)
area:toolsepic:e33milestone:m1mw-e33.17Debug teleport moves physics-object props
Description
The debug console's tp/teleport command (src/sim/debug/system.ts teleport) moves a placed object by rewriting its SceneTransform. Since mw-e33.16, console-spawned and scene props with a body are physics objects whose pose lives in the physics port and physics.object, so teleporting one changes nothing visible. Teleport should move the body (and wake it at rest) through the physics port inside the recorded command's tick. Discovered while doing mw-e33.16.
Acceptance criteria
- AC-1 unit Given a physics-object prop, when the debug tp command teleports it, then its physics body is moved to the target pose (and woken) within the recorded command's tick, and physics.object reflects the new pose.
- AC-2 unit Given the same command log replayed twice, then the resulting state hashes match (deterministic).
- AC-3 unit Given a non-physics placement, tp still rewrites its SceneTransform as before.
Closed
Completed in PR #122 (2026-09-30)
area:toolsepic:e33milestone:m1
E37Art Direction & Asset PipelineThe Vesper Bell has one coherent, readable, performant visual identity — a hand-painted, chunky low-poly storybook world — produced by a solo developer with GPT image generation, CC0 kits and one image-to-3D tool, and every art asset flows through a validated, licence-tracked pipeline from prompt to runtime.3/432 done · 1%
M1 Grey-box slice 1
mw-e37.1Review and sign off the visual style bible and GPT style preamble
Description
Why
Consistency across hundreds of generated assets is only possible if it is defined once, up-front (contract §6). The draft exists; the owner must own the look before any prompt bead is written.
What
The owner reads the draft
docs/art/style-bible.md; Claude test-runs the GPT STYLE PREAMBLE on 4 sample prompts (one concept, one icon, one tileable, one portrait) viacodex execand the owner judges them; the owner adjusts palette/pillars/budgets where they disagree, and marks the bible signed-off (version + date in the header).Scope
- In: Owner review of every section; edits to the draft
- In: Preamble smoke test: 4 Claude-generated samples (ad-hoc
codex execife37-image-gen-runneris not ready) committed underassets/source/concept/as style probes with CREDITS rows - In: Sign-off header
- Out: Prompt templates (see
e37-prompt-templates) - Out: Story/lore names (owned by
e39-story-bible; placeholders in the bible are updated when it lands)
Notes
Draft written by the art-direction planning pass. Key decisions to confirm: 60/30/10 palette ratio, warm-key/cool-fill rule, Legibility Language table (§7), budgets (§13) vs
e32-perf-budgets, fonts (all OFL). Related: e39-story-bible (minotaur heart-chamber and NPC names are placeholders).Acceptance criteria
- AC-1 manual Given the draft style bible, when the owner completes review, then the header shows
Status: SIGNED OFF vX.Y — <date>and all open questions are resolved or filed as beads. - AC-2 manual Given the GPT STYLE PREAMBLE pasted verbatim, when 4 sample prompts (concept, item icon, tileable, portrait) are generated, then at least 3 of 4 match the palette/lighting checklist on the first or second attempt; failures lead to a preamble revision recorded in the bible changelog.
- AC-3 content Given the 4 style probe images committed, when the asset validator (or a manual check before it exists) runs, then each has an
assets/CREDITS.mdrow with tool, model version, tier and date. - AC-4 manual Given the budgets in §13, when compared with
e32-perf-budgets, then any mismatch is reconciled so both documents cite the same numbers. - AC-5 manual Edge: given a preamble sample that produces text/letters or photorealism in 2 consecutive attempts, when reviewed, then the preamble negative clause is strengthened and re-tested before sign-off.
Waits on
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #16 (2026-09-28)
area:artepic:e37milestone:m1needs-human
M3 MVP content 2
mw-e37.431Probe the GPT STYLE PREAMBLE on four sample images
Description
Why
The style bible was signed off (mw-e37.1) before its GPT STYLE PREAMBLE was tested. Four contrasting samples show whether the preamble holds the palette, lighting and no-text rules before prompt beads start producing hundreds of assets.
What
Claude runs the preamble (style bible §14, verbatim) on four sample prompts via codex exec, using the image runner (mw-e37.4) if it's ready or ad hoc if not: one concept (Wendmouth's Stone Wharf, to also test the stone-harbour canon), one item icon, one tileable, one portrait. The owner judges them against the palette/lighting checklist. Commit them under assets/source/concept/ as style probes with CREDITS rows (placeholder: yes). Revise the preamble if needed and log the revision in the style bible change log (§18).
Scope
- In: 4 probes, owner review, CREDITS rows, preamble revisions
- Out: prompt templates, real assets
Acceptance criteria
- AC-1 manual Given the preamble pasted verbatim, when the 4 samples are generated, then at least 3 of 4 match the palette/lighting checklist on the first or second attempt; otherwise the preamble is revised, logged in §18 and re-tested.
- AC-2 content Given the 4 committed probes, when checked, then each has an assets/CREDITS.md row with tool, model version, tier and date.
- AC-3 manual Edge: given a sample with text/letters or photorealism in 2 consecutive attempts, when reviewed, then the negative clause is strengthened and re-tested.
Closed
Completed in the style probes PR: owner review passed 4 of 4, probes committed with CREDITS rows (2026-10-04)
area:artepic:e37milestone:m3mw-e37.432Wire the glenstone style-probe texture into the demo floor
Description
Why
The generated glenstone probe should be evaluated in motion and under the real demo lighting, not only as a flat image.
What
Prepare a seamless power-of-two runtime derivative of tex-glenstone-quay-wendmouth-probe-01 and apply it to walkable geometry in the testbed/demo scene while preserving the greybox material in other scenes.
Scope
- In: non-destructive 1024px runtime derivative, repeating texture setup, testbed-only renderer wiring, disposal and smoke coverage
- In: keep the asset marked as a placeholder/style probe until approval and the full validator/import pipeline land
- Out: KTX2 production compression and global environment art replacement (mw-e37.8)
Acceptance criteria
- AC-1 content The runtime texture is square, power-of-two, and has a source/probe counterpart.
- AC-2 e2e The testbed scene loads with the repeating glenstone texture on walkable geometry and no console errors.
- AC-3 e2e Other scenes retain the existing greybox material.
- AC-4 integration Texture and material resources are disposed with the scene renderer.
Closed
Completed locally; awaiting normal review/commit workflow (2026-10-03)
area:artepic:e37milestone:m3
E38Audio Direction & Asset PipelineThe Vesper Bell sounds like one world — acoustic dark-folk music (hurdy-gurdy, nyckelharpa, low strings, wordless choir) that adapts to exploration, stealth, tension and combat, and tactile SFX whose loudness and character match the stealth/sound simulation — produced with Suno and a decided SFX source through a validated, licence-tracked pipeline.4/181 done · 2%
M1 Grey-box slice 1
mw-e38.1Review and sign off the audio bible with Suno and SFX preambles
Description
Why
Music and SFX from different tools and sessions only sound like one game if identity, loudness and naming are fixed first (contract §6).
What
The owner reviews the draft
docs/audio/audio-bible.md, runs the SUNO STYLE PREAMBLE on 2 test tracks (Briar Glen explore, labyrinth explore) and the SFX PREAMBLE on 3 test sounds (footstep, spell cast, UI click), adjusts, and signs off.Scope
- In: Owner review and edits
- In: Preamble probes committed as placeholders with CREDITS rows
- In: Sign-off header
- Out: Templates (see
e38-prompt-templates) - Out: Audio engine (see
e28-audio-engine)
Notes
Decisions to confirm: instrumentation and "never" list, D Dorian home key, leitmotif shapes, LUFS targets (music −18/−16, ambience −24), Opus + AAC fallback, surface set alignment with
e09-sound-propagation.Acceptance criteria
- AC-1 manual Given the draft audio bible, when reviewed, then the header shows
Status: SIGNED OFF vX.Y — <date>and open questions are resolved or filed as beads. - AC-2 manual Given the SUNO STYLE PREAMBLE, when 2 instrumental test tracks are generated, then both contain no intelligible vocals and use the listed instrumentation; otherwise the preamble/exclusions are revised and re-tested.
- AC-3 manual Given the footstep surface list, when compared with
e09-sound-propagationsurface ids, then the lists match exactly or the bible is updated. - AC-4 manual Edge: given a Suno take that includes vocals despite the Instrumental toggle, when reviewed, then it is rejected and the failure pattern is documented in bible §4 usage notes.
Waits on
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #15 (2026-09-28)
area:musicarea:sfxepic:e38milestone:m1needs-human
M3 MVP content 3
mw-e38.101Write main theme and motif anchor Suno prompts (3)
Description
Why
Suno cannot reproduce a melody from text, so every derived track is built from approved motif anchors (audio bible §1.4); these three come first.
What
Write one prompt file per track in
assets/prompts/music/<asset-id>.mdfrom_templates/music-track.md: Style field = SUNO STYLE PREAMBLE (audio bible §4.1) verbatim + tags, Exclude styles, structure-only Lyrics, key/mode, BPM, metre, length, loop plan and output spec.The title main theme ("The Glen", D Dorian 84 BPM 6/8), the Vesper hymn motif anchor (D Dorian plainchant line answered by a low bell toll, free-time) and the Nightjar's song motif anchor (E Aeolian 3/4 lullaby that drifts out of tune over a churring trill).
Asset ids (3):
music-title-main-thememusic-motif-vesper-hymnmusic-motif-nightjar-song
Scope
- In: One Suno prompt per id
- In: Motif references per audio bible §1.4 and family key/BPM per §3
- Out: Pasting into Suno (
e38-asset-music-anchors-generate, owner)
Notes
Anchors are used as Suno audio references for every other world music set;
music-hymn-vesper-hum(diegetic hum) is derived from the hymn anchor ine38-asset-music-stingers-world-*.Music is human-in-the-loop: the owner pastes each Suno prompt (≈ 1 min per track) and drops the chosen take in
assets/_incoming/music/. Family members share key/mode and BPM (audio bible §2.2); derived tracks use the approved motif anchors as audio reference where the Suno tier allows. Not gated by the sonic lock (different medium). Paid Suno tier required for distribution.Acceptance criteria
- AC-1 content Given the 3 ids, when the asset validator runs, then each has one prompt file matching audio bible §6 music naming (
music-<location>-<time?>-<state>,music-stinger-<event>-<key?>,music-<family>-stem-<layer>). - AC-2 content Given each prompt, when compared with docs/audio/audio-bible.md §4.1, then the Style field starts with the preamble byte-for-byte and the Exclude field matches the bible list.
- AC-3 content Given each prompt, when inspected, then key/mode and BPM equal the audio bible §3 family row (or §2.3 stinger row), length is within §4 / §2.3 limits and the leitmotif it must carry is named.
- AC-4 content Edge: given a prompt whose Lyrics field contains singable words, or which names a real artist, composer or game, when validated, then it fails naming the file.
Waits on
- mw-e38.1 Review and sign off the audio bible with Suno and SFX preambles done
- mw-e38.3 Review and finalize music, stinger and SFX prompt templates open
Closed
Completed in PR #21 using template v1; mw-e38.3 (template review) still open — if it changes music-track.md, update these prompt files there. (2026-09-28)
area:musicasset:musicepic:e38milestone:m3mw-e38.177Probe the SUNO STYLE PREAMBLE on two explore tracks
Description
Why
The audio bible was signed off (mw-e38.1) before its preamble was tested on location music. The approved main theme shows the preamble can produce an instrumental take, but a town cue and a labyrinth cue stress different parts of it (warmth vs cavernous dread).
What
Claude writes two probe prompts from audio bible §4.1: Briar Glen explore and labyrinth (the Knot) explore. The owner auditions takes in Suno and downloads only a take worth keeping (download cap: see Suno plan notes). Record any vocals-despite-Instrumental failure pattern in audio bible §4 and revise the preamble or exclusions if needed.
Scope
- In: 2 prompts, owner audition, bible §4 notes if anything fails
- Out: final location music (the per-location e38 music beads)
Acceptance criteria
- AC-1 manual Given the two probe prompts, when generated in Suno, then the kept takes contain no intelligible vocals and use only the listed instrumentation; otherwise the preamble or exclusions are revised and re-tested.
- AC-2 manual Given a take with vocals despite the Instrumental toggle, when reviewed, then it is rejected and the failure pattern is recorded in audio bible §4.
Closed
Completed in PR #21: owner kept both probe takes (Suno v6, Pro) (2026-09-28)
area:musicepic:e38milestone:m3needs-humanmw-e38.181Record Suno links for every picked music take
Description
Owner wants one place in the repo listing the Suno song link for each kept take (and takes awaiting a pick) so nobody hunts through the Suno UI.
Closed
Completed in PR #286 (2026-11-07)
area:musicepic:e38milestone:m3
E39Story & NarrativeThe MVP adventure "The Labyrinth Beneath Briar Glen" is a coherent, reactive story: the town, the Briarwood, the River Wend, Mooring's Watch, the Deepworks, Bellwater Priory, the Knot and the optional harbour town of Wendmouth are populated with memorable characters, class-flavoured dialogue, multi-resolution quests, rumours that lead somewhere, and books/item text that carry the lore. The minotaur twist lands, failure bends the story instead of ending it, and every class experiences the key beats differently.28/48 done · 58%
M1 Grey-box slice 2
mw-e39.1Owner review and sign-off of the story bible; lock MVP canon
Description
Why
Every dialogue line, quest, book and asset prompt depends on stable canon. The constitution values memorable NPCs and creatures-as-characters; the bible defines them once so parallel work stays consistent.
What
The owner reviews the drafted
docs/narrative/story-bible.md(premise, world history, factions, main quest beat sheet and endings, cast, side quests, rumours, creature lore, failure-creates-stories rules, naming conventions, glossary, canon quick-reference facts) and either signs it off or requests changes. The title section is reviewed and handed to the title decision bead. Signed-off canon is marked with a "Status: CANON v1" header and a change log.Scope
- In: owner read-through and comments
- In: revisions requested by the owner
- In: marking canon v1 and adding a change-log section
- In: confirming the 12 principal NPCs, 10 creature types, 5 side quests and 4 headline endings
- Out: the final title choice (see e39-title-decision)
- Out: writing any content data (later e39 items)
Notes
Draft exists at docs/narrative/story-bible.md. Canon changes after sign-off require a bible change-log entry and a lint update (e39-narrative-lint). Related: art style bible (e37-style-bible) and audio bible (e38-audio-bible) should read §3.5 Locations and §5 Cast.
Acceptance criteria
- AC-1 manual Given the drafted bible, when the owner has reviewed it, then every owner comment is resolved or recorded as an open question in the bible.
- AC-2 content Given the signed-off bible, when opened, then its header reads "Status: CANON v1" with a date and a change-log section exists.
- AC-3 content Given the bible, when checked, then §11.3 lists every canon number (timeline, counts, ages, bounty) that the narrative lint will enforce.
- AC-4 content Given the bible, when checked, then it names exactly the principal cast, creature roster, side quests and endings that downstream e39 items reference, with no TBD placeholders.
- AC-5 manual Edge: given the owner rejects a core element (e.g. the minotaur twist), when revised, then the beat sheet, cast and endings sections are updated together so no section contradicts another.
Closed
Completed in PR #14 (2026-09-28)
area:narrativeepic:e39milestone:m1needs-humansize:smw-e39.2Decide the shipping title and record it as an ADR
Description
Why
"Merlin's Wrath" is a working title; Merlin is not in the story and "wrath" signals the wrong tone. The title shapes asset prompts, the title screen and discoverability.
What
The owner chooses between retaining the codename and the bible's shortlist (top pick: "Nightjar — The Labyrinth Beneath Briar Glen"; runner-up "The Vesper Bell"). A basic name-collision search (games stores, trademark databases, domain/handle availability) is done for the top 3. The decision is recorded as an ADR.
Scope
- In: collision search for the top 3 candidates
- In: ADR at docs/decisions/NNNN-game-title.md
- In: updating the bible title section and backlog-contract working-title line
- Out: renaming the repository (optional follow-up)
- Out: logo/title art (E37 prompt beads)
Notes
Options and rationale: story-bible §1. If the codename is kept, the bible suggests the "Merlin Watch" (falcon) retrofit so the name means something in-world.
Acceptance criteria
- AC-1 content Given the ADR, when read, then it states the chosen title, the alternatives considered, the collision-search results for each of the top 3, and the rationale.
- AC-2 manual Given the collision search, when a candidate collides with an existing game or trademark in the same class, then it is marked rejected in the ADR with the evidence link.
- AC-3 content Given the decision, when merged, then story-bible §1 and the backlog-contract working-title line reflect the chosen title.
- AC-4 content Edge: given the owner defers the decision, when the ADR is written, then it records "codename retained until <milestone>" and the bead stays open.
Waits on
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #2 (owner decided the title ahead of the full story-bible sign-off mw-e39.1) (2026-09-27)
area:narrativeepic:e39milestone:m1needs-humansize:s
M3 MVP content 26
mw-e39.3Set narrative content layout, ID scheme and class-variant tagging
Description
Why
Many narrative items are written in parallel; without one file layout and ID scheme, the lint and validators cannot catch dangling references and class variants will be inconsistent.
What
As a designer, I want a documented, validator-backed convention for narrative data so that every dialogue, quest, rumour, book and description file is found, referenced and tested the same way. Defines: directory layout under src/content (dialogue/, quests/, rumors/, books/, descriptions/, narrative/), ID prefixes from bible §11.1 (npc-, quest-, book-, rumor-NN-, desc-), a class-variant field convention (knight|archer|sorcerer|thief|all), world-state fact naming (e.g. horn.fate, clatter.restored), and a canon glossary data file consumed by the lint.
Scope
- In: docs/narrative/content-conventions.md
- In: src/content/narrative/glossary.json (names, places, canon facts)
- In: one worked example file per content kind passing the existing validators
- Out: the dialogue/quest schemas themselves (e22-dialogue-runtime, e23-quest-runtime)
- Out: the lint tool (e39-narrative-lint)
Notes
World-state fact keys must be registered with the world-state store (e27-world-state-store). Keep in-game text British-leaning, code identifiers US spelling (bible §11.1).
Acceptance criteria
- AC-1 content Given the conventions doc, when read, then it defines directory, ID prefix, class-variant field and world-state fact naming for all six content kinds.
- AC-2 unit Given each worked example file, when run through the content pipeline validators, then it validates with zero errors.
- AC-3 unit Given glossary.json, when validated, then every principal NPC, location, creature type and canon number from bible §5, §3.5, §8 and §11.3 is present exactly once.
- AC-4 unit Edge: given an example dialogue line tagged with an unknown class value (e.g. "paladin"), when validated, then validation fails naming the file and field.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e39.1 Owner review and sign-off of the story bible; lock MVP canon done
Closed
Completed in PR #423 (2026-11-12)
area:narrativeepic:e39milestone:m3size:smw-e39.4Script the main quest graph with four endings as quest data
Description
Why
The MVP scenario must exercise the constitution through one adventure with multiple resolutions; the quest runtime should carry it with data, not bespoke scripts.
What
As a player, I want the Labyrinth Beneath Briar Glen quest to track what I actually did so that my choices about the minotaur, the abbot and Vane produce different outcomes. Encodes bible §4.1 beats 1–16 as quest stages/objectives: the first night, investigation hub (any order) with the optional Wendmouth thread, six entry routes into the Deepworks (including the Weeping Adit river route), the Warden encounter with six resolutions (kill, spare, trick, befriend, help, Trial of Horns), abbot resolutions (fight, trick, persuade, join), the Climb, Vespers, the morning after, and the four headline endings assembled from world-state facts.
Scope
- In: quest-labyrinth-beneath-briar-glen data file(s)
- In: world-state facts: horn.fate, abbot.fate, vane.fate, breach.sealed, tansy.saved, clatter.restored, goblins.standing, wendmouth.visited, crate.intercepted, tern.sailed
- In: the Gilded Tern sailing trigger at Act III start
- In: ending selection rules for the 4 headline endings and their epilogue-slide variants
- In: quest-graph tests reaching every ending
- Out: dialogue lines (e39-dialogue-* items)
- Out: intro/ending prose (e39-intro-endings-text)
- Out: encounter/level layout (E26)
Notes
Investigation hub objectives must be completable in any order (bible §4.1 beat 4). Wendmouth is optional (bible §3.6): no main-quest objective may require it; harbour facts only add evidence, a befriend trigger (name-stone) and epilogue slides. Horn yields rather than dying at low health; killing requires an explicit choice. Related: E26 first dungeon for spatial placement.
Acceptance criteria
- AC-1 unit Given the quest data, when validated by the quest runtime validator, then it passes with zero errors and every referenced world-state fact is registered.
- AC-2 unit Given scripted fact sets, when the ending rules are evaluated, then each of the 4 headline endings is reachable (including with wendmouth.visited = false throughout) and each fact set maps to exactly one ending.
- AC-3 unit Given the investigation hub, when its objectives are completed in each of 3 different orders, then the quest advances to "Getting in" identically.
- AC-4 unit Given any of the 6 Deepworks entry routes is completed, when the quest updates, then the "Getting in" stage completes and the route used is recorded.
- AC-5 unit Edge: given Horn is killed, when Act III begins, then the Climb uses the goblin or Clatter defender variant if available, else the chase variant, and the quest never soft-locks.
- AC-6 unit Edge: given Act III begins with crate.intercepted = false, when the stage starts, then tern.sailed is set and no Wendmouth objective blocks progress.
- AC-7 unit Edge: given Tansy is not intercepted in Act III, when the Cradle stage starts, then the hostage variant activates and all abbot resolutions remain available.
- AC-8 manual Given a full playthrough per class, when the quest completes, then the narrative designer confirms beats 1–16 play in order with no missing objective text.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #440 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.5Write dialogue for Mirela, Dot and Captain Hale
Description
Why
The bookseller, the rumour-monger and the guard captain are the investigation hub; memorable, reactive town NPCs are the QfG/DOS2 heart of the game.
What
As a player, I want the three hub NPCs to remember me, react to my class and point me at real places so that talking feels as rewarding as exploring. Dialogue graphs for Mirela Thorne (Lamplit Stacks: shop banter, restricted-shelf access, Nine Sleeps secret, Mortimer), Dot Farrow (Sleeping Ox: rumour trading hook, fence for thieves, Fitch), and Captain Hale (missing-persons list, sleepwalking secret, jail dialogue, Act III testimony). Voice per bible §5.1, §5.2, §5.4.
Scope
- In: npc-mirela, npc-dot, npc-hale dialogue data
- In: class-variant greetings and ≥ 2 class-specific branches per NPC
- In: disposition-gated lines
- In: post-quest reactions to each headline ending
- Out: rumour content itself (e39-rumor-set)
- Out: shop inventory rules (e21-bookshop-system)
Notes
Mirela must never sell Nine Sleeps. Hale must not know he delivered the "friend below" note until The Captain's Boots reveals it.
Acceptance criteria
- AC-1 unit Given the three dialogue files, when validated by the dialogue validator, then all pass and every node is reachable from an entry node.
- AC-2 unit Given each class in turn, when each NPC greets the player for the first time, then a class-specific greeting line is selected (4 distinct lines per NPC).
- AC-3 unit Given the fact hale.sleepwalking.revealed is false, when any Hale node is evaluated, then no line references him delivering the note.
- AC-4 content Given Mirela's dialogue, when searched, then no option transfers the book-nine-sleeps item to the player.
- AC-5 unit Edge: given Mirela's disposition is "banned" (Mortimer caught the player), when the player talks to her, then the banned branch plays and access resets after the documented condition (biscuits or one in-game day).
- AC-6 manual Given a read-through, when the narrative designer compares lines to bible §5 voice samples, then each NPC's voice is consistent and no line breaks the tone guide (§2.4).
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #430 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.6Write dialogue for Vane, the Pells, Juniper, Oswin and Duggan
Description
Why
The mine owner's grey morality, the baker's grief and the merchants' personalities make Briar Glen a living town rather than a quest dispenser.
What
As a player, I want the rest of Briar Glen to react to the crisis and to me so that the town feels lived-in. Dialogue for Harrow Vane (bounty, charm, confrontation with ledger/Duggan, confess/bribe/flee outcomes), Hollis Pell (grief, family Bible, reunion with Tansy), Tansy Pell (post-rescue), Juniper Fenn (wolves, sleep-draught guilt, merchant lines), Oswin Brand (merchant, bell-tongue mould), Duggan Marl (crate captive, testimony).
Scope
- In: npc-vane, npc-hollis, npc-tansy, npc-juniper, npc-oswin, npc-duggan dialogue data
- In: Vane confrontation branch with 4 outcomes (exposed, confesses, bribes, flees)
- In: merchant greeting/farewell lines with class variants
- Out: shop mechanics (e20-shop-system — Related, not anchor-linked here)
- Out: side quest objective data (e39-side-quests-*)
Notes
Vane is redeemable (bible §5.3); avoid moustache-twirling. Oswin's mould enables a recast tongue if the original is destroyed — must match main quest fallback.
Acceptance criteria
- AC-1 unit Given the six dialogue files, when validated, then all pass with zero unreachable nodes.
- AC-2 unit Given the facts vane.ledger.obtained and duggan.testimony, when the confrontation is evaluated with each combination, then the available Vane outcomes match the table in the file header.
- AC-3 unit Given tansy.saved is true, when Hollis is spoken to after the quest, then the reunion branch plays; given false, the grief branch plays.
- AC-4 unit Edge: given the Vesper Bell tongue was destroyed, when Oswin is spoken to, then the recast-tongue option is offered exactly once.
- AC-5 manual Given a read-through, when compared to bible §5.3–5.7, then voices match and Vane reads as morally grey rather than purely villainous.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #427 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.7Write Brother Horn's dialogue across all Warden resolutions
Description
Why
The minotaur twist is the emotional core of the MVP and the constitution's flagship "fight, trick, befriend or help" creature.
What
As a player, I want the minotaur to speak with me differently depending on how I reached him so that the reveal feels earned. Dialogue for Horn: the yield line, spare/kill choice, post-trick confrontation in the Fold, befriend triggers (Vesper hymn, horn-ring, vouching by Snig or Clatter, lowered weapon), the knight's Trial of Horns, help/heal/seal-the-breach lines, rescue-count remarks after player deaths, Act III shaft-defence barks, and ending lines.
Scope
- In: npc-horn dialogue data
- In: class-specific first-meeting lines (bible §4.3)
- In: rescue-count lines for 1, 2, 3 and 5+ rescues
- Out: Horn combat behaviour (E14 social creatures, E13 bestiary)
- Out: quest state logic (e39-main-quest-data)
Notes
Voice: slow, short, plain sentences with pauses; never roars words. "The monks called me Brother Horn. It was a kindness. I have kept it." Related: E14 social creature hooks for yield/befriend.
Acceptance criteria
- AC-1 unit Given Horn's dialogue file, when validated, then it passes and each of the 6 Warden resolutions has at least one terminal node that sets horn.fate.
- AC-2 unit Given each befriend trigger fact independently, when the first-meeting node is evaluated, then the befriend branch is offered.
- AC-3 unit Given player.rescues.byHorn = 1, 2, 3 and 6, when Horn is spoken to, then the matching rescue-count line is selected.
- AC-4 unit Edge: given the player tricked Horn and then flees the Fold confrontation, when Horn is next met, then the "angry, then weary" re-entry branch plays instead of the first-meeting branch.
- AC-5 manual Given a playtest of the reveal, when the Fold is entered, then the reveal is understood from the scene before Horn explains it (confirmed in e39-playtest-comprehension).
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #433 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.8Write dialogue for Clatter, Mother Kettleback and Snig
Description
Why
A skeleton who remembers and goblins you can talk to are explicit constitution examples; they carry the game's humour and warmth in the dark.
What
As a player, I want the skeleton and the goblins to be people so that the dungeon has characters, not just enemies. Clatter: memory fragments that grow as The Name on the Bones progresses, apology barks when hostile, rest/join choice. Mother Kettleback: negotiation after capture or approach, Horn arrangement, Duggan crate, trade lines. Snig: bookshop burglar scenes, reading attempts, vouching for the player.
Scope
- In: npc-clatter, npc-kettleback, npc-snig dialogue data
- In: memory-stage variants for Clatter (0–3 pieces recovered)
- In: goblin standing variants (hostile, wary, friendly)
- Out: goblin camp AI (E11/E14)
- Out: side quest objectives (e39-side-quests-*)
Notes
Goblins cannot read and do not dream (canon). Clatter loses recovered memories if the song swells before he is restored.
Acceptance criteria
- AC-1 unit Given the three dialogue files, when validated, then all pass with zero unreachable nodes.
- AC-2 unit Given clatter.memories = 0..3, when Clatter is spoken to, then a distinct memory-stage line set is used for each value.
- AC-3 unit Given goblins.standing = hostile, wary and friendly, when Kettleback is approached, then the matching opening branch plays.
- AC-4 unit Edge: given the player was captured by goblins while hostile, when brought to Kettleback, then the negotiation branch is available (capture never ends in a game-over).
- AC-5 content Given Snig's lines, when linted, then none depict a goblin successfully reading full sentences before the Bookshop Burglar "lessons" outcome.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #432 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.9Write the Sleepless Abbot's notes, temptations and Cradle dialogue
Description
Why
The second twist — the helpful "friend below" is the abbot — gives the story its Souls-like sting and re-reads earlier notes in a new light.
What
As a player, I want the notes that helped me to turn out to be manipulation so that I feel the twist and want to re-read them. Content: 6–8 "a friend below" notes (genuinely useful), the Act III reveal speech, class-targeted temptations (Somnomancy for sorcerers, duty for knights, the hunt for archers, treasure for thieves), Cradle dialogue for fight/trick/persuade/join, and the persuade-to-rest scene with Clatter.
Scope
- In: npc-abbot dialogue data
- In: note-friend-below-01..08 note items
- In: persuade branch gated on clatter.restored
- Out: wraith combat (E13)
- Out: the Lull spell mechanics (E08/E06)
Notes
Voice: courteous, never raises his voice. Notes must be re-readable after the reveal; include at least two lines that read differently once the player knows.
Acceptance criteria
- AC-1 unit Given the abbot dialogue file, when validated, then it passes and each of the 4 Cradle resolutions sets abbot.fate.
- AC-2 unit Given clatter.restored is false, when the Cradle dialogue is evaluated, then the persuade-to-rest option is not offered.
- AC-3 unit Given each class, when the temptation node is evaluated, then the class-specific temptation line is selected.
- AC-4 content Given the friend-below notes, when validated as items, then there are 6–8 notes and each references only locations that exist in the glossary.
- AC-5 unit Edge: given the sorcerer accepted Lull, when Horn or Mirela is next spoken to, then the "dreamer mark" fact is readable by their dialogue conditions.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #442 (2026-11-13)
area:narrativeclass:allepic:e39milestone:m3size:smw-e39.16Write opening, first-night and ending/epilogue text
Description
Why
The first ten minutes must hook (a child sleepwalking toward a horned shadow) and the ending must reflect what the player actually did.
What
As a player, I want a strong opening and an ending that remembers my choices so that the adventure feels whole. Text for the arrival and bounty board, the first-night sequence (including failure variants), the title/intro card, and the 4 headline endings with epilogue slides assembled from world-state facts (Horn, abbot, Vane, breach, Clatter, goblins, Tansy, Hale), plus optional harbour slides (crate intercepted → name-stone home; Tern sailed → the far-city crate hook). Also the barge-ride travel narration between Briar Glen Quay and Wendmouth.
Scope
- In: narrative/intro and narrative/endings data
- In: epilogue slide conditions
- In: first-night variants: followed, lost her, blocked by wolves
- Out: class-specific arrival text (e39-class-variants)
- Out: cutscene staging/camera (E01/E24)
Notes
Text-only in MVP; each slide ≤ 60 words. The Long Vespers ending must end "on a doorway, not a grave" (bible §4.4).
Acceptance criteria
- AC-1 unit Given the endings data, when evaluated against every combination of the 7 ending facts, then each combination yields exactly one headline ending and ≥ 3 epilogue slides.
- AC-2 unit Given each first-night variant, when triggered, then it ends with the horned-shadow beat.
- AC-3 unit Given each slide, when measured, then it is ≤ 60 words.
- AC-4 unit Edge: given a fact is unset (e.g. Clatter never met), when epilogue slides are selected, then a neutral slide is used and no slide references an unmet character.
- AC-5 manual Given a playtest, when the opening completes, then ≥ 4 of 5 testers can state what they are trying to do next.
Waits on
- mw-e39.4 Script the main quest graph with four endings as quest data done
Closed
Completed in PR #467 (2026-11-14)
area:narrativeclass:allepic:e39milestone:m3size:smw-e39.17Write class-specific arrivals, routes and signature beats
Description
Why
Classes change possibilities; the story must acknowledge each class's fantasy at key beats, not just in combat.
What
As a knight, archer, sorcerer or thief, I want the story to notice who I am so that my class feels like a role, not a stat block. Content per bible §4.3: four arrival reasons (Oath for Hire, Silent Wolves, Letter from the Lamplit Stacks, Commission from the City), class-flavoured interaction prompts at signature beats (formal relief, signal mirror, reading Horn's axe, sneaking into the Fold, plus one Wendmouth hijinks beat per class per bible §4.3), and class-specific climax lines.
Scope
- In: per-class arrival text and journal entries
- In: class-variant prompt/bark text for ≥ 4 signature beats per class
- In: audit sheet mapping each bible §4.3 beat to its content ID
- Out: the mechanics behind class routes (Related: E16 class-specific solutions)
- Out: NPC dialogue branches already owned by e39-dialogue-* items
Notes
The thief's commission comes from "Mr. S—", Vane's buyer (post-MVP hook; do not resolve).
Acceptance criteria
- AC-1 unit Given each class, when a new game starts, then the matching arrival text and first journal entry are selected.
- AC-2 content Given the audit sheet, when checked, then every class moment in bible §4.3 maps to an existing content ID.
- AC-3 unit Given each class, when the class-variant lookup is run for every signature beat, then each class has ≥ 4 unique entries and no beat falls back to generic text unintentionally.
- AC-4 unit Edge: given a class-variant entry is missing for a beat, when looked up, then the "all" fallback is returned and the lint reports a warning.
Waits on
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
- mw-e39.4 Script the main quest graph with four endings as quest data done
Closed
Completed in PR #470 (2026-11-14)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.19Write dialogue for Mags Oakum, Nell Gannet, Big Oona and Old Reed
Description
Why
Wendmouth is where "shenanigans ensue"; its river and dockside characters sell the tone shift from frightened Briar Glen to a loud, cheerful port on the make.
What
As a player, I want the bargewoman, the smuggler queen and the dockside regulars to be characters with their own angles so that the harbour feels rowdy and alive. Mags Oakum (barge rides, river lore, Weeping Adit route, her crate-carrying secret and pocketed horn-coin), Nell Gannet (Drowned Gull, Low Tide Company jobs, Undertow access, the thief-commission drop, relic-skimming secret), Big Oona (arm-wrestling champion, Tidefair barks), Old Reed (Kestrel Lock keeper, tolls in gossip). Voice per bible §5.13.
Scope
- In: npc-mags, npc-nell, npc-oona, npc-reed dialogue data
- In: class-variant greetings and ≥ 2 class-specific branches for Mags and Nell
- In: barge-ride booking lines (morning only; refuses night runs)
- In: thief commission drop branch (deliver / keep / fake coin)
- Out: Undertow stock and prices (Related: E20 economy, E21 bookshops)
- Out: quest objectives (e39-harbor-heist-quest, e39-side-quests-harbor)
Notes
Mags must never run the barge at night. Nell knows what is in bay nine but only reveals it at high disposition or to a thief. Related: Wendmouth level layout (world-content planners).
Integration note: Owner wants the river and Wendmouth in the MVP (P1). It stays optional to the main quest.
Acceptance criteria
- AC-1 unit Given the four dialogue files, when validated by the dialogue validator, then all pass with zero unreachable nodes.
- AC-2 unit Given each class, when Mags and Nell greet the player for the first time, then a class-specific greeting is selected (4 distinct lines each).
- AC-3 unit Given the in-game time is night, when the player asks Mags for a barge ride, then the refusal branch plays and no travel is started.
- AC-4 unit Given the player class is thief and holds the commission horn-coin, when Nell is spoken to, then deliver, keep and fake-coin options are offered and each sets commission.outcome.
- AC-5 unit Edge: given the player has never visited Wendmouth, when any Briar Glen NPC references Nell or Mags, then only hearsay lines are used (no line implies the player has met them).
- AC-6 manual Given a read-through, when compared to bible §5.13 and the tone guide, then harbour voices read as rowdier and more cheerful than Briar Glen without modern slang.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #434 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.20Write dialogue for Inspector Cray, Tereza Maelo, Quell and Hesketh
Description
Why
The customs inspector, the import merchant and Vane's shipping agent tie the harbour to the main plot and give each class a foil (Cray for sorcerers, Tereza for knights, Quell for thieves).
What
As a player, I want the harbour's officials and traders to react to my class and to evidence I carry so that Wendmouth can change what happens to Vane. Inspector Prudence Cray (book confiscation, search-on-sight for sorcerers, romance-novel weakness, bay-nine suspicion, legal seizure route), Tereza Maelo (arms trade, knight duel challenge, foreign spellbook for a story, Tern access via debt of honour), Jasper Quell (nervous denials, collapse under evidence, honest manifest, head-start bargain), Captain Hesketh of the Gilded Tern (crew hiring, shanghai scene).
Scope
- In: npc-cray, npc-tereza, npc-quell, npc-hesketh dialogue data
- In: evidence-gated branches (foundry invoice, honest manifest, dead-clerk seal)
- In: Tereza duel challenge/accept/decline/win/lose branches
- In: Cray disposition states used by Contraband Pages and The Crate for Mr. S—
- Out: duel combat rules (Related: E04 knight combat)
- Out: Tidemarket stock (Related: E20/E21)
Notes
Cray is incorruptible: every bribe attempt fails and is recorded. Cray never confiscates plot-critical or forbidden-plot items. No human character's surname may begin with S (bible §11.1).
Integration note: Owner wants the river and Wendmouth in the MVP (P1). It stays optional to the main quest.
Acceptance criteria
- AC-1 unit Given the four dialogue files, when validated, then all pass with zero unreachable nodes.
- AC-2 unit Given any bribe option to Cray, when chosen, then it fails, sets cray.bribe.attempted and lowers disposition; no branch lets a bribe succeed.
- AC-3 unit Given the player holds the foundry invoice or honest manifest, when Quell is confronted, then the collapse branch is offered and can hand over crate access and the manifest.
- AC-4 unit Given the player class is knight, when Tereza is first met, then the duel challenge is offered; given win or loss, the matching branch sets tereza.duel.result.
- AC-5 content Given the four files, when searched, then no option lets Cray confiscate an item tagged plot-critical.
- AC-6 unit Edge: given Quell was given a head start and has left Wendmouth, when his location is visited, then his absence is handled by a note/bark and no dialogue references him as present.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #435 (2026-11-12)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.21Script the heist side quest The Crate for Mr. S—
Description
Why
A perfectly executed infiltration should feel as satisfying as a hard fight (constitution §5). The bonded-warehouse heist is the harbour's showpiece and links Wendmouth to Vane, Horn and the Mr. S— hook.
What
As a thief (or any class willing to try), I want to intercept Vane's relic crate before the Gilded Tern sails so that I gain evidence against Vane and a gift for the minotaur. Resolutions per bible §6.1 quest 6: stealth heist with ballast swap and re-seal (thief-favoured), paper route via forged/swapped manifest and Cray's legal seizure, archer net-cut into the harbour, knight force (brawl with Low Tide Company and harbour watch), sorcerer freeze-the-pool or Gust-the-hoist, leverage on Quell. Outcomes: name-stone, honest manifest, Mr. S—'s sealed letter; or the Tern sails.
Scope
- In: quest-crate-for-mr-s data
- In: facts: crate.intercepted, crate.method, heist.undetected, manifest.honest.obtained, namestone.obtained, tern.sailed
- In: undetected-heist variant where the Tern sails with ballast
- Out: stealth/disguise mechanics (Related: E09 stealth, E10 thief toolkit)
- Out: warehouse and ship level geometry (Related: world-content planners, E26-style level beads)
Notes
Heist timing is stage-based, not real-time: the Tern sails when Act III begins (bible §11.3). The name-stone is a befriend-Horn trigger consumed by e39-dialogue-horn.
Integration note: Owner wants the river and Wendmouth in the MVP (P1). It stays optional to the main quest.
Acceptance criteria
- AC-1 unit Given the quest file, when validated by the quest validator, then it passes and exposes ≥ 5 resolutions, each setting crate.method.
- AC-2 unit Given the heist completes with heist.undetected = true, when Act III begins, then tern.sailed is set, crate.intercepted is true and no alarm or Low Tide hostility fact is set.
- AC-3 unit Given crate.method = force, when the quest completes, then lowtide.standing becomes hostile and Cray's arrival branch is flagged.
- AC-4 unit Given namestone.obtained, when Horn's first-meeting node is evaluated, then the befriend branch is offered.
- AC-5 unit Edge: given Act III begins before the crate is intercepted, when the quest updates, then it resolves as "sailed", the customs manifest copy remains obtainable and the main quest is unaffected.
- AC-6 manual Given a playtest by a thief, when the undetected heist is completed, then the tester rates it at least as satisfying as the watchtower fight on a 5-point scale.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #448 (2026-11-13)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.22Script harbour side quests The Tidefair and Contraband Pages
Description
Why
Non-thief classes need harbour hijinks of their own, and the sorcerer needs a Lantern Court foil; both quests also unlock the harbour's better gear.
What
As a knight, archer, sorcerer or thief, I want a harbour contest that suits my class and a scrape with the customs house so that Wendmouth is fun for everyone, not only sneaks. The Tidefair: four events any class may enter — arm-wrestling/Gull brawl (knight flavour), the Gull Shoot (archer), the Regatta with unenforced "no magic" rule (sorcerer), the Pocket Race with Cray as a mark (thief); win, cheat, throw a match for Nell's book, or lose gracefully. Contraband Pages: recover impounded books (Mirela's order, the player's own) via failed bribe, romance-novel trade, writ (Vane or forged seal), strongroom theft, inspector impersonation, or bay-nine evidence.
Scope
- In: quest-tidefair and quest-contraband-pages data
- In: facts: tidefair.results.<event>, tidefair.cheated, cray.disposition, books.impounded, mirela.order.delivered
- In: reward hooks: Tereza under-counter stock, foreign spellbook, discounts
- Out: contest mechanics (Related: E04/E05/E06/E10 class systems)
- Out: shop inventories (Related: E20/E21)
Notes
Losing a Tidefair event must still give a warm outcome (Oona buys a drink) — failure creates stories. Cray's disposition from Contraband Pages gates her help in The Crate for Mr. S—.
Integration note: Owner wants the river and Wendmouth in the MVP (P1). It stays optional to the main quest.
Acceptance criteria
- AC-1 unit Given both quest files, when validated, then they pass; The Tidefair has 4 events each with win/cheat/throw/lose outcomes and Contraband Pages has ≥ 5 resolutions.
- AC-2 unit Given each class, when the Tidefair is joined, then the class-flavoured event is highlighted and all 4 events remain enterable.
- AC-3 unit Given a Regatta win using magic while Cray is present, when resolved, then the win is voided, tidefair.cheated is set and a fine branch plays.
- AC-4 unit Given Contraband Pages completes with the bay-nine evidence route, when Cray is next spoken to, then cray.disposition is at least "grateful" and her seizure route in The Crate for Mr. S— is available.
- AC-5 unit Edge: given the player loses every Tidefair event, when the quest completes, then a consolation outcome is granted and Wendmouth reputation does not decrease.
- AC-6 manual Given playtests with a knight, archer and sorcerer, when each plays the Tidefair, then each tester names their class event as a highlight of the harbour.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #459 (2026-11-14)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.23Write harbour rumours and in-world texts (manifest, notice, tolls)
Description
Why
Rumours and documents are how the harbour hands the player leads and evidence; a cargo manifest is a better clue than a quest marker.
What
As a player, I want Wendmouth gossip and paperwork that point at real secrets so that snooping in the harbour pays off. Harbour rumours 13–18 (bible §6.2) with sources (Mags, Nell, Old Reed, Tidemarket gossip), truth values and targets; texts: the Gilded Tern cargo manifest (customs copy and Quell's honest copy), the Lantern Court port notice, the Kestrel Lock toll board, the foundry invoice, the one readable line of Mr. S—'s sealed letter.
Scope
- In: rumor-13..18 data entries
- In: book-/note- entries for the harbour texts with placements
- In: evidence tags on manifest, invoice and seal used by quest conditions
- Out: town rumours 1–12 (e39-rumor-set)
- Out: general books (e39-books-lore)
Notes
Canon numbers must match bible §11.3 exactly (40 horn-coins, 3 carved stones, 1 name-stone; 40 brass trinkets; clerk E. Ardley dead a year). The sea-serpent rumour is false and needs a debunk path.
Integration note: Owner wants the river and Wendmouth in the MVP (P1). It stays optional to the main quest.
Acceptance criteria
- AC-1 unit Given the harbour rumour entries, when validated, then there are 6 rumours (13–18), each with source, truth value and a target resolving to an existing ID.
- AC-2 unit Given the manifest and invoice texts, when checked by the narrative lint canon rules, then all quantities and names match bible §11.3.
- AC-3 unit Given the honest manifest item, when a quest condition checks evidence tags, then it satisfies both Framed in Brass and The Crate for Mr. S— evidence requirements.
- AC-4 content Given the sea-serpent rumour, when reviewed, then it is marked false and has a debunk path (diving to the figurehead or asking Mags).
- AC-5 unit Edge: given the player never visits Wendmouth, when Dot's rumour pool is evaluated, then harbour rumours 13–18 may be heard only as second-hand lines and never mark a harbour target as visited.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #452 (2026-11-13)
area:narrativeclass:allepic:e39milestone:m3size:smw-e39.24Add narrative lint for names, canon facts and dangling IDs
Description
Why
Parallel writers drift: names misspelled, the Long Vespers happening 60 or 62 years ago, notes pointing at rooms that don't exist. An automated pass keeps canon trustworthy without re-reading everything.
What
As a designer, I want a CI lint that checks narrative content against the canon glossary so that contradictions fail the build. Checks: proper-noun spelling against glossary.json (with a near-miss detector), canon numbers from bible §11.3, all ID references resolve (NPCs, quests, facts, locations, items), class-variant coverage warnings, banned-tone list (modern slang, fourth-wall phrases), and British-leaning spelling in player-facing text. Followed by one manual consistency read of all content against the bible.
Scope
- In: src/tools/narrative-lint (runs in CI)
- In: glossary-driven checks
- In: one manual full-content consistency pass with fixes
- Out: schema validation itself (e00-content-pipeline)
- Out: localisation (post-MVP)
Notes
Depends only on the P1 content; P2 content (side quests, rumours, books, spellbook flavour, descriptions, all Wendmouth content) is linted automatically in CI as it lands, and its authors fix lint errors in their own PRs. Tooling code in src/tools needs ≥ 90% coverage per backlog contract §3. Near-miss detector example: "Mirella" or "Kettlebach" → error with suggestion.
Acceptance criteria
- AC-1 unit Given a fixture containing "Mirella", when linted, then an error names the file, line and suggests "Mirela".
- AC-2 unit Given a fixture stating the Long Vespers was "sixty years ago", when linted, then a canon-fact error is reported citing bible §11.3.
- AC-3 unit Given a dialogue node referencing a non-existent fact or location ID, when linted, then a dangling-reference error is reported.
- AC-4 unit Given a fixture with a banned-tone phrase, when linted, then a warning is reported with the matched phrase.
- AC-5 integration Given the full src/content narrative set, when the lint runs in CI, then it completes in ≤ 10 s with zero errors.
- AC-6 manual Given the manual consistency pass, when complete, then every discrepancy found is fixed in content or recorded in the bible change log.
Waits on
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
- mw-e39.5 Write dialogue for Mirela, Dot and Captain Hale done
- mw-e39.6 Write dialogue for Vane, the Pells, Juniper, Oswin and Duggan done
- mw-e39.8 Write dialogue for Clatter, Mother Kettleback and Snig done
Closed
Completed in PR #436 (manual read moved to mw-e39.37 with the content dependencies) (2026-11-12)
area:narrativearea:toolsepic:e39milestone:m3size:mmw-e39.26Story bible: retitle to The Vesper Bell; stone river trade and imperial silver
Description
Why
The bible still opens with the retired 'Merlin's Wrath' evaluation, and the owner has updated the world's economy: stone was the valley's original wealth, quarried and barged down the Wend to the sea for export abroad; silver, later found at the heart of the mine, is now the empire's cash cow and the root of its dominance over neighbouring states.
What
Replace §1 with The Vesper Bell (per ADR-0002); rewrite the world, history, factions and locations sections for the stone trade and imperial silver; make Wendmouth visibly built on stone (quays, breakwater, warehouses and civic buildings of massive dressed stone); fix everything downstream (cast, quests, glossary, canon facts).
Scope
- In: docs/narrative/story-bible.md; style bible harbour visual notes if they conflict
- Out: re-planning asset beads (follow-up if needed)
Acceptance criteria
- AC-1 content No 'Merlin' references remain in the story bible except the codename note.
- AC-2 content The stone trade, the river's role carrying stone to the sea, and imperial silver are consistent across all sections and the canon-facts list.
- AC-3 content The Wendmouth section describes a harbour of big stone structures clearly built from the stone trade.
Closed
Completed in PR #13 (2026-09-28)
area:narrativeepic:e39milestone:m3mw-e39.28Add npc-* creature entries for every principal and minor character in the glossary
Description
From PR #423 (mw-e39.3): dialogue speakers resolve through creature content, but only Dot, Hollis, Juniper, Oswin, Ottilie, Quillon, Garrick and Rooke exist as npc-* creatures. Add greybox creature entries (townsfolk/other factions, placeholder model, dialogue field empty) for every glossary character so the E39 dialogue beads can reference them; ids per docs/narrative/content-conventions.md (first name by default, surname for officials/surname-led cast, npc-kettleback, npc-abbot). AC: glossary characters all resolve to a creature; content:coverage and dialogue:validate pass.
Closed
Completed in PR #424 (2026-11-12)
area:narrativeepic:e39milestone:m3mw-e39.42Write the abbot's 6-8 friend-below notes
Description
Split from mw-e39.9 (its AC-4): the friend-below notes need the note content type from mw-e39.13, which does not exist yet. Write 6-8 note-friend-below-* texts per bible (Hale delivered the first while sleepwalking; don't break hale-watch.json's boots reveal), register their ids in glossary.json texts, validate as notes, each referencing only glossary locations.
Waits on
- mw-e39.13 Write in-game books, notes and inscriptions done
Closed
Completed in PR #447 (2026-11-13)
area:narrativeepic:e39milestone:m3mw-e39.10Script side quests The Bookshop Burglar and The Captain's Boots
Description
Why
Town side quests make Briar Glen dense and reward talking to people; both feed the main quest (Snig vouches for you; Hale backs your testimony).
What
As a player, I want small town mysteries with several honest solutions so that my class and choices matter even in town. The Bookshop Burglar: stake out, track to the well, find Snig; resolutions turn in / broker lessons / sell the book / Mortimer scare. The Captain's Boots: follow Hale at night; resolutions wakeful tea / wrong draught / tie down / bell on door / follow route; then tell the town or keep the secret.
Scope
- In: quest-bookshop-burglar and quest-captains-boots data
- In: world-state facts they set (snig.lessons, hale.secret.kept, route.briarwood.revealed)
- Out: NPC dialogue text (e39-dialogue-town-core, e39-dialogue-labyrinth-folk)
Notes
Picking the sleep draught instead of wakeful tea must create a story (Hale walks into the Deepworks and must be retrieved), not a fail state. Bible §6.1.
Acceptance criteria
- AC-1 unit Given both quest files, when validated by the quest validator, then they pass and each has ≥ 3 resolutions.
- AC-2 unit Given each Bookshop Burglar resolution, when completed, then the documented goblin-standing and snig facts are set.
- AC-3 unit Edge: given the player gives Hale the sleep draught, when night falls, then the "Hale walks in" branch starts and the quest remains completable.
- AC-4 unit Given The Captain's Boots completes with the secret kept, when Act III testimony is evaluated, then Hale backs the player.
- AC-5 manual Given a playtest, when each quest is completed with two different solutions, then both feel intentional and neither is flagged as a "failure" by the tester.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #468 (2026-11-14)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.11Script The Watch Still Stands, Framed in Brass, Name on the Bones
Description
Why
These quests carry class-specific solutions (knight relieves the Sentinels, archer rings the cord), Vane's exposure and the game's most emotional thread (Clatter remembering).
What
As a player, I want wilderness and dungeon side quests with class-flavoured solutions so that exploring off the main path changes the story. The Watch Still Stands (watchword / knight formal relief / archer bell cord / sorcerer dispel / thief stealth / smash). Framed in Brass (free Duggan by force, trade the kettle, persuade, take the kit; combine with ledger — and optionally Quell's foundry invoice from Wendmouth — to expose Vane). The Name on the Bones (psalter, pear tree, sister's letter; sorcerer memory-read risk; join or rest).
Scope
- In: quest-watch-still-stands, quest-framed-in-brass, quest-name-on-the-bones data
- In: facts: sentinels.relieved, duggan.freed, vane.exposed, clatter.memories, clatter.restored, clatter.choice
- Out: Sentinel/goblin AI (E11/E13)
- Out: pear tree bloom VFX (E29)
Notes
A failed sorcerer memory-read permanently removes one memory piece option but must still allow completion via the remaining pieces plus an alternate (bible §6.1).
Acceptance criteria
- AC-1 unit Given the three quest files, when validated, then they pass and each has ≥ 3 resolutions.
- AC-2 unit Given the player class is knight, when The Watch Still Stands is evaluated at the Sentinels, then the formal-relief resolution is available; given other classes, it is not.
- AC-3 unit Given vane.ledger.obtained and duggan.freed, when Framed in Brass completes, then vane.exposed can be set.
- AC-4 unit Edge: given a failed memory-read, when The Name on the Bones is re-evaluated, then it remains completable and clatter.memories max is reduced by exactly 1.
- AC-5 unit Given clatter.restored, when the join/rest choice is made, then clatter.choice is set and the main quest Cradle/Climb variants read it.
Waits on
- mw-e23.2 Implement quest state machine with branching and multi-resolution done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #472 (2026-11-14)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.12Write the rumour set with sources, truth values and targets
Description
Why
The constitution promises that NPC rumours lead somewhere; rumours are the town's map of secrets.
What
As a player, I want rumours that are true, half-true or false and point at real places so that listening pays off. Encodes town/wilderness rumours 1–12 in bible §6.2 (plus up to 6 more; harbour rumours 13–18 live in e39-harbor-rumors-texts) with source NPC(s), truth value, target location/secret, unlock conditions, and follow-up lines when the player confirms or debunks one with Dot.
Scope
- In: src/content/rumors data
- In: truth value (true | half | false) and target reference per rumour
- In: Dot trade-back lines for confirmed/debunked rumours
- Out: rumour UI/journal (E22)
- Out: Dot's general dialogue (e39-dialogue-town-core)
Notes
Every target must resolve to a glossary location, quest or fact. Goblin rumours are only available after goblin standing ≥ wary.
Acceptance criteria
- AC-1 unit Given the rumour data, when validated, then there are 12–18 rumours and each has a source, truth value and target that resolves to an existing ID.
- AC-2 unit Given goblins.standing = hostile, when rumour availability is evaluated, then no goblin-sourced rumour is offered.
- AC-3 unit Given the player has visited a rumour's target, when Dot is spoken to, then the confirm/debunk follow-up line is available once.
- AC-4 content Given the set, when counted, then at least 2 rumours are false or half-true and each false rumour has a debunk path.
- AC-5 unit Edge: given all rumours are already known, when Dot offers to trade, then a fallback line plays and no coin is taken.
Waits on
- mw-e22.5 Implement dialogue graph runtime with conditions and effects done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #474 (2026-11-14)
area:narrativeclass:allepic:e39milestone:m3size:smw-e39.13Write in-game books, notes and inscriptions
Description
Why
"Bookshelves can contain books." Lore should come from contradicting voices in objects and texts, Souls- and Elder Scrolls-style, and the bookshop must feel stocked.
What
As a player, I want to find short, readable books and notes that reveal the world in pieces so that curiosity is rewarded. Full texts for the bible §7.2 books (Vesperine Hours, Traveller's Account, On the Silence of Goblins, The Ox Beneath the Hill, Keeper's Log, Abbot's Letters, Nine Sleeps lore pages, Deepworks Ledger), 8–12 additional short books for the Lamplit Stacks and shelves, notes (Hale's list, Duggan's instructions, sleepwalker scrawls), and statue/wall inscriptions.
Scope
- In: book-* data files with title, author, text, location(s)
- In: note-* and inscription-* data
- In: ≥ 3 independent sources for each major fact (Long Vespers, Horn, the bell)
- Out: spellbook teaching texts (e39-spellbook-flavor)
- Out: bookshop stock rules (e21-bookshop-system)
Notes
Length 100–300 words per book (readable in under a minute); the Vesper hymn text must be identical everywhere it appears.
Acceptance criteria
- AC-1 unit Given all book, note and inscription files, when validated, then they pass and each has ≥ 1 placement location from the glossary.
- AC-2 unit Given each book text, when measured, then word count is between 40 and 400.
- AC-3 unit Given the Vesper hymn, when every occurrence is compared, then all occurrences are byte-identical.
- AC-4 content Given the lore map in the file header, when checked, then the Long Vespers, Horn's role and the bell each have ≥ 3 independent sources (book, object, person).
- AC-5 content Edge: given the damaged-book convention, when a book is flagged damaged, then its text includes the missing/illegible markers the UI renders.
Waits on
- mw-e00.18 Load schema-validated content with a per-entry test harness done
- mw-e39.3 Set narrative content layout, ID scheme and class-variant tagging done
Closed
Completed in PR #444 (2026-11-13)
area:narrativeclass:allepic:e39milestone:m3size:mmw-e39.27Sync backlog and asset plans with the stone-trade canon
Description
Why
Story bible v0.3 (mw-e39.26) made glenstone the valley's original export and Wendmouth a monumental stone harbour: the Ore Wharf is now the Stone Wharf, the Ore Stair the Stone Stair, ore barges are stone barges, and the Deepworks is a glenstone mine with silver galleries. About 28 beads, plan/world.json, plan/assets-harbor.json, plan/world-asset-needs.md and plan/id-map.json still use the old terms, including 'stilted tarred warehouses' in the harbour art briefs.
What
Update bead descriptions and plan files to the new canon. Decide whether to rename keys and asset ids (e40-ore-wharf, env-wendmouth-ore-wharf-tidefair-01 and friends) or keep them as stable ids with updated titles and descriptions. Refresh harbour asset briefs so art prompts ask for monumental glenstone quays, breakwater, warehouses and Customs House (style bible §2.2 Wendmouth row).
Scope
- In: bd descriptions, plan/*.json, plan/*.md, id-map if keys change
- Out: story and style bible text (done in mw-e39.26)
Acceptance criteria
- AC-1 content No bead or plan file describes Ore Wharf, Ore Stair, ore barges or stilted warehouses except as ids deliberately kept stable.
- AC-2 content node scripts/load-plan.mjs --check passes after the edits.
Closed
Completed in PR #31 (2026-09-28)
area:narrativeepic:e39milestone:m3mw-e39.32Set wendmouth.visited when the player first reaches Wendmouth
Description
wendmouth.visited (fact group harbor-folk-dialogue) gates Dot's hearsay about Mags Oakum and Nell Gannet (mw-e39.19 AC-5). Nothing writes it yet: the Wendmouth scene arrival (or barge travel) must set it.
Closed
Completed in PR #451 (2026-11-13)
area:narrativeepic:e39milestone:m3mw-e39.40Build the epilogue slideshow that reads quest-briar-glen-epilogue
Description
mw-e39.4 ships quest-briar-glen-epilogue: while active, QuestRuntime.activeObjectives lists the slides for the chosen ending (objective ids vr-*, lw-*, hs-*, lv-*, harbor-*). Build the slideshow, emit epilogue-slide-<id> per slide and set epilogue.finished. Prose is mw-e39.16.
Closed
Completed in PR #469 (2026-11-14)
area:narrativeepic:e39milestone:m3mw-e39.44Add inventory items for the story books (book-* texts from mw-e39.13)
Description
mw-e39.13 wrote the text of 18 story books in src/content/data/book/ (id = item id). Each needs its inventory item in src/content/data/item/<id>.json: category book, icon (icon-manifest placeholder), value, legality/rarity (Nine Sleeps forbidden and unique, never sold), book.pages[n].textKey = bookPageKey(id, n) (book.<id>.page-<n+1>), title/author equal to the text, condition damaged for the ones flagged damaged. checkReadables (src/content/readable-checks.ts) enforces the pairing at load. Do it with the placement beads (mw-e24.16, mw-e26.22, bookshop stock) or ahead of them. AC: every book text has a loading item; pnpm content:coverage passes.
Closed
Completed in PR #453 (2026-11-13)
area:narrativeepic:e39milestone:m3
E40Wendmouth HarborWendmouth, the rowdy harbour town at the mouth of the River Wend, is an optional-but-rewarding MVP level where shenanigans ensue: sneakiness is richly rewarded (customs house, bonded warehouse, manifests, rooftops, rigging, crowds, the Undertow), every other class gets hijinks (brawls, arm-wrestling, duels, the Gull Shoot, the Regatta, freezing the harbour pool), and everyone can equip themselves better than in Briar Glen (the Tidemarket and the Undertow). The Crate for Mr. S— heist, the Tidefair and Contraband Pages play out here.2/26 done · 8%
M3 MVP content 2
mw-e40.1Block out Wendmouth harbour in grey-box with every named place
Description
Why
Wendmouth is where shenanigans ensue; its layout decides every sightline, rooftop and heist route.
What
As a player, I want a crooked, crowded harbour I can read in one visit so that I can plan mischief.
Grey-box (~200×160 m): Stone Wharf (great stepped quay, barge berth, block yards, open Tidefair flagstones, and the gated Silver Quay at its far end — seen, not entered), the columned Customs House, the vaulted Bonded Warehouse, the fish market over the Undertow, the Tidemarket, net lofts, the breakwater with the Drowned Gull at its end, the pool with the Gilded Tern at anchor, towpath and barge arrival points. Massing is monumental glenstone (stepped quays, block breakwater, vaulted warehouses) with timber net lofts and sheds perched on the stone; the pool and quay edges are standing-water volumes.
Scope
- In: Harbour scene, building shells and entries, navmesh, water volumes, connectivity nodes, arrival spawns
- Out: Per-place content (other E40 items)
- Out: Art (e40-harbor-art-dress)
Notes
Wendmouth is optional (bible §3.6): no main-quest edge may require it. The river does not flow in the sim; the harbour pool is standing water.
Acceptance criteria
- AC-1 content Given the scene, when validated, then every bible §3.6 place exists as a named node linked in connectivity.json.
- AC-2 content Given connectivity.json, when the main-quest critical path is computed, then no required edge passes through a Wendmouth node (edge: optional town).
- AC-3 integration Given the navmesh, when queried, then every building entry is reachable from both the barge berth and the towpath arrival.
- AC-4 perf Given the grey-box harbour from the Stone Wharf, when measured, then frame time p95 <= 16.7 ms (steady 60 fps) on the reference machine (MacBook Pro M1 Pro, Chrome, High preset, 1440p-equivalent).
Waits on
- mw-e00.21 Create greybox testbed scene, level kit, scene loader and debug camera done
- mw-e01.11 Connect scenes with area transitions and named spawn points done
- mw-e11.4 Navmesh generation and deterministic pathfinding for creatures done
- mw-e24.1 Design the world map: areas, loops, shortcuts and ability gates done
Closed
Completed in PR #451 (2026-11-13)
area:contentepic:e40milestone:m3mw-e40.5Run data-defined contests with entry, scoring, cheating and bets
Description
Why
The Tidefair and the Drowned Gull need contests; no other plan owns a contest runner, and bespoke scripting per event would not scale.
What
A small data-driven contest runner in src/sim built on the condition/effect DSL and quest objective trackers: contest definitions {id, entry conditions, fee, participants, scoring from sim events, win/lose conditions, cheat rules (condition → detected-if-observed-by), betting book (odds, throw-the-match option), rewards/effects}. Emits contest.started/ended with placement.
Scope
- In: Contest schema + validator, runner, cheat detection via perception of watchers, betting hook, outcome effects
- Out: Individual events (Tidefair items, e40-drowned-gull)
- Out: Quest graphs (e39-side-quests-harbor)
Notes
Cheating is always possible and always has a consequence (bible §6.1 quest 7). Losing gracefully is a valid outcome with its own effect.
Acceptance criteria
- AC-1 unit Given a contest with a scoring rule "hits on target tag gull", when 3 matching hit events occur, then the player score is 3 and ties break by earliest completion.
- AC-2 unit Given a cheat rule "magic used" and Cray perceiving the cast, when it occurs, then contest.cheatDetected fires and the disqualify effect applies; unobserved, no detection (edge).
- AC-3 unit Given the player accepts Nell's bet to throw the match, when they lose, then the payout effect applies; if they win, the betting-book grudge effect applies.
- AC-4 content Given contest data, when validated, then every contest has entry, win and lose conditions and references existing effects.
Waits on
- mw-e11.5 Creature perception: sight cones, hearing and special senses done
- mw-e22.2 Create shared typed condition/effect DSL for dialogue, quests, shops done
- mw-e23.3 Bind quest objective kinds to sim events done
- mw-e27.1 Store persistent world facts in a typed deterministic fact store done
Closed
Completed in PR #454 (2026-11-13)
area:simepic:e40milestone:m3
E41CI & Release AutomationA PR gets a trustworthy verdict in about 6 minutes, only gates that must run per PR run per PR, the rest run nightly and5/22 done · 23%
Unscheduled 5
mw-e41.1Rebalance e2e: isolate cross-browser audio and shard chromium by duration
Description
Why
The slowest e2e shard (600-810 s) sets the whole PR's wall clock, and every shard pays about 40 s to install three browsers and PulseAudio although only audio.spec.ts runs in Firefox and WebKit.
What
Move the Firefox and WebKit audio run (with PulseAudio) into its own job, run the chromium project in weighted shards, and keep the e2e-smoke aggregator so required checks do not change.
Scope
- In: ci.yml jobs, playwright.config.ts projects, a duration-weighted shard list, a script to regenerate it
- Out: changing which specs run per PR (see the nightly and path-aware beads)
Notes
Playwright --shard splits by file order; use explicit per-shard spec lists from e2e/shards.json instead. Timings from run 37179655581: shards 810/488/674 s.
Acceptance criteria
- AC-1 e2e Given the CI workflow, when a PR runs, then Firefox and WebKit audio specs run in one dedicated job and the chromium shards install and run only chromium.
- AC-2 unit Given a committed e2e/shards.json built from Playwright durations, when specs are listed, then each chromium shard's summed duration is within 20% of the mean.
- AC-3 e2e Given 5 consecutive PR runs, then the slowest e2e job is at most 420 s and the required check names are unchanged.
Closed
AC-1/AC-2 met in PR #214; AC-3 (<=420 s) unreachable by sharding alone because perf-budget (~510 s) and coverage-gate (~400 s) set the floor; that goal moves to mw-e41.5. PR #252 closed unmerged. (2026-10-24)
mw-e41.2Run the unit suite once: coverage-gate is the only test runner
Description
Why
unit and coverage-gate each run the same 534 files and 5,133 tests (about 300 s each), doubling runner cost for one signal.
What
Drop pnpm test from the unit job and have coverage-gate run test:coverage, which also feeds content:coverage and content:loot. The unit job keeps the checks that need no second test run. Consider vitest --shard with merge-reports so the single run takes about 2 minutes.
Scope
- In: ci.yml, package.json scripts, coverage reporter wiring
- Out: changing coverage thresholds (never lowered)
Notes
Required check names
unitandcoverage-gatemust stay, so keep both as jobs. Benchmarks move per the nightly bead.Acceptance criteria
- AC-1 e2e Given the CI workflow, when a PR runs, then pnpm test runs exactly once across all jobs.
- AC-2 unit Given the coverage run, when it finishes, then the content coverage reporter output used by pnpm content:coverage exists.
- AC-3 e2e Given a PR with a failing unit test, then coverage-gate fails and names the test.
Closed
Completed in PR #214 (2026-10-04)
mw-e41.5Define per-PR, nightly and release gates and move the slow ones off PRs
Description
Why
perf-budget (about 510 s, warn-only, GPU-less runners), vitest bench, cross-browser audio and wall-clock load budgets add runner time and flake noise to every PR while catching things that change slowly.
What
Write docs/ci-gates.md classifying every gate. Create a nightly workflow (and a reusable workflow_call) that runs the nightly and release tier; PRs keep lint, typecheck, unit and coverage, chromium e2e, playthrough when sim, game, content or render change, build, and the security and PR hygiene checks.
Scope
- In: ci.yml, new nightly.yml, docs/ci-gates.md, DoD wording in docs/backlog-contract.md
- Out: the release workflow itself (see the release gate bead)
Notes
Owner decision needed on the tier of vitest bench and cross-browser audio; the proposal is nightly plus release. perf-budget stays the baseline producer on main pushes.
Acceptance criteria
- AC-1 unit Given docs/ci-gates.md, then every CI job and test project is listed with its tier (per-PR, nightly, release) and a reason.
- AC-2 e2e Given a PR, then perf-budget, vitest bench, Firefox and WebKit audio and wall-clock load-budget specs do not run.
- AC-3 e2e Given the nightly workflow on main, then those gates run and a failure opens or updates one issue.
- AC-4 unit Given backlog-contract.md section 3 DoD, then items 3 and 4 name the tiers instead of listing perf as a per-PR gate.
Waits on
- mw-e41.1 Rebalance e2e: isolate cross-browser audio and shard chromium by duration done
- mw-e41.2 Run the unit suite once: coverage-gate is the only test runner done
Closed
Completed in PR #263 (2026-10-24)
mw-e41.8Move wall-clock load-budget assertions out of PR e2e specs
Description
Why
slice-saves 'within 3 s', title 'within 10 s' and scenes timeouts failed repeatedly on shared GPU-less runners; they are jitter, not regressions. See also mw-e00.33.
What
Move budgets from the e2e specs into the perf suite (nightly and release tier), keeping functional assertions in e2e.
Scope
- In: e2e/slice-saves.spec.ts, e2e/title.spec.ts, e2e/scenes.spec.ts, e2e/perf
- Out: changing the budgets themselves (contract section 1)
Notes
Include the playthrough's 30 s data-saved-game wait that failed in run 37201477872 if it proves load-sensitive.
Acceptance criteria
- AC-1 e2e Given the PR e2e suite, then no spec asserts a wall-clock budget such as load-to-playable within 3 s or 10 s.
- AC-2 e2e Given the perf suite, then those budgets are asserted there against the contract hardware baseline.
- AC-3 e2e Given 20 consecutive CI runs, then none fail on a timing assertion in e2e.
Closed
Completed in PR #234 (2026-10-04)
mw-e41.22Regenerate e2e/shards.json: shard 4 hit the 15-minute job limit
Description
Today added many e2e specs (spell-cast, spell-hotbar, burning-hazard, bridge-sorcerer, bridgeward-college, book study...) that e2e/shards.json doesn't weight, so they fall back to defaults and shard 4 ran 15m18s and was cancelled on PR #280 (run 40124541438). Regenerate weights from recent main runs with scripts/e2e-shards.ts (multi-report averaging from closed PR #252 is worth reviving), and check no shard is near the job timeout.
Acceptance criteria
- AC-1 every e2e spec has a weight; slowest chromium shard under 10 min on 3 consecutive main runs
Closed
Completed in PR #289; AC-1 met: slowest chromium shard 9, 9 and 8 min on main runs 42411093899, 42412812207, 42413910526 (2026-11-07)
E42Open Valley & Town EconomyA grand-feeling valley outside the start: a cliff overlook above Briar Glen and the mountain (backdrop art, tight walkable paths), a bridge over the Wend to enter the village, a river that sinks armoured players, shops (clothes, staves, swords, armor, helmets, scrolls, potions, ingredients, breakable jars, arrows, bows, food, knives, gloves, inn bed), and enough varied skeletons whose gold and gear drops fund real purchases.33/42 done · 79%
M3 MVP content 33
mw-ju8.1Spike: valley backdrop layers and tight-path terrain within perf budgets
Description
Why
Grand feel via backdrops + tight paths (owner decision) must hold High/Low budgets (hardware baseline).
What
Greybox valley scene with a cliff overlook, a few-hundred-metre walkable path corridor, layered backdrop cards (mountain, castle, village silhouette) generated via Higgsfield, fog depth, and measured frame time on both presets. Report findings in docs/design/valley-spike.md.
Acceptance
- AC-1 perf Given the overlook scene, when measured, then High p95 <= 16.7 ms and Low meets its budget.
- AC-1b manual Given the overlook, when viewed, then village, mountain and castle read as a continuous vista.
- AC-2 manual Given the doc, then it recommends terrain approach (heightfield vs kit + backdrops) with numbers.
Closed
Completed in PR #224 (reference-machine perf split to mw-ju8.2) (2026-10-04)
area:contentarea:renderepic:e42milestone:m3mw-ju8.2Measure valley scenes on the reference M1 Pro and fix the backdrop floor band
Description
Why
Split from mw-ju8.1: the spike's frame-time AC could only be measured on software GL.
What
Run pnpm perf:ref on valley-01..03 (High and Low), add the valley scenes to perf/perf-budgets.json, and fix the flat sky-colour band where the backdrop card meets the floor.
Acceptance
- AC-1 perf Given each valley scene on the reference M1 Pro, when measured, then High p95 <= 16.7 ms and Low meets its budget.
- AC-2 manual Given valley-01, when viewed, then no flat colour band shows between backdrop and floor.
Closed
Completed in PR #347. Valleys p95 about 3.3 ms High, 3.0 ms Low on the M1 Pro. AC-2 checked with stand-in art; real-art check noted on mw-e37.282. (2026-11-09)
area:contentarea:renderepic:e42milestone:m3mw-ju8.3Author the shop item catalogue: weapons, armor, clothes, helmets, gloves, knives, staves, bows and arrows
Description
Why
Shops need real stock. Owner list 2026-10-04: clothes, staves, swords, armor, helmets, gloves, knives, bows, arrows.
What
Item JSON under src/content/data/item with tiered values from docs/design/economy.md (mw-e20.1); armor weights follow ADR-0003; each item has a sell value.
Acceptance
- AC-1 content Given the catalogue, when validated, then every item passes the item schema and has a value within its tier's price band.
- AC-2 content Given the catalogue, then it covers each category in the owner list with at least 2 tiers.
Closed
Completed in PR #231 (2026-10-04)
area:contentepic:e42milestone:m3mw-ju8.4Author consumable stock: food, potions, potion ingredients, breakable jars and spell scrolls
Description
Why
Owner list 2026-10-04. Potions are buy-and-use only; brewing from ingredients is later (new bead when scheduled).
What
Food and potion items with effects via existing consumable/condition systems; ingredient items (stock only, no brewing); breakable jar containers that use the breakables system and spill/break on impact; spell scroll items that cast a known spell once (check e06/e07 for what exists and file gaps).
Acceptance
- AC-1 content Given the stock, when validated, then all items pass the schema.
- AC-2 integration Given a potion jar thrown or dropped hard, when it breaks, then it emits the break event and its contents are lost.
- AC-3 integration Given a scroll, when read, then its spell casts once and the scroll is consumed.
Closed
Completed in PR #232 (scrolls are stock only; read-and-cast is mw-ju8.17) (2026-10-04)
area:contentepic:e42milestone:m3mw-ju8.5Build the village shop row: smith, armourer, clothier, apothecary, general store and fletcher with merchant data
Description
Why
Shops where the starting gold goes. Uses merchant schema (mw-e20.2) and stock from the catalogue beads.
What
Merchants (identity, specialty, stock rules, buy list) and a greybox shop row in a village scene past the bridge, each shop with a counter interactable that opens the shop UI. Full Briar Glen town stays in E24.
Acceptance
- AC-1 content Given all merchant files, when validated, then every stocked item exists and every shop type in the owner list is covered.
- AC-2 e2e Given the village scene, when the player talks to each merchant, then the shop opens.
Waits on
- mw-e20.2 Define merchant schema with identity, specialty and stock rules done
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
- mw-ju8.3 Author the shop item catalogue: weapons, armor, clothes, helmets, gloves, knives, staves, bows and arrows done
- mw-ju8.4 Author consumable stock: food, potions, potion ingredients, breakable jars and spell scrolls done
Closed
Completed in PR #235 (2026-10-05)
area:contentepic:e42milestone:m3mw-ju8.6Add the Sleeping Ox bed: pay for a room, sleep and wake rested
Description
Why
Owner list: a bed at an inn. Pulls a thin slice of mw-e24.4 / mw-e27.13 forward.
What
Innkeeper merchant service (mw-e20.9) selling a night's rest; sleeping passes world time, restores health/stamina and autosaves.
Acceptance
- AC-1 integration Given enough gold, when the player buys a room and sleeps, then gold drops by the price, time advances to morning and health is full.
- AC-2 integration Given too little gold, when asked, then the purchase is refused with no state change.
Waits on
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
Closed
Completed in PR #236 (2026-10-05)
area:contentarea:gameepic:e42milestone:m3mw-ju8.7Sell gear and loot to merchants at buy-back prices
Description
Why
Skeleton drops should be worth gold. Depends on transactions (mw-e20.4).
What
Merchant buy lists and sell-value rules (condition, stolen flag from e20.6 later); sold items enter merchant stock.
Acceptance
- AC-1 unit Given an item a merchant buys, when sold, then gold rises by the buy-back price and the item leaves the pack.
- AC-2 unit Given an item outside the buy list, then the sale is refused.
Waits on
- mw-e20.4 Implement merchant buy/sell transactions with persistent stock done
Closed
Completed in PR #240 (2026-10-05)
area:contentarea:simepic:e42milestone:m3mw-ju8.8Generate Marsh's General Store concept art via Higgsfield: exterior, ground-floor shop, upstairs home, shopkeeper, props
Description
Why
Owner 2026-10-04: first building the player sees on entering town; ground floor is the shop, upstairs is where the shopkeeper lives. Placeholder names Ottilie Marsh / Marsh's General Store (pending bible entry).
What
Higgsfield concept set from the style-bible preamble: exterior from the bridge, ground-floor interior, upstairs living quarters, shopkeeper turnaround + portrait, shop prop sheet. Files in assets/_incoming/concept-marsh-general-store-01 for owner approval.
Acceptance
- AC-1 manual Given the set, when the owner reviews, then each image is approved or sent back for regeneration.
Closed
Completed in PR #228 (2026-10-04)
area:artarea:contentasset:conceptepic:e42milestone:m3mw-ju8.9Build Marsh's General Store: two-floor interior, shopkeeper NPC and counter interaction
Description
Why
First building inside town; holds the first working shop (ground floor) and the shopkeeper's home (upstairs).
What
Greybox two-floor interior from the e00 kit (stairs between floors, counter, shelves, barrels, living quarters), the shopkeeper NPC placed behind the counter with an idle routine, counter interactable that opens the shop UI. Depends on transactions (mw-e20.4) and shop UI (mw-e20.10). Art-dress from approved concept set.
Acceptance
- AC-1 e2e Given the entrance door, when the player enters, then the interior loads and the shopkeeper is behind the counter.
- AC-2 integration Given the counter, when used, then the shop opens with the merchant's stock.
- AC-3 integration Given the stairs, when climbed, then the upstairs room is reachable and the shopkeeper's bed and table exist.
Closed
Completed in PR #230 (2026-10-04)
area:contentepic:e42milestone:m3mw-ju8.10Owner generates Marsh's General Store and Ottilie music in Suno (shop theme, upstairs, hum, stinger bundle)
Description
Why
Ottilie Marsh (placeholder name) runs Marsh's General Store, the first building inside Briar Glen. The audio bible (docs/audio/audio-bible.md) has no voice acting in the MVP, so her sound identity is music: shop theme, upstairs variant, a wordless hum, and three short cues. Her spoken lines stay as text.
Owner instructions (about 5 minutes in Suno, Pro tier)
Paste the Style field and Exclude field as given. Lyrics field = structure tags only. Generate each, pick the best take, download WAV to assets/_incoming/music/<asset-id>.wav. Downloads are scarce (Pro annual cap): generate freely, DOWNLOAD ONLY THE PICKS. Reject any take with intelligible words. Record the Suno model version and date.
Shared preamble (audio bible 4.1, verbatim):
instrumental, dark folk fantasy RPG score, medieval and early-baroque, hurdy-gurdy drone, nyckelharpa melody, solo cello, low string ensemble, wordless choir oohs, frame drum, bodhran, hammered dulcimer, celtic harp, wooden flute, modal harmony, acoustic, organic, intimate stone-hall reverb, mysterious yet warm and hopeful, cinematic but restrained, no vocals
Shared exclude (verbatim):
vocals, lyrics, rap, pop, EDM, dubstep, trap, drum kit, electric guitar, synth, synth pad, 808, trailer braams, orchestral hits, lo-fi, autotune
1. music-marsh-general-store (anchor; plays inside the ground-floor shop, diegetic)
Length 2:00-3:00, clean loop region over 60 s. Instrumental ON.
STYLE FIELD: <preamble>, D dorian, 92 bpm, 6/8, cosy bustling shop, unhurried, hammered dulcimer lead, celtic harp arpeggios, wooden flute answering phrases, soft frame drum and shaker, pizzicato cello, village warmth, gentle rising fifth then stepwise descending folk melody, loopable, no vocals
EXCLUDE: <shared exclude>
LYRICS FIELD:
[Intro]
[Instrumental]
[Main theme]
[Instrumental break]
[Main theme]
[Outro]
2. music-marsh-general-store-upstairs (the shopkeeper's home, same key as the anchor)
Length 2:00-2:30, loopable. Instrumental ON. Generate by Cover/Extend from the picked take of 1 if the UI allows, to keep key and melody.
STYLE FIELD: <preamble>, D dorian, 72 bpm, 6/8, intimate homely evening, kettle-warm, solo celtic harp and soft solo cello, sparse hammered dulcimer, slow breathing pace, the shop theme melody played slowly and softly, fireside stillness, loopable, no vocals
EXCLUDE: <shared exclude>
LYRICS FIELD:
[Intro]
[Instrumental]
[Main theme, slow]
[Instrumental break]
[Outro]
3. music-ottilie-hum (wordless working hum; plays under her dialogue and at the counter)
Length 30-45 s, loopable. TURN INSTRUMENTAL OFF for this one (a hum is a voice) but the lyrics field must hold no words. Reject any take with intelligible words.
STYLE FIELD: solo female humming, wordless, mm and hm only, close-miked, warm, mature low-mid voice, unaccompanied with one soft hammered dulcimer drone, D dorian, 92 bpm, 6/8, the shop theme melody hummed absent-mindedly while working, medieval folk, intimate dry room, no words, no lyrics
EXCLUDE: lyrics, words, singing, choir, pop, rap, EDM, drum kit, electric guitar, synth, autotune, reverb wash, vibrato diva
LYRICS FIELD:
[Hummed melody, wordless: mm, hm, mm]
[Hummed melody, wordless: mm, hm, mm]
4. music-stinger-marsh-bundle (three cues in one generation; trim later)
Length 40-60 s with a clear silent gap between cues. Instrumental ON. One generation, one download.
STYLE FIELD: <preamble>, D dorian, 92 bpm, three separate short cues with a few seconds of silence between each: (1) greeting: a bright small shop-bell chime flourish on celtic harp and wooden flute, 3 seconds, welcoming; (2) sale made: a warm resolving cadence on hammered dulcimer and pizzicato cello with a soft coin-purse jingle, 3 seconds, satisfied; (3) farewell: a gentle descending two-phrase figure on solo flute and harp, 4 seconds, friendly, clear ending
EXCLUDE: <shared exclude>
LYRICS FIELD:
[Instrumental]
[Cue 1: greeting]
[Silence]
[Cue 2: sale made]
[Silence]
[Cue 3: farewell]
Acceptance
- AC-1 manual Given the four takes, when the owner listens, then each is approved or sent back with a note.
- AC-2 manual Given takes 1 and 2, then they share key and melody.
- AC-3 manual Given take 3, then it contains no intelligible words.
Follow-up
Integrate bead wires the approved takes into the adaptive music/cue system, records CREDITS (Suno tier and date) and approvals, and trims the bundle into three stingers. Prompt file to commit as assets/prompts/music/music-marsh-general-store.md when approved.
Closed
All Marsh takes picked by the owner and in assets/_incoming/music (hum integrated in PR #287) (2026-11-07)
area:contentarea:musicasset:musicepic:e42milestone:m3needs-humanmw-ju8.11Integrate approved Marsh's General Store and Ottilie music
Description
Wire approved takes into the adaptive music and cue systems, trim the stinger bundle into greeting, sale and farewell cues, add CREDITS rows (Suno tier and date) and approval records, commit the prompt file. Blocked by the Suno generate bead and the interior bead mw-ju8.9.
Waits on
- mw-ju8.10 Owner generates Marsh's General Store and Ottilie music in Suno (shop theme, upstairs, hum, stinger bundle) done
- mw-ju8.9 Build Marsh's General Store: two-floor interior, shopkeeper NPC and counter interaction done
Closed
Completed in PR #288 (2026-11-07)
area:contentarea:musicasset:musicepic:e42milestone:m3mw-ju8.12Build the Miners' Bridge and the Bridge Watch post: day and night guards who turn away monsters and hooligans
Description
Why
Owner canon 2026-10-04 (story bible §3.5): the bridge is the only way into town; guards day and night keep out hooligans and monsters.
What
Bridge greybox from the kit joining valley-03 to the town (needs area transitions mw-e01.11), a guard post at the town end, Bridge Watch guards on day and night schedules (e27 clock), a gate rule: creatures with hostile faction do not cross; armed hostile players are challenged. Shift times and the armoured-stranger policy are open design questions to settle in the bead.
Acceptance
- AC-1 integration Given a hostile creature chasing the player, when it reaches the post, then the guards engage it and it does not enter town.
- AC-2 integration Given day and night, then at least one guard is at the post in both.
- AC-3 e2e Given the player crosses the bridge, then the next scene is the town with Marsh's General Store first.
Closed
Completed in PR #247 (guards stand watch; creature-vs-creature engagement filed as follow-up) (2026-10-05)
area:contentepic:e42milestone:m3mw-ju8.13Build the Bridgeward College: fighting and archery trainers, no sorcery
Description
Why
Owner canon 2026-10-04: a soldierly training college in town teaches fighting and archery.
What
College building (greybox), instructors for knight techniques (reuse mw-e19.8 dialogue services) and archery (reuse the archery range mw-e05.14), paid lessons priced from docs/design/economy.md trainers band (100-400 crowns). Name Bridgeward College is a placeholder.
Acceptance
- AC-1 integration Given enough crowns, when the player pays a trainer, then the technique unlocks and crowns drop by the price.
- AC-2 e2e Given the college, when visited, then both a fighting and an archery instructor can be talked to and no one offers sorcery.
Closed
Completed in PR #265 (2026-10-24)
area:contentepic:e42milestone:m3mw-ju8.14Place the robed sorcery teacher under the Miners' Bridge, night only
Description
Why
Owner canon 2026-10-04: sorcery is taught by a robed figure met at night underneath the bridge, not by the college.
What
Night-only NPC under the bridge arch (e27 clock schedule), reached by a path from the riverbank; teaches a first spell through the spellbook-learning system (e07), payment in crowns or favours; discoverable by rumour (Dot, Mirela). Name, price and teaching are open. Unlicensed by the Lantern Court: schools taught are a design decision.
Acceptance
- AC-1 integration Given daytime, then the figure is absent; given night, then present.
- AC-2 integration Given the player pays, then a first spell is learned via e07.
- AC-3 manual Given a first-time player, when exploring at night, then the figure is findable without a guide.
Closed
Completed in PR #275 (AC-3 manual playtest tracked in mw-ju8.39) (2026-10-24)
area:contentepic:e42milestone:m3mw-ju8.17Scroll read-and-cast: reading a spell scroll casts its spell once and consumes it
Description
Gap from mw-ju8.4 AC-3. Scroll items (scroll-of-mage-hand, scroll-of-ember, scroll-of-firebolt) are authored as category book with no effect because no spell content exists and the cast pipeline (mw-e06.2) is not built. Once spells exist as content, add a cast-once use effect (or book read path) that casts the named spell without mana or learning and spends the scroll; test AC-3: reading casts once and consumes it. Owner decision: scroll cast cost and whether a non-sorcerer may read.
Waits on
- mw-e06.2 Run data-driven spells through a cast→deliver→effect pipeline done
Closed
Completed in PR #281 (2026-10-24)
area:contentepic:e42milestone:m3mw-ju8.18Flaky e2e: marsh-store AC-1 times out on CI runners (about 1 fps software GL)
Description
e2e/marsh-store.spec.ts AC-1 (walk in from the street, shopkeeper behind counter, open shop, buy) timed out at 90 s on PR #230 (first run) and on PR #231 after #230 merged; shard 2 runs about 11 min versus about 7 for the others. Fix: shorten the path (teleport with the debug console instead of walking), split the buy step into its own light spec if needed, avoid long frame-paced waits, give the spec a shard weight, keep what it proves (door, keeper behind counter, stair, shop opens, buy works). AC-1 e2e Given a CI runner at about 1 fps, when the spec runs three times, then it passes every time.
Closed
Completed in PR #233 (2026-10-04)
area:contentarea:toolsepic:e42milestone:m3mw-ju8.19Skeleton roster: Forgotten archer, shield-bearer and brute with crown and gear drops
Description
Why
Owner 2026-10-04: enough enemy skeletons, of different varieties, in the open world to earn crowns or pick up gear to sell. Existing: the Forgotten miner (e13-forgotten-skeleton).
What
Three new Forgotten variants beside the miner: archer (ranged, uses the bow/arrow system), shield-bearer (blocks, bashes; punishes lazy attacks), brute (slow heavy two-hander, big telegraphs). Creature defs, moves/attacks with readable telegraphs, behaviours on the e11 AI, drop tables per docs/design/economy.md (tier 1 miner 1-4 crowns, tier 2 3-8, tier 3 8-20) with sellable gear drops (rusted weapons, a chance at catalogue gear), bone/rattle cue sets reusing existing audio/VFX hooks.
Acceptance
- AC-1 content Given each variant, when validated, then it passes the creature, attack and loot schemas and its drops lie inside the economy bands.
- AC-2 integration Given each variant killed N times with a seeded roller, then mean crown yield sits inside its tier band.
- AC-3 integration Given each variant, when it sees the player, then it enters Combat and uses its signature move (archer shoots, shield-bearer blocks then bashes, brute winds up).
- AC-4 manual Given a tester, when fighting each, then the telegraph is readable.
Closed
Completed in PR #239 (real animations/models: mw-ju8.23-.25) (2026-10-05)
area:contentarea:simepic:e42milestone:m3mw-ju8.20Treasure chests for the valley: wooden, iron-bound and hidden chests with tiered loot tables
Description
Why
Owner 2026-10-04: treasure chests here and there in the open world.
What
Loot tables for chest tiers per docs/design/economy.md (common 8-25 crowns, fine 25-70, superior 70-180, masterwork 180-450 plus gear), chest container definitions (wooden, iron-bound locked with a skeleton-key/lockpick, a hidden one behind breakable/climb), placed along the valley path scenes with sensible risk/reward (off-path nooks, overlook, bridge underside).
Acceptance
- AC-1 content Given the tables, when validated, then crown ranges match the economy tiers.
- AC-2 integration Given a seeded open of each chest tier, then contents are deterministic and inside the band.
- AC-3 integration Given a locked chest, when opened with a key or picked, then it opens; without, the prompt says locked.
- AC-4 manual Given the valley run, then a chest is discoverable roughly every 40-60 m of path without breaking the straight route.
Closed
Completed in PR #238 (2026-10-05)
area:contentepic:e42milestone:m3mw-ju8.21Populate the valley: place skeleton encounters and chests across valley-01 to valley-03
Description
Why
The shops are only fun if the valley pays out. Owner 2026-10-04.
What
Place the miner, archer, shield-bearer and brute (mw-ju8.19) in groups with leashes and wake triggers across the three valley scenes, plus chests (mw-ju8.20), balanced so a full clear yields the early-game purchases in docs/design/economy.md (about 355 crowns per 45 minutes). Path stays tight; no encounter forces more than two enemies at once early.
Acceptance
- AC-1 integration Given a headless simulated full clear, then total crowns plus sellable gear value falls in the economy doc's per-45-minutes band.
- AC-2 e2e Given each valley scene, then it loads with its encounters and zero console errors.
- AC-3 manual Given a playtest, then the valley feels like an adventure: danger ahead, reward visible.
Waits on
- mw-ju8.19 Skeleton roster: Forgotten archer, shield-bearer and brute with crown and gear drops done
- mw-ju8.20 Treasure chests for the valley: wooden, iron-bound and hidden chests with tiered loot tables done
Closed
Completed in PR #243 (respawn mw-ju8.29) (2026-10-05)
area:contentepic:e42milestone:m3mw-ju8.34Give the seven Briar Glen NPCs placeholder bodies via Higgsfield (no more capsules)
Description
Why
All seven town NPCs (npc-ottilie, npc-hollis, npc-juniper, npc-oswin, npc-dot, npc-watchman-day, npc-watchman-night) render as placeholder capsules; they are the most-seen characters in the valley.
What
Generate a turnaround flat per NPC with Higgsfield (style bible §14 preamble verbatim; Ottilie reuses her owner-approved char-ottilie-marsh-base-01), front-view crop to Tripo H3.1 via Higgsfield generate_3d, optimise to public/assets/model/, and swap the model in for the capsule by presentation.mesh, keeping the capsule as fallback. Follows PR #195/#198 precedent. Placeholder until owner approval.
Acceptance criteria
- AC-1 each NPC id has a model glb (<=6k tris, one 512 texture) and provenance md with job ids
- AC-2 loading a scene with an NPC shows the model, capsule kept if load fails (unit test)
- AC-3 CREDITS rows placeholder: yes; owner approval bead filed
Closed
Completed in PR #255 (2026-10-24)
area:artarea:contentasset:modelepic:e42milestone:m3mw-ju8.40Integrate Ottilie's hum as a looping spatial cue at her counter
Description
Owner approved the hum (vocal stem of Suno take B, supplied via Voice Memos 2026-11-07) and asked to integrate it now, ahead of the rest of mw-ju8.11. Import assets/_incoming/music/music-ottilie-hum.wav with scripts/audio/import-suno-take.sh, add a spatial looping sound at Ottilie in the marsh-store scene, CREDITS row, prompt file.
Acceptance criteria
AC-1 the hum is imported as .ogg + .m4a with a loop sidecar at the bible's target loudness. AC-2 in the marsh-store scene the hum loops from Ottilie's position, audible near the counter and fading with distance. AC-3 CREDITS and the prompt file record Suno v6 Pro annual, the stem split and the Voice Memos route.
Closed
Completed in PR #287 (2026-11-07)
area:contentepic:e42milestone:m3mw-ju8.16Give merchants a display name so the shop window says Ottilie Marsh, not 'Npc ottilie'
Description
Why
The shop window derives the merchant name from npcId, so Marsh's General Store opens as 'Npc ottilie'.
What
Add an optional displayName (and optional shopName) to the merchant schema, regenerate schemas and docs, use it in the shop window header; fall back to the readable npc id. Set it for marsh-general-store (Ottilie Marsh / Marsh's General Store).
Acceptance
- AC-1 unit Given a merchant with displayName, when the shop opens, then the header shows it.
- AC-2 e2e Given the Marsh counter, when used, then the window says Ottilie Marsh.
Closed
Completed in PR #235 (2026-10-05)
area:contentarea:uiepic:e42milestone:m3mw-ju8.22Add the Sleeping Ox building and door on briar-glen-lane (exit to the sleeping-ox scene)
Description
The sleeping-ox scene exits to briar-glen-lane, but the lane has no inn building or door marker, so the link is one-way. Add the inn front (kit building on the west side past Marsh's General Store) with an area-exit marker tagged scene:sleeping-ox, plus a lane scene test and light e2e assertion. AC-1 integration Given the lane scene, then an area-exit marker with scene:sleeping-ox exists at a doorway the player can reach. Mind the briar-glen-lane layout (shops east side, Marsh west).
Closed
Completed in PR #244 (2026-10-05)
area:contentepic:e42milestone:m3mw-ju8.23Forgotten archer animations: bow draw, aim and loose clips (and bow model)
Description
Why
mw-ju8.19 shipped the Forgotten archer with a PLACEHOLDER: its shot and shove play the miner's lunging-thrust clip retimed to the move's frames (anim-forgotten-archer-shot, anim-forgotten-archer-shove), and it carries no visible bow. Readability of the draw (AC-4 of mw-ju8.19) depends on a pose the player can recognise as an archer drawing.
What
Author a bow draw, hold and loose clip set for the greybox-humanoid rig (or its replacement), hit marker on the loose at the move's first active tick (tick 42 of forgotten-archer-shot), a short stave-shove clip, and an owner-approved bow model on the archer. Assets via the usual pipeline (flat image approval, then 3D/animation); do not fake with the miner's clips.
Acceptance
- AC-1 content Given anim-forgotten-archer-shot and anim-forgotten-archer-shove, when validated, then they are bespoke clips (not retimed copies of the miner's) whose hit markers lie within one tick of the move's first active tick.
- AC-2 manual Given a tester fighting the archer in ?scene=skeleton-pen, when it draws, then the bow draw reads clearly before the arrow leaves.
Closed
Completed in PR #291 (2026-11-07)
area:assetsarea:contentepic:e42milestone:m3mw-ju8.24Forgotten shield-bearer animations: shield raise, hold, bash and counter clips (and shield model)
Description
Why
mw-ju8.19 shipped the Forgotten shield-bearer with PLACEHOLDER animation: forgotten-shield-bash plays the miner's thrust and forgotten-shield-counter the miner's slash, both retimed (anim-forgotten-shield-bash, anim-forgotten-shield-counter), there is no raise/hold pose while its guard step runs, and no shield is drawn (it shares the miner's model), so the player cannot see where the guard is.
What
Author shield raise/hold/lower, bash and slow counter-swing clips, and an owner-approved rotten board shield model attached to the off hand, with the guard pose driven by the creature's GuardComponent (held/raisedAt). The 110 degree guard arc (shield forgotten-board-shield) should be legible from the model.
Acceptance
- AC-1 content Given anim-forgotten-shield-bash and anim-forgotten-shield-counter, when validated, then they are bespoke clips whose hit markers lie within one tick of the move's first active tick.
- AC-2 manual Given a tester fighting the shield-bearer in ?scene=skeleton-pen, when it guards, bashes and counters, then each phase reads from its pose and the shield is visible.
Closed
Completed in PR #292 (2026-11-07)
area:assetsarea:contentepic:e42milestone:m3mw-ju8.25Forgotten brute: heavy two-handed overhead and sweep clips, and a bigger brute model
Description
Why
mw-ju8.19 shipped the Forgotten brute with PLACEHOLDER animation (the miner's chop and slash retimed to 56 and 44 ticks, anim-forgotten-brute-overhead / -sweep) and the miner's model scaled to its 2.3 m nav height, so the long telegraph is a stretched miner swing and not a heavy two-handed lift.
What
Author a heavy two-handed overhead (lift, hold, slam) and a wide sweep clip, and an owner-approved brute model (maul or great-axe, heavier build) on the same rig or its replacement.
Acceptance
- AC-1 content Given anim-forgotten-brute-overhead and anim-forgotten-brute-sweep, when validated, then they are bespoke clips whose hit markers lie within one tick of the move's first active tick.
- AC-2 manual Given a tester fighting the brute in ?scene=skeleton-pen, when it winds up, then the lift reads as a heavy swing about to land.
Closed
Completed in PR #290 (2026-11-07)
area:assetsarea:contentepic:e42milestone:m3mw-ju8.29Creature repopulation: skeletons return after the player rests or N days pass
Description
mw-e27 respawn rules (src/sim/respawn, respawn-rules.json) only decide where the PLAYER comes back after death. Nothing repopulates killed creatures: scenes place creatures once, kills persist as level deltas, and respawnCreature (src/sim/creatures) only rebuilds a creature from its origin. Needed: a per-spawn (or per-creature-def) opt-in such as 'repopulate': {afterDays|onRest}, a system listening to restCompleted/time.day (as merchant restock does, mw-e20.5), clearing the dead creature's delta and respawning at its origin when the player is not in sight and not in that scene's combat; excluded for bosses/brute gatekeeper. Enables the loop clear valley, sleep at the Sleeping Ox, clear again, sell at Brand's (mw-ju8.21 owner intent). AC: deterministic, saved/loaded, not within sight of the player.
Closed
Completed in PR #245 (2026-10-05)
area:contentepic:e42milestone:m3mw-ju8.35Forgotten variant placeholder models via Higgsfield: bigger brute body, archer bow, shield-bearer shield
Description
Why
forgotten-brute, forgotten-archer and forgotten-shield-bearer all reuse the miner look with no distinguishing gear (model parts of mw-ju8.23/24/25; animation clips stay in those beads).
What
Higgsfield flats + Tripo H3.1 generate_3d for a hulking brute body (+ two-handed weapon), a bow and a shield; wire them so each variant reads at a glance. Placeholder until owner approval.
Acceptance criteria
- AC-1 brute renders larger with its own model; archer holds a bow; shield-bearer holds a shield
- AC-2 provenance md + CREDITS rows placeholder: yes
- AC-3 miner unchanged (test)
Closed
Completed in PR #254 (2026-10-24)
area:artarea:contentasset:modelepic:e42milestone:m3mw-ju8.36Owner approval: NPC turnaround flats and placeholder town models (Ottilie, Hollis, Juniper, Oswin, Dot, Watch guard)
Description
Approve or reject the flats char-hollis-pell-base-01 (b), char-juniper-fenn-base-01 (a), char-oswin-brand-base-01 (b), char-dot-farrow-base-01 (b), char-watchman-base-01 (a), and the models model-npc-ottilie-01, model-npc-dot-01, model-npc-watchman-01 (assets/prompts/model/model-npc-bodies-01.md). Local flats are in assets/_incoming/ (gitignored). Ottilie's flat is already approved (mw-ju8.8). From mw-ju8.34.
Closed
Owner approved 2026-10-24; recorded in PR #262 (2026-10-24)
area:artarea:contentasset:modelepic:e42milestone:m3needs-humanmw-ju8.37Model Hollis, Juniper and Oswin (town NPC bodies, leftover from mw-ju8.34)
Description
Three Tripo H3.1 runs (9 credits each, 27 total) from the front-view crops of char-hollis-pell-base-01 b, char-juniper-fenn-base-01 a, char-oswin-brand-base-01 b (crop with ffmpeg -vf crop=368:752:0:0; see assets/prompts/model/model-npc-bodies-01.md). Then add the mesh ids to NPC_MODEL_URLS in src/render/creatures/npc-model.ts, set presentation.mesh in the three npc-*.json, add CREDITS rows.
Closed
Completed in PR #257 (2026-10-24)
area:artarea:contentasset:modelepic:e42milestone:m3mw-ju8.38Shop window Services tab says 'Book' for college lessons; should say 'Train' or 'Learn'
Description
From PR #265 (mw-ju8.13): train services reuse the inn's book-a-room button label.
Acceptance criteria
- AC-1 train services show a Learn/Train button label (unit test)
Closed
Completed in PR #265 (2026-10-24)
area:contentepic:e42milestone:m3mw-ju8.42Make the riverbank path gap in Briar Glen lane readable
Description
From owner playtest of mw-ju8.39 (2026-11-08): a first-time player could not tell where the riverbank path is. The gap in the lane's east wall (z 5.5-9, just past the Bridge Watch post near the bridge end) is marked only by lantern-riverbank-path, which looks like every other street lantern.
Make it readable at a glance from the bridge end and from mid-lane, day and night, without relying on art that doesn't exist yet:
- a distinct ground treatment into the alley (path/steps material), and a waypost or signpost spawn at the mouth;
- a stronger or differently-coloured light pool in the alley so it reads as an opening at night;
- frame the gap (posts/arch kit pieces) so the wall break is obvious;
- placeholder marker cones for signs/banners should not float misleadingly at sign height (render them as a flat board or hide in non-debug builds).
AC-1 e2e From arrive-from-valley-03 facing north, the gap's light pool and signpost are within the camera view after turning right (screenshot check).
AC-2 manual Owner re-runs mw-ju8.39 and finds the path without a guide.
AC-3 unit Scene validation: riverbank-path has a signpost spawn within 2 m of the gap.
Closed
Completed in PR #332 (AC-2 owner re-test tracked on mw-ju8.39) (2026-11-08)
area:contentepic:e42milestone:m3mw-ju8.43Briar Glen lane has no night: time of day doesn't change its lighting
Description
From mw-ju8.42 (PR #332) screenshots: 'time set 1 22:00' does not visibly darken briar-glen-lane; the scene's light environment is fixed moonlight, so day and night look the same. mw-ju8.39 (find the sorcery teacher at night) can't be judged properly without a real night. Also: the light rig gives every fire source the same colour (no per-source tint) and caps point lights at 6, which limits wayfinding lights.
AC-1 unit The lane's ambient/sun light follows the game clock (dusk, night, dawn) via the existing environment/time-of-day system.
AC-2 e2e At 22:00 the lane's average frame luminance is clearly lower than at 12:00, and street lanterns' pools stand out.
AC-3 unit A light source may declare a colour tint used by the light rig.
AC-4 manual Owner re-checks mw-ju8.39 at night.
Closed
Completed in PR #336. AC-1 to AC-3 delivered and tested; AC-4 is the owner's night re-check, tracked by mw-ju8.39. (2026-11-08)
area:contentepic:e42milestone:m3
Other work81/171 done · 47%
M1 Grey-box slice 12
mw-2l9Forgotten miner: concept, 3D model and pick, wired into the slice arena
Description
The slice's one enemy is still a grey box. Make the miner-Forgotten (story bible s8, style bible 5.2): unarmed A-pose flat image, Tripo model skinned to the humanoid rig by auto-skin (same pipeline as the knight), and the mining pick as a separate prop on the right-hand bone. Placeholder until the owner approves flats and turntables; Tripo terms unconfirmed (mw-uuk).
Acceptance criteria
AC-1 e2e the slice arena shows the skeleton model and the existing creature/animation specs pass. AC-2 manual owner approves the unarmed flat and turntables.
Closed
Completed in the Forgotten miner PR (model wired as a static figure; approvals split to a needs-human bead) (2026-10-03)
area:artarea:renderepic:e37milestone:m1mw-7w0Wire the glenstone texture onto the slice's walls, floors and ledges
Description
The owner's glenstone quay probe (tex-glenstone-quay-wendmouth-probe-01, 1024 px, 4 m tile) is wired onto the testbed floor only, through the grid shader (owner's uncommitted work in the main checkout). Generalise it into a triplanar stone surface for the walkable, blocking and climbable parts of the slice (and the testbed floor), drop the grid lines on textured surfaces in the slice, ship the runtime texture as WebP, and commit the probe's prompt file and a CREDITS row. Placeholder until owner approval (mw-e37.431, mw-e37.432).
Acceptance criteria
AC-1 e2e the slice and testbed load the texture with no console errors and the slice, mechanisms and climb specs pass. AC-2 manual owner reviews the look.
Closed
Completed in the stone surfaces PR (2026-10-03)
area:artarea:renderepic:e37milestone:m1mw-9mqWire the placeholder knight model into the player body
Description
Why: the player is still a grey box; owner generated a Tripo knight via Higgsfield. What: load model-char-knight-01.glb, auto-skin it to the humanoid rig, swap it for the boxes once loaded (grey box stays if the load fails). Placeholder: not owner-approved, licence unverified.
Acceptance criteria
AC-1 e2e the testbed player renders the knight model and the existing player e2e specs pass. AC-2 unit autoSkin binds vertices to the nearest bones with weights summing to 1.
Closed
Completed in PR #195 (2026-10-03)
area:artarea:renderepic:e37milestone:m1mw-546Slice set pieces: wall torch, brazier, wooden and iron door art
Description
The slice's torches and brazier are stand-in boxes and its doors are plain coloured boxes. Make an unlit torch and brazier (Tripo models; the light rig supplies the flame at the spawn point) and painted door fronts (wooden-door and iron-door, 1.2 x 2.2 m) applied to the door leaf boxes. Placeholders until owner approval; Tripo terms unconfirmed (mw-uuk).
Acceptance criteria
AC-1 e2e the slice shows the torch, brazier and door art with no console errors and the mechanisms and light e2e specs pass. AC-2 manual owner approves flats and turntables.
Closed
Completed in the set pieces PR (torch, brazier and door fronts wired; approvals split to a needs-human bead) (2026-10-03)
area:artarea:renderepic:e37milestone:m1mw-qovVerify Higgsfield/Tripo output licence terms and record in CREDITS
Description
model-char-knight-01 is recorded GEN-OWNED but UNVERIFIED. Read Higgsfield (Plus) and Tripo terms for commercial use and redistribution in a public repo; update the CREDITS row and the 3D pipeline ADR (mw-e37.6). Higgsfield also exposes Meshy, Hunyuan3D and SAM 3 through generate_3d.
Closed
Completed in PR #196 (Higgsfield terms verified and recorded; Tripo's own terms split to a needs-human bead) (2026-10-03)
area:artepic:e37milestone:m1needs-humanmw-uukOwner: confirm Tripo's own terms allow our use of model-char-knight-01
Description
Higgsfield's Terms of Use (read 2026-10-03) give us ownership and commercial use of Outputs, but the knight was generated by Tripo H3.1 through Higgsfield, and Higgsfield §8 says the third-party provider's policies apply. tripo3d.ai/terms returns 403 to automated fetches. Open https://www.tripo3d.ai/terms in a browser and confirm no clause restricts a customer's use or redistribution of Outputs (and whether access through Higgsfield counts as a paid/API tier). Then set the CREDITS row for model-char-knight-01 to placeholder: no (once the turntable is approved too). See assets/prompts/model/model-char-knight-01.md.
Closed
Owner approved 2026-10-24; recorded in PR #262 (2026-10-24)
area:artepic:e37milestone:m1needs-humanmw-va0Slice set pieces 2: chest model, crate, pillar and ivy-ledge surfaces
Description
The alcove chest is a box, the loose crate is a box, the arena pillars are grid-shaded boxes and the ivy ledge is a green slab. Make a chest model (Tripo; 9 credits), a crate face texture (all six faces of the crate box), a pillar stone tile and an ivy-on-stone tile (world-unit UVs on the two pillar parts and the climbable platform part). Placeholders until owner approval; Tripo terms unconfirmed (mw-uuk).
Acceptance criteria
AC-1 e2e the slice loads the chest model and the crate, pillar and ivy textures with no console errors and the slice, mechanisms and climb specs pass. AC-2 manual owner picks flats and approves the chest turntable.
Closed
Completed in the set pieces 2 PR (chest, crate, pillars, ivy wired; approvals split to a needs-human bead) (2026-10-03)
area:artarea:renderepic:e37milestone:m1mw-0i5Owner: approve the Forgotten miner and pick flats and the two turntables
Description
Flats: assets/_incoming/creature-forgotten-miner-base-01/ (variants a to d; b was modelled) and assets/_incoming/prop-miner-pick-01/ (a, b; b was modelled). Models: public/assets/model/model-creature-forgotten-miner-01.glb and model-prop-miner-pick-01.glb. Pick a different variant to regenerate (9 credits each). Known gap: creatures have no animation driver, so the miner is static (mw-e37.401, mw-e37.402). Tripo terms still open (mw-uuk). See assets/prompts/creature, props and model.
Closed
Owner approved 2026-10-04 (flats and turntables as shipped in game; picks as wired) (2026-10-04)
area:artepic:e37milestone:m1needs-humanmw-1jaFour Forgotten miner looks, picked at random each game
Description
The four approved-to-try miner flats (a to d) become four models; each miner draws one from a per-session random salt and its entity id (visual only, sim stays deterministic). ?miner=N pins a variant for tests and screenshots. Models: model-creature-forgotten-miner-01 (b), -02 (a), -03 (c), -04 (d).
Acceptance criteria
AC-1 unit pickVariant is stable for a salt and key and covers every variant. AC-2 e2e ?miner=N draws that variant without console errors.
Closed
Completed in the four miner looks PR (2026-10-03)
area:artarea:renderepic:e37milestone:m1mw-gqxOwner: pick the chest, crate, pillar and ivy flats and approve the chest turntable
Description
Flats in assets/_incoming/prop-chest-wooden-01 (a modelled), prop-crate-face-01 (a used), tex-pillar-glenstone-01 (b used) and tex-ivy-stone-01 (b used); two variants each. Model: public/assets/model/model-prop-chest-wooden-01.glb; paintings: public/assets/surface/crate-01.webp, pillar-glenstone-01.webp, ivy-stone-01.webp (free to swap; the chest costs 9 credits to regenerate). The wall, floor and platform slabs are still plain grey-box colours (the glenstone texture stream). Tripo terms open (mw-uuk). See assets/prompts/props and model.
Closed
Owner approved 2026-10-04 (flats and turntables as shipped in game; picks as wired) (2026-10-04)
area:artepic:e37milestone:m1needs-humanmw-k2vOwner: approve the torch, brazier and two door flats and the torch and brazier turntables
Description
Flats in assets/_incoming/prop-wall-torch-01 (a modelled), prop-brazier-floor-01 (b modelled), prop-door-wood-01 (b used) and prop-door-iron-01 (b used); two variants each. Models: public/assets/model/model-prop-wall-torch-01.glb and model-prop-brazier-floor-01.glb; door fronts: public/assets/door/door-wood-01.webp and door-iron-01.webp. Pick a different variant to regenerate (9 credits per model; door fronts are free to swap). Note the arena-brazier spawn moved to y 0.75 (coals height) so its flame burns in the bowl. Tripo terms still open (mw-uuk). See assets/prompts/props and model.
Closed
Owner approved 2026-10-04 (flats and turntables as shipped in game; picks as wired) (2026-10-04)
area:artepic:e37milestone:m1needs-humanmw-whmREADME: correct the play guide's slice status (title, pause, key now exist)
Description
The play guide merged in #193 says there is no title screen or pause menu and that the miner and gallery key are not placed. The title screen (mw-e01.2), pause menu (mw-e01.3) and the key and chest (mw-e01.6) have since landed; the miner is still unplaced (mw-e01.5, in progress). Correct both passages.
Closed
Completed in the README status PR (2026-10-03)
area:designmilestone:m1
M2 Class fantasies 7
mw-e04.21.1Target health bar and damage numbers
Description
Owner playtest 2026-10-04: no way to see damage dealt/taken or an opponent's health. Top-centre target bar (name + health with chip) for the locked or last-hit fighter, and rising damage numbers (dealt over the target, taken over the player, 'No effect' when immune), toggle Gameplay > Damage numbers (default on). First slice of mw-e04.21; its boss bar, poise bar and hit-marker variants remain.
Closed
Completed in PR #213 (2026-10-04)
area:uiclass:allepic:e04milestone:m2mw-xn1Physics-objects bench flakes over its 2.0 ms budget on CI runners
Description
Why
The mw-e03.10 AC-6 bench (tests/bench/physics-objects.bench.ts, 200 bodies, p95 ≤ 2.0 ms) passes locally at ~0.8 ms but on GitHub runners ranges 0.94–2.18 ms across unrelated branches (main 0.94 and 1.78; feat/knockback-fall-damage 2.02; feat/lock-on 1.56, 2.04, 2.06, 2.18), so PRs fail the unit job at random.
What
As a developer, I want the perf budget to measure the sim, not runner noise, so that CI failures mean regressions.
Scope
- In: make the CI measurement stable (warm-up, more ticks, median-of-runs, or a CI-specific reference factor as other benches do) without loosening the reference-machine budget.
- Out: physics performance work itself unless the profile shows a real regression.
Notes
Found while merging mw-e02.16 (PR #102).
Acceptance criteria
- AC-1 perf Given 10 consecutive CI runs on main, when the bench runs, then none fails while the reference-machine p95 stays ≤ 2.0 ms.
- AC-2 perf Given a deliberate 50% slowdown of the physics-object system, when the bench runs on CI, then it fails.
Closed
Duplicate of mw-w3d; completed in PR #115 (2026-09-30)
area:perfepic:e03milestone:m2mw-55iCast a studied spell in the running game: browser spec for book study
Description
Why
mw-e07.2 AC-6 asked for an e2e: pick up a fixture book in the greybox testbed, study it, cast its spell. Casting is not wired into the shipping game yet (aim/hotbar: mw-e06.15, mw-e06.16), so mw-e07.2 proves the same path in tests/integration/book-study.test.ts.
What
Once a spell can be cast in the browser game, add an e2e spec: pick up a fixture spellbook, choose Study in the inventory, wait out the session, cast the spell and see the cast succeed. Reuse the #app data readouts.
mw-b1aRun the element rules (fire) in the game world so burning hazards hurt in play
Description
Why
mw-e04.34 wired the environmental damage rules into the game world (src/main.ts, the testbed and the replay world via startTestbedCombat), including the burning-hazard rule. But the game world does not run the element or fire rules, so nothing ever has burning=true in play, and the hazard damage (and the frame-data World column's 'hazard') can't happen outside tests.
What
Install the element rules (fire/heat) and their systems in installGamePhysics or the game wiring, after stimulusSystem. Add a way to light something in the testbed (a lit brazier prop, or a console heat stimulus), so a burning prop hurts the knight standing in it.
Discovered from mw-e04.34.
Acceptance criteria
- AC-1 integration Given the testbed with a burning prop, when the player stands within its reach, then they take fire damage in 250 ms pulses.
- AC-2 e2e Given the testbed, when the player stands in a fire, then the frame-data HUD shows hazard damage.
Closed
Completed in PR #279 (2026-10-24)
area:gamearea:simepic:e04milestone:m2mw-e23.6.1Journal: unread indicator and new-entry toast, pinned HUD tracker and text-size scrolling (mw-e23.6 AC-3, AC-4, AC-5)
Description
Remainder of mw-e23.6 after the journal screen (J / pause menu): AC-3 new-entry toast >= 3 s and unread indicator on the journal tab until viewed; AC-4 text size 150% with a 10-objective quest scrolls without clipping (needs the settings text-size hook); AC-5 pinned quest tracker in the HUD, off when disabled in settings. Entry log appended as quest events occur (journalKey).
area:uiepic:e23milestone:m2mw-g2lOwner: approve the unarmed knight flat and the three model turntables; remove tabard slivers at extreme arm poses
Description
PR for mw-juy replaced the fused knight with a separate body (model-char-knight-01 v2), sword and shield. (1) The derived unarmed A-pose flat image (empty hands, no sword or shield; assets/_incoming/char-knight-base-01/unarmed-v1.png) (Higgsfield gpt_image_2_5, job e7f8f3d9-a600-4a8c-91c9-e065bdbc23a4) has not had the flat-image approval. (2) No turntable approval yet for model-char-knight-01, model-prop-arming-sword-01, model-prop-wooden-shield-01 (mw-e37.21). (3) Automatic skin weights leave a hairline sliver of tabard edge stretching from the shoulder when an arm is raised 70 to 120 degrees; hand weights or a Meshy rig (meshy_rigging via Higgsfield) would remove it. See assets/prompts/model/*.md.
Closed
Owner approved 2026-10-04 (flats and turntables as shipped in game; picks as wired) (2026-10-04)
area:artarea:renderepic:e37milestone:m2needs-humanmw-juySplit knight sword and shield from the body mesh; hand-tune skin weights
Description
The Tripo knight has sword and shield fused into the mesh, and auto-skin weights are crude at shoulders and hips. Generate hands-free knight image, regenerate, attach prop-arming-sword-01 / prop-wooden-shield-01 to the sword bone and left arm; compare Meshy rigging (meshy_rigging via Higgsfield) vs auto-skin. Add LOD1 and KTX2 via mw-e37.8.
Closed
Completed in the knight props PR (sword and shield split off; skin weights improved; sliver follow-up filed) (2026-10-03)
area:artarea:renderepic:e37milestone:m2
M3 MVP content 9
mw-e24.13.1Schedule Briar Glen NPCs by hour windows on spawn presence (no walking yet)
Description
Delivered in PR #471: presence.hours windows, schedules for the named NPCs and ten townsfolk, shop hours bound to keepers. Walking between places (mw-8551) and the frame-time measure (mw-8stk) remain on mw-e24.13.
Closed
Completed in PR #471 (2026-11-14)
area:contentepic:e24milestone:m3mw-e39.38.1Wire the main quest's built-level sources: Briar Glen arrival, belfry deck, abbot reveal
Description
The slice of mw-e39.38 that existing levels support: briar-glen-arrival signal graph, belfry-deck volume, abbot-revealed emit. Delivered in PR #449; the rest stays on mw-e39.38 (mw-sba, mw-f25, mw-5wg).
Closed
Completed in PR #449 (2026-11-13)
area:narrativeepic:e39milestone:m3mw-e40.26.1Link the riverbank under the bridge to the Wendmouth Stone Wharf (stopgap towpath)
Description
Delivered part of mw-e40.26: a towpath transition from under-the-bridge to the Stone Wharf and back. AC-1 integration a walker arriving from the riverbank reaches the wharf and berth; AC-2 content the edge lists its transitions and the main quest never needs it.
Closed
Completed in PR #457 (2026-11-14)
area:contentepic:e40milestone:m3mw-opf6Wire contests into the Wendmouth scene: report arm-wrestling, dice and cast events and map perception to cheat detection
Description
mw-e40.5 shipped the contest runner (src/sim/contest) and the drowned-gull-arm-wrestling and drowned-gull-dice definitions. Left: (1) a game-side adapter that constructs ContestRunner with the world's host, rng and event bus, calls update(tick) each tick and forwards sim events (armwrestle.push, dice.round-won, dice.loaded, spell.cast, arrow.hit) to record(); (2) a ContestPerception implementation over the perception system (src/sim/perception 'perceived' reports) mapping creature ids to entities; (3) saving ContestRunner.serialize() with the world save; (4) UI for entry fee, odds and the throw-the-match offer; (5) Tidefair definitions (gull shoot, regatta, pocket race) under e40.7-9. Blocked on the Wendmouth scene (PR 451).
Closed
Completed in PR #458 (2026-11-14)
area:gameepic:e40milestone:m3mw-3wl9Wire follow-tansy: mine-gate forecourt volume in the Deepworks
Description
The mine-gate trigger volume for follow-tansy (part of mw-sba).
Closed
Completed in PR #455 (2026-11-14)
area:narrativeepic:e39milestone:m3mw-cgeFlaky CI: scaffold AC-4 nests a coverage run inside coverage-gate and times out at 60 s
Description
tests/toolchain/scaffold.test.ts 'AC-4: test:coverage writes coverage-summary.json and lcov.info' runs pnpm test:coverage from inside the coverage-gate job's own pnpm test:coverage. On slow runners it hits its 60000 ms timeout (seen on PR #224 and PR #226; both passed on re-run). Fix: skip or shorten the nested run when already under coverage (e.g. assert on the outer run's output files), or raise the timeout. AC-1 unit Given a slow runner, when coverage-gate runs, then AC-4 does not time out.
Closed
Completed in PR #227 (2026-10-04)
area:toolsmilestone:m3mw-cqfnPlace the four Knot friend-below notes (outer-ring, spiders, ossuary, warden) once the Knot scenes exist
Description
note-friend-below-outer-ring, -spiders, -ossuary, -warden (place-knot, bible 4.1 beats 7-8, 10) already emit friend-note-found on first read (mw-nro9) but no Knot scene exists to hold them. When the Outer Ring / Ossuary / Warden scenes land (mw-e26.9), add a readable spawn per note (tags readable + note id, read affordance), following src/content/friend-notes.test.ts.
Closed
Completed in PR #464 (2026-11-14)
area:narrativeepic:e39milestone:m3mw-nro9Emit friend-note-found on first read of a friend-below note
Description
friend-below-note (event friend-note-found, stage investigation, optional) has no source. The seven notes (mw-e39.42) are content only: nothing places them in a scene and there is no reader or read effect that could emit an event (reader is mw-e07.9, placement of notes is mw-e26.22). Once a note can be found and read, emit friend-note-found on the first read; note-friend-below-door is the one under the player's door in the Sleeping Ox.
Closed
Completed in PR #462 (2026-11-14)
area:narrativeepic:e39milestone:m3mw-bj41Place the Cradle friend-below note (note-friend-below-cradle) once the Cradle scene exists
Description
note-friend-below-cradle (place-cradle, bible 4.1 beats 10-12) emits friend-note-found on first read (mw-nro9) but no Cradle scene exists. Add a readable spawn when it lands.
Closed
Completed in PR #465 (2026-11-14)
area:narrativeepic:e39milestone:m3
Unscheduled 53
mw-d8hDev server reloads on agent worktree and coverage output
Description
pnpm dev at the repo root watches .claude/worktrees/ and coverage/, so every parallel agent's coverage run triggers thousands of full page reloads (125k logged overnight), making the game unplayable in the browser. Ignore .claude/, coverage/, playwright output in server.watch.
Closed
Completed in PR #120 (2026-09-30)
mw-ra8Puzzle coverage CLI test times out in CI coverage-gate (main red)
Description
src/tools/puzzle/coverage-cli.test.ts AC-1 runs the full puzzle audit 4x and exceeds 120 s on CI with coverage instrumentation; main CI is red. Fix: memoize auditContent per content object.
Closed
Completed in PR #403 (2026-11-12)
mw-0gxCI: slow apt mirror still eats e2e shard time after the install retry
Description
PR #382 run: install-deps took 6-7 minutes in shards 2 and 3 (first 240 s try timed out, the retry finished in about 20 s), leaving 8-9 minutes for specs that need 9; both were cancelled at 15 minutes with the specs nearly done (40 passed in 9.0 m). Shorten each try to 120 s and give the shard 20 minutes. Longer term: run e2e in the Playwright container image so no apt step is needed.
Closed
Completed in PR #383 (2026-11-10)
area:toolsmw-7piSplit the menu-focus e2e into per-menu specs so each fits CI's 1 fps budget
Description
e2e/menu-focus.spec.ts (mw-e31.11, PR #386) timed out on CI (240 s) in its pause/options test: the Tab sweep waited two frames per key, over 100+ controls on the Controls tab. Fix: the sweep waits on nothing (focus moves synchronously), axe is scoped to the open screen, tab steps poll the selected tab, and the spec is split into one file per menu group so CI's weighted sharding spreads them.
Closed
Completed in PR #389 (2026-11-10)
mw-8qoFlaky e2e: marsh-store AC-1 and AC-3 time out waiting for pointer lock on CI
Description
PR #347 run 42551996218, shard 2: AC-1 failed all 3 retries and AC-3 was flaky at the pointer lock poll (5 s timeout, line 61 of the failing spec helper), in a shard where creatures.spec took 5.3 minutes. Passed on rerun with an unrelated diff. Related to closed mw-ju8.18.
Closed
Completed in PR #361 (2026-11-09)
mw-18wPlace Wendmouth harbour texts (manifests, invoice, seal, sealed letter, notice) and wire rumour pools
Description
mw-e39.23 wrote the content; placement waits for the Wendmouth scene (PR #451, scene src/content/data/scene/wendmouth-harbour.json). Intended anchors: note-cargo-manifest in place-customs-house (clerk's desk) and place-bay-nine (crate seal); note-cargo-manifest-quell-copy in Quell's desk in place-bonded-warehouse; note-foundry-invoice in Quell's desk in place-customs-house; note-sealed-letter-line inside the crate in place-bay-nine; note-lantern-court-notice on the notice board at place-stone-wharf and place-customs-house; inscription-kestrel-lock-toll-board at place-kestrel-lock. Pickups must write the evidence.* facts named in each text's evidence field. Rumours 13-18 pools: Nell 13/14/15, Mags 16/18, Reed 17, Dot second-hand via rumourPool. Related to mw-e40.22.
Closed
Completed in PR #460 (2026-11-14)
mw-30jparry-replay AC-7 replays time out at 60 s in CI coverage-gate
Description
tests/integration/parry-replay.test.ts AC-7 (100 replays) exceeded its 60 s timeout in CI coverage-gate on PR #427 (twice) and repeatedly in local full runs under load, failing unrelated PRs. Raise to 180 s.
Closed
Completed in PR #431 (2026-11-12)
mw-342Forgotten miner dies: AI stops at zero health, body topples
Description
Owner playtest 2026-10-04: the miner did not die when its health ran out. Cause: the AI loop kept thinking, circling and pressing a dead creature. Dead agents (health 0) are now skipped by the AI system (sim), and the drawn body topples backward onto the ground on Died and stays (render). Final approved death clips remain mw-e37.399..402.
Closed
Completed in PR #221 (2026-10-04)
mw-965Investigate vfx-system bench at 13-16 ms locally (was 0.13 ms at merge)
Description
PR #92 merged tests/bench/vfx-system.bench.ts at ~0.13 ms p99 (2000 particles). A full local pnpm bench on 2026-09-30 measured 13-16 ms against its 1-2 ms budgets. Could be machine load (load avg ~25-30) or a real regression from later merges (animation driver, physics poses as VFX anchors in PR #99, etc). Bisect on a quiet run; check CI's bench numbers for recent main commits. Fix the regression if real; if it's load-only, document and close.
Closed
Load-only: CI p99 flat at 0.13-0.21 ms across all main commits since e1d8975; local main == e1d8975 (~0.11 ms p99); base commit reproduces multi-ms p99 under load spike. No code change. (2026-09-30)
mw-apsCrush, fall and fire-hazard damage reach AI creatures and training dummies
Description
Found in mw-e03.13: the environmental damage rules (src/sim/combat/environment/environment.ts) only look at entities with both a CharacterController and Health. AI creatures (a Forgotten miner) and the testbed's training dummies have Health but no CharacterController, so a chandelier or anvil that lands on one does no crush damage, nor do falls or burning hazards. Only the player (and spawnCharacter entities) is hurt. mw-e03.13's chandelier room therefore has the player stand under the chandelier. Fix: let the rules find any creature with Health and a placement capsule (feet from its placement, velocity from its locomotion), then add a chandelier-on-a-creature test.
Acceptance criteria
A physics object that strikes an AI creature above the kinetic speed threshold deals blunt crush damage blamed on whoever released it; the same for a falling or burning hazard; existing player behaviour unchanged.
Closed
Completed in PR #351. Crush and fire reach AI creatures and dummies; navmesh creatures cannot fall. (2026-11-09)
area:simepic:e04mw-bh5Let the player walk while swinging
Description
Owner playtest 2026-10-04: movement froze for the whole attack. Swings now commit facing but allow walking at ATTACK_MOVE_SCALE (0.6); jump and sprint stay blocked mid-swing.
Closed
Completed in PR #212 (2026-10-04)
mw-btfCI: coverage-gate job outgrew its 10 minute timeout
Description
PR #360 run 42626772979: pnpm test:coverage finished at about 9:50, then the job was cancelled at the 10 minute limit during coverage:exclusions, before content:coverage and content:loot ran. Main runs took 5.5 to 10 minutes on 2026-11-09 (75814a8 took 9:55), so every PR is at risk. The tests themselves passed.
Closed
Completed in PR #361 (2026-11-09)
area:cimw-e8nKnight body distorts at extreme poses (strong attack: stretched sword arm, tabard ribbon, bent legs)
Description
Owner playtest 2026-10-04: during the strong (heavy) attack the knight's sword arm is stretched into a long red ribbon, the tabard flares and the legs distort (screenshot in session). Cause: the Tripo knight mesh is bound to the grey-box rig by nearest-bone autoSkin (src/render/animation/auto-skin.ts, src/render/player/knight-model.ts), so vertices near other bones (tabard, cape, torso) are dragged by the arm/leg bones at extreme poses. Fix the skin weights (region/chain-aware, smoother falloff, torso/tabard excluded from arm bones) so the heavy swing, light swings, walk and block read cleanly; do not change the pose data. Relates to mw-e37.21 and mw-e04.13.
Closed
Completed in PR #216 (2026-10-04)
area:artarea:rendermw-j6elInteraction line of sight ray-casts to an NPC's feet, so NPCs on non-zero floors can't be talked to
Description
Found fixing PR #451. reachable() in src/sim/interaction/system.ts casts from the player's reach point to the target's placement point (its feet). For an NPC standing on a floor whose top is not y 0 (Mags at y -2 on the barge landing in wendmouth-harbour), the ray ends inside the floor collider and the NPC is dropped as a candidate: no Talk prompt. #451 worked around it by lifting Mags to y -1.75. Fix: cast to a point above the feet (e.g. the target's chest or interaction anchor), add a sim test with an NPC on a raised floor, then put Mags back at y -2 and check the other scenes for NPCs that were nudged up.
Closed
Completed in PR #456 (2026-11-14)
mw-jxewNarration panels (class arrival, epilogue) are unreadable: transparent panel over the game
Description
Owner screenshot 2026-11-14: the new-game 'Why you came' narration (screen class-beat, #473) draws dark text straight on the 3D scene, because .vb-panel.vb-epilogue sets background: transparent and only data-screen=epilogue gets a backdrop (#000, which would also hide dark text). Give the panel the normal parchment background and both screens a dark backdrop.
mw-kf4cReadable placement: reader UI and evidence facts for notes and inscriptions
Description
Notes and inscriptions have no inventory item (item.ts and readable.ts only pair book- ids) and no in-game reader: the 'read' affordance verb exists in the sim but nothing handles it. mw-18w placed the Wendmouth harbour texts as marker spawns tagged 'readable' plus the text id, with a read affordance (harbour-placements.test.ts). Needed: a reader that opens a tagged spawn's text, and writing the text's evidence.* facts when it is read or picked up (cargo manifest -> evidence.dead-clerk-seal, Quell copy -> evidence.honest-manifest, invoice -> evidence.foundry-invoice), 100% covered. Until then dialogue reading those facts (Cray, Quell, The Crate for Mr. S) cannot be satisfied in play.
Closed
Completed in PR #461 (2026-11-14)
mw-royAnimate the Forgotten miner in game
Description
Owner request 2026-10-04: the Tripo skeleton was a static figure that slid. It is now skinned onto the shared humanoid rig (auto-skin) and driven by the existing animation runtime (idle, walk/run by measured speed, attack from its timeline move, hit react). Final approved clips remain mw-e37.399..402.
Closed
Completed in PR #219 (2026-10-04)
mw-w3dphysics-objects bench p95 near 2.0 ms budget on CI (was 1.24 ms at merge)
Description
PR #94 merged tests/bench/physics-objects.bench.ts at ~1.24 ms p95 on CI (budget 2.0 ms). On PR #110's branch CI measured 2.016 and 2.17 ms twice, then passed after rebase. Check CI bench history on main since 6e5bb89 for drift (candidates: onRemove hooks PR #99, scene physics, hit reaction pushers, locomotion). Fix any real regression; if it's runner variance, make the bench measurement robust (retained samples, warm-up) without loosening the budget.
Closed
Completed in PR #115 (2026-09-30)
mw-wi8Fix backwards swings, backward walk lean and footstep rate
Description
Owner playtest 2026-10-04: the swing and heavy attack read as coming up from the ground; the walk leans way back; the footstep sound is faster than the steps drawn. Causes: every torso/head clip assumed +x leans forward, but spine, chest and head point up so +x leans BACK (and +y turns the right shoulder forward); all attacks but one fell back to one clip whose raise swept the blade up the front; footstep spacing (run 1.0 m = 5/s) did not match the clips' step rate (3.1/s at 5 m/s). Fixed the signs in 14 clips, authored light 1-3, heavy, charged heavy, riposte, the Forgotten's chops/thrust, and set footstep spacing to 0.75/1.6/2.05/1.0 m.
Closed
Completed in PR #223 (2026-10-04)
mw-zciReflow playtest HUD and recover out-of-bounds player
Description
Move combat health/stamina bars to the top-right, constrain the bottom-left controls hint to a readable paragraph, and reset a player who falls below the level back to the authored player-start spawn. Update owner-facing UI docs and automated coverage.
Acceptance criteria
Combat bars are top-right at every HUD scale; controls text wraps in a narrow left column without occupying the quick-slot strip; falling sufficiently below the player-start resets controller position and look deterministically; focused tests, typecheck, lint, and build pass.
Closed
Moved combat bars to the top-right, constrained the controls hint to a 34rem left column, added deterministic scene-relative out-of-bounds recovery to player-start, updated UI/style docs, and passed 92 focused tests plus typecheck, lint, format, and build. (2026-10-03)
mw-0j5Import and wire the mine-slice music, stingers and ambience
Description
Import four kept Suno v6 takes (explore bed, skeleton-arena combat loop, stinger bundle cut into four cues, mine ambience bed) and wire a minimal placeholder into the slice. Full adaptive controller stays mw-e28.11.
Closed
Completed in PR #202 (2026-10-03)
area:audioasset:musicmw-1jmTag scenes with acoustic surface regions and reconcile material and surface offsets
Description
From PR #355 (mw-e09.4): no scene is tagged yet, so surface regions change nothing in play. Tag carpets, gravel paths and creaky boards in existing scenes once mw-e09.5 makes them audible to creatures. Also: a floor's offset comes from its material's footstepLoudness (dry-wood 4 dB) but a region's from the surface table (wood 2 dB), so the same wood can differ by 2 dB depending on how it is tagged. Pick one source or align the numbers. Audio should prefer the region's surface when sets exist (mw-e38.176, mw-e38.23).
Closed
Completed in PR #358. Remaining scenes need floor art first: mw-wif. (2026-11-09)
mw-1mytJournal screen: show the class first-journal entry and replace the main quest arrival stage text
Description
mw-aw1y shows the class first-journal entry as the last page of the arrival narration because no journal/quest-log UI exists. When a journal screen exists: write narration-class-beats first-journal into it (keyed by player.class) in place of the main quest's generic arrival stage text, and journal each class beat shown (facts class-beat.<beat>.shown). Discovered from mw-aw1y.
Closed
Completed in PR #479 (2026-11-14)
area:narrativeepic:e39mw-2uie2e: arrow-vfx.spec takes 7 to 8 minutes
Description
PR #360 run 42626772979 shard 2: e2e/arrow-vfx.spec.ts took 8.1 minutes and the shard 14m15s of its 15 minute limit. Locally it takes about 7 minutes. Cut its wall time (fewer real-time waits, split it, or give it a shard weight) so shards stay well inside the limit, without losing what it proves.
Closed
Completed in PR #363 (2026-11-09)
area:toolsmw-3jhReconcile spell noise-op loudness (metres) with the NoiseEvent loudness scale
Description
The spell schema's noise op documents loudness as a hearing radius in metres (fixture-bolt uses 15), while the arrow noise payload (mw-e05.1) and mw-e05.11 use loudness 70 on the NoiseEvent scale that mw-e03.25 will define. Once mw-e03.25 fixes the NoiseEvent loudness unit, align the spell noise op description/values and the arrow noise payload to it (and regenerate content docs/schemas).
Closed
Completed in PR #271 (2026-10-24)
mw-3n0fPlace Clatter's garden and the priory pear tree, and make the pear bloom for The Name on the Bones
Description
Clatter is a stub spawn in knot-outer and the dead pear tree has no interaction. Needs: Clatter as a placed NPC with clatter-garden, a graft/Nature-spell interaction on the priory dead-pear-tree that sets bones.pear-bloomed (today Juniper's dialogue sets it), the bloom VFX (E29), the altar where a non-knight lays the psalter, the Pell Bible/letter as a readable item (today hollis.bible-lent stands for it), the sorcerer memory-read as a real action and the Tansy/Clatter family scene (tansy.kin.known is set but nothing reads it yet).
Closed
Completed in PR #475 (2026-11-14)
mw-03tStart the slice combat music on the hit at 1.75 s
Description
Owner playtest 2026-11-08: the battle track should start between 0:01 and 0:02 rather than 0:00. AC-1: a music layer can name a start offset and the controller passes it to the engine. AC-2: the slice combat layer starts 1.75 s into the file (strongest onset in that window), each time it comes in. AC-3: the loop still wraps to the file's loop start.
Closed
Completed in PR #335 (2026-11-08)
mw-3xxarrow-retrieval e2e AC-2 still flaky in CI after mw-fd3 (prompt not visible, 3/3 retries)
Description
Seen on PR #405 run 43152521435 (shard 2), unrelated change: e2e/arrow-retrieval.spec.ts AC-2 'Take arrows (n)' prompt toBeVisible failed on all 3 retries. mw-fd3 (#394) raised timeouts; the prompt wait may need to poll game state (focused interactor) rather than DOM visibility, or the aim/turnTo step may be leaving the arrows out of focus range. Download artifacts for screenshots.
Closed
Completed in PR #411 (2026-11-12)
mw-5h9Pressing Interact on a stuck arrow sometimes takes nothing
Description
Seen on PR #360 (mw-e05.7): e2e/arrow-retrieval.spec.ts failed in about 2 of 6 local runs with the prompt reading 'Take arrow' and E taking nothing. The cause was not found. The spec now presses E repeatedly until the quiver rises, so it no longer catches this; its comment guesses focus moves between neighbouring arrows, which is unconfirmed. Find the cause, then make the spec press once. A player would need to press again.
Closed
Completed in PR #373 (2026-11-10)
area:simepic:e05mw-7oue2e/vfx.spec.ts times out under load
Description
Several agents report e2e/vfx.spec.ts timing out in full local Chromium e2e runs on a loaded machine (passes alone; CI e2e-smoke green so far). Find the slow step (likely waiting on frame counts/particle totals) and make the spec deterministic and cheap rather than raising the timeout.
Closed
Completed in PR #108 (2026-09-30)
mw-64wFlaky bench: light-field AC-6 p95 just over 0.5 ms budget on CI
Description
tests/bench/light-field.bench.ts > AC-6 (64 dynamic emitters, 500 samples/tick, p95 <= 0.5 ms) failed the unit job on PR #91 (run 36639965025) with p95 0.50294 ms; unrelated change (test-only). Budget has no headroom on shared CI runners; investigate optimising the light query or measuring more robustly (e.g. median of runs) rather than silently raising the budget.
Closed
Completed in PR #103 (2026-09-30)
area:qaarea:simmw-145CI: e2e shards creeping toward the 15 minute limit
Description
PR #375 run 42740057429: e2e-shard (5) was cancelled at the 15 minute limit (passed in 10m6s on rerun). Shard 5 took 6.7, 12.5, 11.2 min on PRs #372-#374; other shards 9-11 min. The suite grows with every feature (203 chromium tests). Rebalance shard weights, add a sixth shard, or cut slow specs, so no shard runs within 3 minutes of its limit.
Closed
Completed in PR #376 (2026-11-10)
area:toolsmw-798Record Suno prompts and provenance for the mine-slice music and ambience takes
Description
Owner auditioned and kept four Suno v6 takes on 2026-10-03: music-open-world, music-skeleton-arena-combat-music, music-stinger-bundle, mine-ambience-bed. Write prompt files with provenance (hashes, loudness) under assets/prompts/music/. Import/integrate is separate.
Closed
Completed in PR #201 (2026-10-03)
area:musicasset:musicmw-8551Walk scheduled NPCs between locations instead of popping at window edges
Description
Follow-up to mw-e24.13: schedules place people by spawn presence.hours; a person leaving a scene vanishes out of view. Wire the AI hourOfDay port to the game clock, add a townsfolk behaviour that follow-routes, and have leavers walk to the scene exit or door before despawning (AC-3 walk). Also gives Dot, Duggan (pre-capture) real routines.
Closed
Completed in PR #476 (2026-11-14)
mw-a6tKnight carries the sword at a low ready, not pointing straight down
Description
Owner playtest 2026-11-08: while moving, the knight holds the sword pointing straight at the ground with a nearly straight arm. It should look like a low ready: elbow bent, hand away from the hip, blade angled out and away from the body at a low angle (owner's reference image). AC-1 e2e In idle, walk, run and crouch the sword's blade is measurably off vertical and clear of the body and legs. AC-2 unit The carry pose applies to the knight only: NPCs sharing the humanoid clips, including the miner's pick on the sword bone, are unchanged. AC-3 e2e An attack, block or dodge played from the carry pose still reaches the same attack pose, blending with no snap, and returns to the carry pose afterwards. AC-4 manual Owner approves the look in the game.
area:artarea:rendermw-aieFlaky timeout: fire.test.ts AC-5 (water extinguishes burning object) under coverage load
Description
src/sim/elements/fire.test.ts > drying and extinguishing > AC-5 hit the default 5 s test timeout in CI (PR #85 unit job, first attempt) and locally under pnpm test:coverage on a loaded machine; it passes alone in ~0.6 s for the whole file. tests/toolchain/scaffold.test.ts AC-4 then fails as a consequence (coverage files not written). Find the slow part (likely a long field simulation) and make it cheaper or give it an explicit timeout.
Closed
Completed in PR #91 (2026-09-29)
area:qaarea:simmw-akbGive flammable props and authored fires a temperature so heat and fire work in every scene
Description
Heat stimuli only reach entities that have a temperature property; props spawned by addPropPhysics and most scene spawns have none, so only the testbed loose-crate (given temperature 20 in scene data, mw-e06.15) reacts to Ember. Decide a general rule (default temperature for flammable props, or scene data), and revisit settleAuthoredFires, which warms scene torches at load because the fire rules put a burning thing cooler than its ignition point out as cold.
Closed
Completed in PR #279 (2026-10-24)
area:gamemw-aw1yShow class arrival, first journal entry and class-beat prompts in the game
Description
mw-e39.17 wrote narration-class-beats (src/content/data/narration/narration-class-beats.json) and classBeat()/newGameClassText() in src/content/types/narration.ts, keyed by player.class. Nothing in src/game or src/ui shows them yet. Wire: (1) new game: arrival pages after the road-at-dusk sequence, first-journal as the first journal entry (replacing the main quest's generic arrival stage text); (2) each beat's pages shown as the class action prompt at: getting-in (mw-e25.9 supply tunnel, enter-by-winch-cage, enter-by-inner-bar, mw-e24.12 rooftops), mooring-watch (mw-e25.7, mw-e25.8), wendmouth (mw-e40.8, mw-e40.4, mw-wmdv), horn (mw-e13.14, mw-e26.14), climax (mw-e26.19), night-one (mw-e25.2). Done: each beat fires once per class and is journalled. Another worker owns the epilogue slideshow UI; coordinate.
Closed
Completed in PR #473 (2026-11-14)
area:narrativeepic:e39mw-b2xCI: regenerate e2e shard weights from real CI reports after the menu-focus split
Description
Several specs carried guessed or local shard weights; PR #389's run had shard 1 at 14 minutes. Regenerate e2e/shards.json from CI e2e-report artifacts (PR #390).
Closed
Completed in PR #390 (2026-11-10)
area:toolsmw-fd3Arrow-retrieval e2e AC-2 times out on CI's 1 fps runner (10 s toBeVisible)
Description
e2e/arrow-retrieval.spec.ts:17 'AC-2: stuck arrows show Take arrows (n)...' failed all 3 tries on PR #393 (run 42854808120, shard 6) with expect(locator).toBeVisible() Timeout 10000ms, though the same game code passed on main (1aca05d3). PR #393 changed no game code. Pace the prompt check on state with 30 s polls like other CI-paced specs, then run it 5x with --retries=0 under yes hogs.
Closed
Completed in PR #394 (2026-11-10)
area:qaepic:e05mw-fh5e2e/arrow-retrieval AC-2 fails on origin/main: 'Pull lever' prompt shown instead of 'Take arrows'
Description
Reproduced on a clean origin/main worktree (build + playwright --project=chromium e2e/arrow-retrieval.spec.ts) while working mw-e27.7. In the testbed the player stands facing the room lever, so the interact prompt reads 'EPull lever' (expected /Take arrows/) after the arrows are stuck; an earlier run failed at the quiver-count assertion (>=21, got 20). Not caused by persistence changes. Likely position/timing dependent (the spec walks W until z < -2.5 then aims at the latest arrow).
Closed
Completed in PR #373 (2026-11-10)
mw-heoaAdd arrival volumes for rumour target places
Description
mw-e22.11 resolves a heard rumour when its target is visited (src/game/rumours/rumours.ts VISIT_TRIGGERS). These targets have no observed arrival volume yet, so their rumours stay leads: place-briarwood (1), place-mooring-watch (2, also a debunk dive), place-glen-well (4), place-lamplit-stacks (7), place-millweir (9), place-standing-horn (12), place-weeping-adit (16), place-breakwater (18, also a debunk dive). Add an observed player volume per place in its scene's signal graph (node id = area id) and a VISIT_TRIGGERS entry; also place-bay-nine currently maps to the whole bonded-warehouse area and could get its own volume. AC: entering the place after hearing the rumour sets entity:rumours/<id>.investigated.
Closed
Completed in PR #478 (2026-11-14)
mw-kwxFade the player's body when it hides a nearby creature
Description
Owner request 2026-10-04: at close range the orbit camera sits behind the knight and his body hides the creature. The body now fades to 30% while a creature within 10 m lies behind it as seen from the camera (pure test in src/game/camera/occlusion.ts; fade in src/render/player/occlusion-fade.ts).
Closed
Completed in PR #220 (2026-10-04)
mw-lhuBump vite to 8.3.3 in the three spikes (Dependabot high alerts)
Description
Dependabot reports 3 high + 6 moderate vite advisories, all in spikes/*/package.json (dev-only). Pin 8.3.3 in all three; supersedes #250/#251.
Closed
Completed in PR #256 (2026-10-24)
mw-m97iReplace the Contraband Pages stand-in facts with book and writ items
Description
mw-e39.22 uses stand-in facts (books.impounded, books.recovered, writ.held, writ.source, cray.romance-brought) for the impounded crate (eleven volumes incl. The Glasswright's Primer, Illusion), the player's confiscated spellbooks, a writ (Vane's or forged Lantern seal) and a romance novel from Mirela's stock. Author the items (with icon art), make the strongroom hold them, replace the fact stand-ins with haveItem/deliverItem objectives and takeItem/giveItem effects, and add the Primer as a Lamplit Stacks stock row (mirela.illusion-stocked).
Closed
Completed in PR #463 (2026-11-14)
mw-nh24.1Map the seven new arrival volumes to rumour visits (VISIT_TRIGGERS)
Description
Delivered in PR #480: place-briarwood, standing-horn, mooring-watch, glen-well, lamplit-stacks, weeping-adit and breakwater mapped to their #478 volumes, with dive debunks. Millweir and bay-nine stay on mw-nh24.
Closed
Completed in PR #480 (2026-11-14)
mw-txgShard the coverage run so it stays well under the CI timeout
Description
Follow-up to the coverage-gate timeout bug: split pnpm test:coverage across vitest shards and merge the reports (json-summary + lcov) before the thresholds, ratchet and content:coverage steps.
Waits on
- mw-e00.39 coverage-gate hits its 20-minute CI timeout done
Closed
Completed in PR #445 (2026-11-13)
mw-utnFlaky 5 s timeouts: alert-states AC-6 fuzz and perf agitator tests
Description
alert-states AC-6 (10,000 fuzz sequences per behaviour) and agitator's 64-crate determinism test overran vitest's 5 s default on CI (PR #269 coverage-gate) and on loaded dev machines. Give them explicit 30 s timeouts like other heavy integration tests.
Acceptance criteria
- AC-1 both tests carry explicit timeouts; coverage-gate no longer flakes on them
Closed
Completed in PR #273 (2026-10-24)
mw-vvoFlaky e2e: sleeping-ox booking toast not found on slow CI runner
Description
PR #367 run (shard 3, 2026-11-09): e2e/sleeping-ox.spec.ts:80 'booking the room takes the crowns, sleeps until morning, toasts and autosaves' failed all 3 retries at expect(page.getByText('You sleep until morning.')).toBeVisible() with the default 5 s timeout. Passed locally twice and on the next CI run after merging main. Likely the toast is short-lived and the slow runner answers after it has gone; check the toast lifetime against the poll, pace it by sim tick or read a published event instead.
Closed
Completed in PR #370 (2026-11-09)
area:toolsmw-wbr7Wire the rumour book into the journal Rumours tab
Description
The journal (mw-e23.6) consumes JournalRumours { entries() } (src/game/journal/journal.ts), satisfied structurally by RumourBook.entries() from PR #477 (mw-e22.11). Once #477 is on main, pass the RumourBook as the rumours source in src/main.ts (the JournalController model builder), and add an integration test with real heard rumours. Also give the journal a pad button if one frees up (pause menu is the pad route today).
mw-86sCI: playwright install-deps can hang on apt for the whole e2e shard timeout
Description
PR #380 run: e2e-shard (2) sat in 'pnpm exec playwright install-deps chromium' from 05:13:37 to 05:28:34 (last line: apt Get noble-security InRelease) and was cancelled at 15 minutes before any spec ran; rerun passed in 9m33s. Give the install step its own timeout-minutes (e.g. 4) and a retry, or cache the apt packages, so a slow mirror fails fast instead of eating the shard.
Closed
Completed in PR #381 (2026-11-10)
area:toolsmw-kilAdd Somnomancy to the constitution's list of magic schools
Description
Owner decided 2026-09-29 to keep Somnomancy (story bible: forbidden dream magic, spell Lull) as a spell school; spell schema (PR #88) already includes it. Add it to CONSTITUTION.md line listing potential schools so docs agree.
Closed
Completed in PR #106 (2026-09-30)
mw-y7tFlaky e2e: creature-dissolve under load
Description
e2e/creature-dissolve.spec.ts has failed under parallel load several times, most recently with midFrames 0: no frame sampled mid-dissolve in a 1.2 s window on a slow runner. Passes alone. Seen in the mw-e29.7 and mw-aps workers' local runs; not yet on CI.
Closed
Completed in PR #363 (2026-11-09)
No items match these filters. Clear the search or pick "All" to see everything.