Repository navigation
task 7 brief
Files:
- Create:
-
tools/temples/questPlan.ts:Schedule,PlanQuest,SCHEDULESandtemplateFor, moved out ofregenerate.ts. -
tools/temples/renderModule.ts:literal,camel, andrenderModule(id, temple, header: string[]), moved out ofregenerate.ts. tools/temples/classic/dress.tstests/unit/ClassicTemples.spec.tstests/e2e/specs/classic-hera.spec.js
-
- Modify:
-
tools/temples/regenerate.ts(imports the moved pieces;PLANSdropsstorm-temple) -
src/lib/engine/temples/storm-temple.ts(generated) -
tests/unit/ShippedTemplesAudit.spec.ts(split the regenerated block from a new classic block) - the seed refresh in
src/lib/engine/core/dungeons/importAllTemples.tsif Step 1's refresh test fails
-
Interfaces:
- Consumes:
ClassicDungeon(Task 5);RoomDrops,isBombSource(Task 1);generateTemple'sGeneratedTempleshape (rooms keyed by token, maze per floor). - Produces, from
dress.ts:interface ClassicPlan { name: string; theme: string; element: string; relic: string; shop?: string; trap: [string, number]; puzzle: string; mustClear: PlanQuest[]; minibosses: PlanQuest[]; boss: PlanQuest }-
CLASSIC_PLANS: Record<string, ClassicPlan>,CLASSIC_IDS: string[] dressClassic(id: string): GeneratedTemple-
renderClassicModule(id: string, temple: GeneratedTemple): string, with a header namingtools/temples/classic/dress.tsand the dungeon'ssource -
classicReport(id): { mustClear: number; repeats: string[] }, the beasts a must-clear room had to repeat - Run with esbuild exactly as
regenerate.tsis:… --outfile=/tmp/dress-temples.cjs && node /tmp/dress-temples.cjs [id]
Dress rules (from the spec's Dressing table):
-
Fights:
- One fight per LTTP
room: the screen with the most enemies (Review Focus 2). A shutter or enemy key-drop room is a must-clearmonsterwithlockOnEnter; the key goes indrops, and abigkey drop meansdrops: { chest: 'dungeon', dungeon: 'master-key' }. - A shutter room whose other side leads straight to the Big Key chest or the Big Chest is a
miniboss. - If the plan has more minibosses than that rule finds, the must-clear rooms nearest the Big Key chest, then the Big Chest, become the extra minibosses.
- Other enemy rooms are side
monsters without a seal. - The boss cell is
bosswithlockOnEnter.master(the Pegasus chest) goes in a free cell next to the boss, growing that floor's grid by one row or column if none is free.
- One fight per LTTP
-
Chests:
-
big-key→loot { chest: 'dungeon', dungeon: 'master-key' }. -
big-chest→loot { chest: 'dungeon', dungeon: plan.relic }. -
map,compassandsmall-key→ loot dungeon chests of the same item. -
potKey→ akeyroom. -
other→loot { chest: 'gold', gold: 25 }.
-
-
Other cells:
-
puzzle→puzzle { puzzleType: plan.puzzle }. -
stairsandhole→stairs-up/stairs-down/pitwithtargetLevel/targetCoords;teleport→ ateleportpair. - Empty cells, nearest the entrance first: one
shop(withspecialItem: plan.shop), onesave, onecamp { healthPoints: 15 }per floor, and the restempty.
-
-
Sides go into
sides:open→door,small-key→locked,big-key→masterlocked(and every side into the boss cell),bomb→bombable,shutter→door,item→door,wall→wall. -
Bomb chests: when
solveTemplefails because ofbombablesides and the temple has no reachable Bomb source, the nearest reachable empty cell before each such wall getsloot { chest: 'consumable', items: ['Bomb'] }. -
Chores:
-
mustCleartemplates go to must-clear monster rooms in path order from the entrance, easiest first. - Side rooms take the beasts of their floor's must-clear rooms in turn; a floor with none borrows the nearest floor's.
- A plan short of beasts repeats one and lists it in
classicReport().repeats. - Today's quests in
regenerate.tsPLANS[id].questsmust all stay in the plan, so starter quests still fill rooms.
-
-
Step 1: Write failing tests in
ClassicTemples.spec.ts, overCLASSIC_IDS:- the file is exactly
renderClassicModule(id, dressClassic(id)); - every JSON cell is a room at the same position, and every side matches the JSON per the side rules;
- each LTTP
roomholds at most one fight; - every must-clear room has
lockOnEnter; - must-clear beasts are distinct and of the theme, and side-room beasts come from must-clear rooms;
- every key drop sits in
drops; - every side into the boss cell is
masterlocked; - the audit passes and
solveTemple(...).solvableholds with no wall blown by hand; - every chest and every fight cell is reachable (Review Focus 3).
In
ShippedTemplesAudit.spec.ts:- split "the eight regenerated temples" into the remaining
SHIPPED_IDSand a classic block; - the size checks read
templeSizefrom the dressed rooms (≥ 21); - "used once" applies to must-clear rooms only.
A refresh test (Review Focus 1): a stored, unedited copy of the old storm layout, with a hero's saved run positioned on a cell that is a wall in the new one. After seed sync and
whenProgressReady, the hero stands on the entrance with no keys and emptyopenedChests. - the file is exactly
-
Step 2: Run
chemx test tests/unit/ClassicTemples.spec.ts. Expected: FAIL. -
Step 3: Move
questPlan.tsandrenderModule.tsout ofregenerate.ts. Then runchemx test tests/unit/ShippedTemplesAudit.spec.ts: the eight regenerated temples must still render byte-for-byte. -
Step 4: Implement
dress.tsand the Storm plan. The theme is bedroom and clutter, the relic is Storm's current relic chest item, and the plan keeps every beast in today's Storm plan. Generatestorm-temple.tsand dropstorm-templefromPLANS. -
Step 5: Run
ClassicTemples.spec.ts,ShippedTemplesAudit.spec.tsandClassicDungeonJson.spec.ts. Expected: PASS. -
Step 6: Write
classic-hera.spec.js:-
cy.seedWorld(), then log a hero in withwindow.__xp.loginUser. - Open the Storm Temple and walk
solveTemple'sorderwith the arrow controls, winning each sealed fight through the battle seam thequest-rooms.spec.jse2e uses. - Assert the Master Room's Pegasus chest opens.
- Run:
pnpm vue-cli-service test:e2e --headless --browser chrome --url http://localhost:<worktree port> --spec tests/e2e/specs/classic-hera.spec.jsagainst the worktree's own server, which the builder stops when done. Expected: PASS.
-
-
Step 7: Commit
feat(temples): the Storm Temple follows the Tower of Hera, dressed from its JSON.
- The converter was split:
tools/temples/classic/convert.py(CLI + run) andlttp_tables.py,lttp_layout.py,lttp_doors.py,lttp_contents.py; the pinned copies are undertools/temples/classic/vendor/.classicDungeon.tsgainedClassicLink.lock?: "small-key" | "big-key"(a locked stairway, recorded on the stairs link of the cell holding the locked door; 8 of them: storm 1, light 1, water 1, shadow 1, ice 2, fire 2).report.mustClear= distinct LTTP rooms with a cell holding enemies and (shutter or keyDrop); bosses/minibosses not counted. - Locked climbs land in task #1484 before you start: a stairs room with
content.locked: 'small' | 'master'asks for a small key (spent once per run) or the Boss Key on its first Climb, and the solver links it with that lock. Dress writescontent.lockedfromClassicLink.lock(small-key->small,big-key->master). Check the exact field name in master when you start (chemx q lockedin RoomInteractions/templeGraph). - Keep every file under 500 lines (the audit ratchet):
dress.tswill be large, so split it (e.g.dressRooms.ts,dressDoors.ts,dressChores.ts,dressPlans.tsfor CLASSIC_PLANS) withdress.tsas the entry. Name conditions inifs; give object-literal types a named interface.
- Bomb chests: put a Bomb chest (
loot { chest: 'consumable', items: ['Bomb'] }) in the nearest reachable empty cell before EVERY cracked wall whose blocking (bombable read as wall) cuts the boss off, whether or not a shop is reachable (the spec: "a Bomb chest sits before any cracked wall on the only way forward"). This replaces the plan's "only when the solver fails" wording. - Chores: must-clear plans start from today's beasts in
PLANS[id].quests(every beast ofSTARTER_QUESTSthat today's plan holds must keep a must-clear room, so starter quests still fill rooms), topped up with other beasts of the theme to the must-clear count; extras beyond the must-clear count leave that temple. Side rooms cycle through their own floor's must-clear beasts (a floor with none borrows the nearest floor's). -
ClassicTemples.spec's "must-clear beasts distinct" exempts the beasts listed inclassicReport(id).repeats.
- About 60 converted cells hold several contents (e.g. Skull Woods' entrance cell:
big-chest+teleport; Hera's boss cell:stairs+hole+teleport). One cell is one room type: define a precedence (entrance > boss > stairs/hole/teleport links > Big Key / Big Chest > key drop fight > chests > puzzle > enemies) and move the displaced content to the nearest free cell of the same LTTProom, else the nearest free cell on the floor, growing the grid by one row/column only if none is free. Record every move inclassicReport(id).movesand test that nothing is dropped (every chest, key, map, compass, Big Key and Big Chest of the JSON appears exactly once in the dressed temple). - GT Quad Pot (moon 3F, 12,24) has
enemies: 3that are all minibosses; do not count it as a must-clear monster fromenemies > 0alone (Storm does not hit this, but write the rule now so Task 8's Moon does). - Boss rooms reached only by stairs or a fall (Hera, Skull, Ice): the Boss Key must still gate the boss. Use a
big-key-locked climb (#1484) on the stairs that lead to the boss's floor when the boss is reached by stairs; when it is reached by a hole or teleport, put the Boss door (masterlocked) on the side into the cell the hero must cross before that hole. Test that the solver needs the Boss Key to reach the boss. - Sparse floors (Ice B6 17x3 with 5 cells, Hera 3F 17x2): trim each floor's grid to its used bounding box before writing the module.
- The JSON's entrance floor is named 1F; keep the temple's entrance on 1F (the floor-wake code assumes the entrance floor is where a run starts).
Run only chemx test tests/unit/ClassicTemples.spec.ts, chemx test tests/unit/ShippedTemplesAudit.spec.ts, chemx test tests/unit/ClassicDungeonJson.spec.ts (and any spec you create). Before committing, run npx --yes chemx@26.10.19-530 audit --no-index --unroll > /tmp/audit-t7.txt 2>&1, check no file you touched appears in LINE_BUDGET_FILE / CONTROL_FLOW_INLINE_BOOLEAN / TYPE_COLOCATION, then restore git checkout -- chemx-ratchet.json (the audit rewrites it; never commit it). The Cypress run (Step 6) needs a dev server: use your own worktree's server on its own port and stop it when done; if that is not possible, write the spec, report that it was not run, and mark DONE_WITH_CONCERNS.
- You work in your own isolated git worktree branched from origin/master; run
pnpm install --frozen-lockfile --prefer-offlinefirst. Commit on your branch only; the lead merges it into feat/classic-temples-2 and ships one PR. Never touch /home/xopher/www/x/do-it-for-the-xp itself (other sessions share it). Nevergit stash. - Use chemx only. Run only the spec(s) your brief names, one at a time:
chemx test tests/unit/<Name>.spec.ts. Never rawpnpm test|typecheck|lint|jestornpx jest, never a barechemx test, neverchemx verify, no typecheck/lint/build (the lead runs those once per wave; CI runs the suite on the PR). - Keep every file you create or grow under 500 lines (the repo's audit ratchet counts files over 500 as LINE_BUDGET_FILE). Name inline conditions in
ifs (const isX = ...; if (isX)), and give object-literal parameter types a named interface (TYPE_COLOCATION) — the ratchet blocks the PR otherwise. - Temple ids, names, regions and themes don't change (
wind-temple,earth-temple, …). Theme =id.replace("-temple", "")againstBEAST_LIBRARY[].theme. - Game rules go in pure modules under
src/lib/services/**(orsrc/lib/engine/**) with jest specs; screens stay thin. New components use<script setup lang="ts">; pages keepdefineComponentwith a sibling.ts/.scss. - In-world text comes from the copy catalogue (
src/lib/services/copy/catalog/, three voices) throughuseCopy(); in-world prompts useuseRevealPrompt(), never Ionic alerts. Sounds useplay$fx(<key of src/assets/fx/index.ts>). - Original beasts 001–093 are family art: never overwrite or re-point them.
- Quest difficulty is Fibonacci: must-clear monsters 1–2, minibosses 3–5, boss 8–13. Schedules follow
SCHEDULES(daily 19:00, am 08:00, bed 20:30, school weekdays, weekly 18:00, monthly custom). - Every door into a boss room is a Boss door (
isBossRoominauthoredDoors.ts). Bombs never open locked doors. - Keep comment density and naming like the surrounding code (long, plain-English doc comments).
- Do not dispatch subagents of any kind, and never a reviewer.
-
The field is
content.locked: 'small' | 'master'on astairs-up/stairs-downroom: the climb FROM that room is locked; its landing on the other floor climbs back freely. Check the final names in master (chemx q isClimbUnlocked,chemx q climbKey). -
A locked flight needs its own room token: a shared token would lock every cell that uses it. Its landing stairs on the other floor must not reuse the locked token, or the climb back would be locked too. Test this.
-
TempleGrounds.tsis exactly 500 lines: do not grow it. -
Never write
content.lockedon a loot room: DungeonManager reads it there as "chest locked".
- Architecture
- Component-Library
- Hooks-Reference
- Temple-System
- Battle-Field-Architecture
- current_context - For AI/Human Handoff
- User Menu
- General Settings
- Family Settings
- Sound Settings
- Notification Settings
- Reward Settings
- Party Settings
- Theme Settings
- Chore Settings