final fix report - xopherdeep/do-it-for-the-xp GitHub Wiki
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 |
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 gotmovedinstead 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 returnedmoved: 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.
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:
- It waits for
whenOpened, thenwhenProgressReady, thennextTick. - If the hero stands in the route's room and
roomActions(...).drop === 'waiting', it callsshowDropReveal()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
ionViewDidEnterwhere Ionic storesonIonViewDidEnter. - 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.
- The first RED run also had a test bug: it read
- GREEN:
✔ All tests passed (5 passed). - The gate run of
TempleSystemCleared.spec.tsthen failed 4 tests (onIonViewDidEnter is not a function). Its hand-made@ionic/vuedouble lacked the hook, so 943ded2d adds a no-op hook to the double:✔ 31 passed.
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.questIdsis exactly one ofQUICK_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
currentwhile the seed is the same, and onrefreshit carriesquickStartQuestIdson 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 becausequickStartPlandid 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
currenton the Spire, thenrefreshes to the Eagle with M005 quick-dishes and M004 quick-room, exactlyquickStartTemplePlan(eagle), and the next sync iscurrent. - (b) The copy stays
editedfor 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.
- 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
wholedrop 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.
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 [email protected] 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.jsonwas run; the tree is clean.
File sizes: TempleGrounds.ts 500, useTempleEntry.ts 139, quickStartPlan.ts 217, quickStartTemple.ts 103, importAllTemples.ts 299.
-
src:
lib/engine/core/dungeons/floorOrder.tslib/services/temples/roomInteractions/hazards.tslib/engine/dungeons/generator/templeGraph.tsapp/Admin/XpCompendium/views/XpTemples/components/XpTempleCreator/hooks/grid/roomContent.tsapp/Console/TempleGrounds/templeGrounds/useTempleEntry.tsapp/Console/TempleGrounds/TempleGrounds.tsapp/Console/TempleGrounds/components/TempleChestReveal.vue-
lib/services/temples/quickStartPlan.ts(new) lib/services/temples/quickStartTemple.tslib/engine/core/dungeons/importAllTemples.ts
-
tools:
temples/classic/dressPlans.tstemples/classic/dressDoors.tsart/temple_floors.py
-
tests:
unit/RoomInteractions.spec.tsunit/TempleSolver.spec.tsunit/TempleCreatorRoomContent.spec.ts-
unit/TempleBattleReturnDrop.spec.ts(new) unit/TempleSystemCleared.spec.tsunit/quickStartTemple.spec.tsunit/importAllTemples.spec.tsunit/WindTemple.spec.tsunit/classicFixtures.tsunit/ClassicDungeonGuards.spec.tse2e/specs/classic-hera.spec.js
- 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.
- 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.
-
Not checked: the wake memory after a refresh. A refreshed copy keeps each hero's Quick Start wake memory
(
wind-temple@1Fwoken on the Spire). I did not check how the Eagle's 1F sleeping rooms wake after the swap; it is outside this ruling. - 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.
-
Process deviations.
- One raw
npx jest … -tdebug run while diagnosing the hook name in the new spec. - One extra
chemx typecheckmid-wave, besides the final one. - Both were mistakes against the chemx-only / one-typecheck rule; no other raw runs.
- One raw