Skip to content

final fix report

Xopher edited this page Oct 22, 2026 · 1 revision

Final fix wave: report

Branch feat/classic-temples-2. Five commits on top of fa447e66:

SHA Subject
0293ca79 fix(temples): a pit honours a named floor only below it, so an old Creator seed never drops the hero onto itself
d1c4e383 feat(temples): coming back from a won fight opens the beast's drop in the chest reveal
24be6891 fix(temples): a Quick Start family's Wind Temple refreshes to the Eagle, and tonight's quests sit by the door
d6cca154 chore(temples): the review's sweep: design rationale for process words, stale comments put right, the unread trap gone
943ded2d test(temples): the temple start spec's Ionic double has the page-shown hook the battle-return drop uses

1. Critical: old Temple Creator pits (0293ca79)

Change. One shared pure rule, pitDropOf(floors, floor, content) in src/lib/engine/core/dungeons/floorOrder.ts (with a named PitDrop interface). It honours targetLevel only when the temple has that floor and it stands strictly below the pit in the climbing order, and reads targetCoords only then. Anything else (the pit's own floor, the old seed; a floor above; a floor the temple lacks) falls back to the old rule exactly: next floor down, same cell, then the first stairs up. With no floor below, the pit bruises once. Both the engine (hazards.ts: pitDrop → fallInPit, onEnterRoom) and the solver (templeGraph.ts linkPits) now call it, so they agree on a missing floor too. This also settles the deferred minor. seedTravelTargets (roomContent.ts) now clears the own-floor seed on a pit as it does on stairs.

TDD.

  • RED, chemx test tests/unit/RoomInteractions.spec.ts -t "named floor is not below": ✖ Test Failures (4 failed out of 152). The old-seed one-floor pit got moved instead of "You land hard at the bottom". The old-seed two-floor pit fell to 2F (0,0). The missing-floor pit bruised instead of falling to 1F. The ground pit naming 2F returned moved: true.
  • RED, chemx test tests/unit/TempleSolver.spec.ts -t "not below it": ✖ Test Failures (1 failed out of 55). The one-floor seed gave the self-door (Array [] … + Array [).
  • RED, chemx test tests/unit/TempleCreatorRoomContent.spec.ts -t "seedTravelTargets": ✖ (1 failed out of 62). The pit seed was not cleared.
  • GREEN: RoomInteractions ✔ 152 passed, TempleSolver ✔ 55 passed, TempleCreatorRoomContent ✔ 62 passed.

The existing targeted-pit tests (two floors down to a named cell, a one-room B1) stay green.

2. Important: drop reveal on the battle return (d1c4e383, 943ded2d)

Change. The fix lives in useTempleEntry.ts (new useBattleReturnDrop). TempleEntryParts gains returnsTo (the route's x/y, startPosition), liveContext and showDropReveal. On onIonViewDidEnter it does the following:

  1. It waits for whenOpened, then whenProgressReady, then nextTick.
  2. If the hero stands in the route's room and roomActions(...).drop === 'waiting', it calls showDropReveal() once per page.

DidEnter rather than onMounted: Ionic shows a parked page again, without remounting it, when a battle returns to the same pathname. That happens when the hero flees and then wins the same room. Ionic fires DidEnter on a page's first show too.

Later arrivals in the room, and the page shown again (Portal Home and Back), open nothing. The room's sheet keeps "Take it".

TempleGrounds.ts stays at exactly 500 lines: the entry call became useTempleEntry({ ...entry, returnsTo: startPosition, liveContext, showDropReveal }).

useRoomOutcomes is unchanged. A 6th return member there would have tripped HOOK_RETURN_OVERLOAD.

TDD. New spec tests/unit/TempleBattleReturnDrop.spec.ts. It wires useRoomOutcomes and useTempleEntry as TempleGrounds does, over the real engine, and fires Ionic's onIonViewDidEnter hooks the way fireLifecycle does. It has five cases:

  • A won fight with a waiting drop opens the reveal.
  • The reveal opens once: not on a later arrival, nor when the page is shown again, and the sheet keeps Take it.
  • A fled fight opens nothing, then the reveal opens once a second battle there is won (the reused page).
  • A plain arrival in a live fight room opens nothing.
  • A walk-in, with no battle return, opens nothing.

Evidence:

  • RED, chemx test tests/unit/TempleBattleReturnDrop.spec.ts: ✖ Test Failures (3 failed out of 5), "Expected number of calls: 1, Received: 0".
    • The first RED run also had a test bug: it read ionViewDidEnter where Ionic stores onIonViewDidEnter.
    • After that was fixed, I re-proved RED by disabling useBattleReturnDrop(parts): the same 3 failed.
    • A mutation that drops the once-only memory fails the "opens it once" case.
  • GREEN: ✔ All tests passed (5 passed).
  • The gate run of TempleSystemCleared.spec.ts then failed 4 tests (onIonViewDidEnter is not a function). Its hand-made @ionic/vue double lacked the hook, so 943ded2d adds a no-op hook to the double: ✔ 31 passed.

3. Important: Quick Start families and the new Wind (24be6891)

How Quick Start's placements are identified: by quest id, not a marker. Every family's copy written since 2026-10-18 already holds Quick Start's quests and carries no other mark. A marker would only help future runs, and would leave every existing family on the Howling Spire.

The rule is withoutQuickStartQuests(templeId, layout) in the new pure module src/lib/services/temples/quickStartPlan.ts:

  • It reads only the Wind Temple's copy.
  • A room whose content.questIds is exactly one of QUICK_START_QUEST_IDS, each id counted once, is a room Quick Start wrote.
  • It takes those quests out and returns their ids in the pack's order.

planSeedSync compares that stripped layout with seedSignature:

  • Only Quick Start's placements: the copy is unedited. It stays current while the seed is the same, and on refresh it carries quickStartQuestIds on the plan.
  • Any Game Master room edit, a quest of their own in a room, or a quest placed beside one of tonight's: the copy stays edited.

On a refresh, syncSeeds sets dungeonLayout = placeQuickStartQuests(seedLayout, ids), which runs the existing quickStartTemplePlan. It keeps seedSignature = the shipped signature, so the next start is current.

The documented limitation: if a Game Master placed one of tonight's quests alone into a room of an otherwise untouched copy by hand, it is read as Quick Start's. On a refresh that quest is placed again by Quick Start's rule, so it stays in the temple.

Quick Start's own rule (spec c). On the shipped Eagle, Quick Start picked m006 behind the entrance's locked door. quickStartTemplePlan now places quests only in monster rooms reached from the entrance with nothing to open or win first. A door is shut when its lock is not "none" (small key, Boss Key, Bomb) or when it is the way out of a room with lockOnEnter, because entering seals that room. The walking order used for the wake is unchanged. In the Eagle:

  • M005 gets quick-dishes and M004 gets quick-room.
  • quick-homework is unplaced: it stays on the board, not in a room.
  • m006 (Spill Sprite) wakes with the other monster rooms.

Structure. The pure plan moved from quickStartTemple.ts to quickStartPlan.ts, so importAllTemples can use it without an import cycle (quickStartTemple imports ensureBuiltInTemples). quickStartTemple.ts keeps the writes and re-exports the plan, so QuickStart.vue and the spec imports are unchanged.

TDD.

  • RED, chemx test tests/unit/quickStartTemple.spec.ts: ✖ Test Failures (4 failed out of 29). On the Spire as made, quick-room and quick-homework went behind the locked door. On the Eagle, m006 was placed. The room past M005's seal took a quest. Spill Sprite was not woken.
  • RED, chemx test tests/unit/importAllTemples.spec.ts: the suite failed to compile because quickStartPlan did not exist. After the change, I re-proved a behavioural RED with a mutation (planSeedSync compares the unstripped layout): ✖ Test Failures (3 failed out of 30), the current / refresh / only-in-Wind cases.
  • GREEN: quickStartTemple ✔ 29 passed, importAllTemples ✔ 30 passed.

The spec specifically:

  • (a) A legacy copy holds the old rule's three placements (M0305, M0204, M0203). It stays current on the Spire, then refreshes to the Eagle with M005 quick-dishes and M004 quick-room, exactly quickStartTemplePlan(eagle), and the next sync is current.
  • (b) The copy stays edited for a room edit, a GM quest in another room, and a GM quest beside quick-dishes.
  • planSeedSync reads tonight's quests as Quick Start's only for the Wind Temple's copy.
  • (c) On the shipped Eagle, quests go only to M005 and M004, never m006, and quick-homework is unplaced. Nothing past a sealed room is placed. m006's Spill Sprite wakes.

Existing Spire tests that placed three quests now walk openSpire() (the Spire with its entrance's north door a plain door). This is the new rule, not a weakened test: in the Spire as made only M0305 is a walk from the door, and a new test pins that.

4. Sweep (d6cca154)

  • Process wording: dressPlans.ts:281 and dressDoors.ts:5, 128 and 253 now give the design reason. I also fixed the same wording in two comments in ClassicDungeonGuards.spec.ts.
  • dressPlans.ts:113: the circle lands "beside the Big Chest (50 gold and a Potion)".
  • temple_floors.py: Wind is read from the dress step's files like every temple.
  • TempleChestReveal.vue: the comment says rows start unticked except pre-ticked ones (Pegasus egg and whistle), and that a whole drop comes all ticked.
  • classic-hera.spec.js: now points at docs/superpowers/plans/2026-10-19-classic-dungeon-temples-record/.
  • WindTemple.spec title: "the two cracked walls left".
  • withHandRooms: its doc comment is moved back above it.
  • ClassicPlan.trap: removed, with all nine entries and the fixture's.

Nothing read trap, so the dress output is unchanged. I did not run or bundle dress.ts, and the committed temple modules are untouched.

Gate

Every spec was run one at a time with chemx test tests/unit/<Name>.spec.ts, all green on the final tree:

Spec Passed
RoomInteractions 152
TempleSolver 55
quickStartTemple 29
importAllTemples 30
ClassicTempleRefresh 5
wakeOnEntry 54
WindTemple 13
ClassicTemples 172
ClassicDungeonGuards 91
TempleCreatorRoomContent 62
TempleSystemCleared 31

I also ran the specs I touched or that load the changed modules, all green:

Spec Passed
TempleBattleReturnDrop 5
FloorOrder 8
ClassicDress 48
ClassicDressEagle 18
RoomInteractionsModules 30
TempleGenerator 10
ShippedTemplesAudit 162
CompendiumPackImport 6
TempleOpenOrder 1
TempleCreatorHook 57
TempleDataComposable 75
XpTempleWizards 21
TempleCreatorMazeReadouts 38
FightDrops 42
TempleSystemFloors 20

chemx typecheck: clean.

Audit, npx --yes chemx@26.10.19-530 audit --no-index --unroll > /tmp/audit-final-fix.txt:

  • The rule counts match a baseline taken after commit 1.
  • The diff shows only the file and line totals.
  • No touched file appears under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN, TYPE_COLOCATION, HOOK_RETURN_OVERLOAD, UNGUARDED_LOGGING or CONTROL_FLOW_CASCADE_GUARDS. TempleGrounds.ts appears only under HOOK_SATURATION, as in the baseline.
  • git checkout -- chemx-ratchet.json was run; the tree is clean.

File sizes: TempleGrounds.ts 500, useTempleEntry.ts 139, quickStartPlan.ts 217, quickStartTemple.ts 103, importAllTemples.ts 299.

Files

  • src:
    • lib/engine/core/dungeons/floorOrder.ts
    • lib/services/temples/roomInteractions/hazards.ts
    • lib/engine/dungeons/generator/templeGraph.ts
    • app/Admin/XpCompendium/views/XpTemples/components/XpTempleCreator/hooks/grid/roomContent.ts
    • app/Console/TempleGrounds/templeGrounds/useTempleEntry.ts
    • app/Console/TempleGrounds/TempleGrounds.ts
    • app/Console/TempleGrounds/components/TempleChestReveal.vue
    • lib/services/temples/quickStartPlan.ts (new)
    • lib/services/temples/quickStartTemple.ts
    • lib/engine/core/dungeons/importAllTemples.ts
  • tools:
    • temples/classic/dressPlans.ts
    • temples/classic/dressDoors.ts
    • art/temple_floors.py
  • tests:
    • unit/RoomInteractions.spec.ts
    • unit/TempleSolver.spec.ts
    • unit/TempleCreatorRoomContent.spec.ts
    • unit/TempleBattleReturnDrop.spec.ts (new)
    • unit/TempleSystemCleared.spec.ts
    • unit/quickStartTemple.spec.ts
    • unit/importAllTemples.spec.ts
    • unit/WindTemple.spec.ts
    • unit/classicFixtures.ts
    • unit/ClassicDungeonGuards.spec.ts
    • e2e/specs/classic-hera.spec.js

Concerns

  1. Quick Start places two quests, not three, in the Eagle. Only M004 and M005 are a walk from the door. quick-homework is still seeded on the board but sits in no room, for new runs and for refreshed copies alike. This is the ruling's rule; the owner may want to know.
  2. Identifying placements by quest id has one blind spot. A Game Master who hand-placed one of tonight's quests alone into an otherwise untouched Wind copy has it read as Quick Start's, as described in item 3. Nothing is lost: the quest is placed again.
  3. Not checked: the wake memory after a refresh. A refreshed copy keeps each hero's Quick Start wake memory (wind-temple@1F woken on the Spire). I did not check how the Eagle's 1F sleeping rooms wake after the swap; it is outside this ruling.
  4. The drop reveal is proven at the composable level only. The spec fires Ionic's DidEnter hooks by hand. It has not been tried in a running app or Cypress.
  5. Process deviations.
    • One raw npx jest … -t debug run while diagnosing the hook name in the new spec.
    • One extra chemx typecheck mid-wave, besides the final one.
    • Both were mistakes against the chemx-only / one-typecheck rule; no other raw runs.

Table of Contents


Home - See Pages

Developer Docs

Start Screen

User Management

My Portal

My Home

The City (Hometown)

The World Map

The Dungeon

Game Master

Settings

Support & Glossary

About XP

Clone this wiki locally