task 7 report - xopherdeep/do-it-for-the-xp GitHub Wiki

Task 7 report: the dress step and the Storm Temple from the Tower of Hera (board #1457)

Status: DONE_WITH_CONCERNS (the Cypress walk is not green, see "The e2e"; and the dressing deviates from the brief in three places the Tower of Hera's data forced; each is recorded in classicReport and below).

Commits on feat/classic-temples-2 (only my paths, git add <files>):

  • c2773bde refactor(tools): the quest plan words and the module renderer leave regenerate.ts, so the dress step can share them
  • f9b5cefa feat(temples): the Storm Temple follows the Tower of Hera, dressed from its JSON
  • 0a5d0d5d test(e2e): the Storm Temple walked along the solver's run, with a read seam for where the hero stands (the Cypress walk, not green yet, and the __xpTemples.state read seam it uses)

What I built

Shared pieces (Step 3)

  • tools/temples/questPlan.ts: Schedule, PlanQuest, SCHEDULES, templateFor, moved out of regenerate.ts verbatim.
  • tools/temples/renderModule.ts: literal, camel, renderModule(id, temple, header: string[]). regenerate.ts renders through renderShippedModule(id, temple) (its old header, built in shippedHeader). The seven regenerated modules are byte-identical: ShippedTemplesAudit's "is exactly what regenerate.ts writes" passed after the move (218 passed) before anything else changed.
  • regenerate.ts keeps PLANS, optionsFor, generateShipped, SHIPPED_IDS; storm-temple left PLANS; the header says where it went.

The dress step (Step 4), six modules under tools/temples/classic/, all under 400 lines

Module Lines Holds
dress.ts 170 entry: dressDungeon(dungeon, plan), dressClassic(id), classicReport(id), renderClassicModule(id, temple), the extras (shop, save, camp, empty), the CLI (esbuild ... --outfile=/tmp/dress-temples.cjs && node /tmp/dress-temples.cjs [id])
dressPlans.ts 58 ClassicPlan, CLASSIC_PLANS (Storm), CLASSIC_IDS, loadDungeon(id), DUNGEON_DIR
dressBoard.ts ~370 the board: cells read as items, one fight per LTTP room, the precedence, moves, growth, the Master Room
dressDoors.ts ~225 the side table into sides, one-ended small locks, the Boss door, Bomb chests, hopsFrom
dressChores.ts ~175 mini-bosses, must-clear monsters, side rooms, repeats / overflow / left
dressRooms.ts ~230 slots to rooms: tokens, content per kind (a lookup table), stairs and circle targets, locked climbs, the trimmed maze, placements

classicReport(id) returns { mustClear, repeats, overflow, left, moves, dropped, merged, bombChests, placements }: a superset of the brief's { mustClear, repeats } plus the handoff's moves. placements lists every room with its type, cell, the converted cell it came from, its LTTP room and whether the dressing added it; the specs and the next seven temples read the dressing through it.

The dress rules as implemented

One fight per LTTP room (Review Focus 2). The screen with the most enemies fights (ties: floor order, then reading order); the other screens' enemies fold into it (merged). The room is must-clear when any of its screens has shutter or keyDrop and it has enemies; the key drop becomes the fight's drops ({ chest: 'dungeon', dungeon: 'key' | 'master-key' }). A key drop in a room with no enemies lies in the open (key room; a Big Key becomes a Boss Key chest). GT Quad Pot's rule holds: enemies alone never make a must-clear room.

One thing per cell, by precedence: entrance > boss > must-clear fight > stairs > Big Key chest > Big Chest > other chests > pot key > puzzle > circle > side fight > hole. Two deviations from the handoff's suggested order, both forced by Hera (see Concerns): a must-clear fight outranks the stairs sharing its screen (the fight is what gates the doors there; the flight lands next door through targetCoords), and a hole comes last (a pit replaces its cell's walking doors, so one on a screen the hero must cross cuts the dungeon in two: with the handoff's order Hera's Startile Corner became a pit and the tower was unsolvable).

What loses its cell moves to the nearest free converted cell of the same LTTP room, else the nearest free converted cell of the floor, else a wall position inside the grid, else a grown row or column (Manhattan distance, ties by reading order; column before row). A room the dressing adds opens on one neighbour only, its anchor (the nearest to the source cell, a room displaced from the same cell first), so it never shortcuts a lock. Stairs, chests, keys and side fights always move. A displaced hole or circle is kept as a circle only when its landing is a circle (the ride comes back; Hera's fairy room); otherwise it is dropped and listed, as is a displaced riddle. Nothing in the no-drop list (chests, keys, map, compass, Big Key, Big Chest) is ever dropped: the spec counts each against the JSON.

The Master Room goes in a dead-end empty cell beside the boss, else a wall position inside the grid beside it, else a grown row or column; it opens on the boss only (both ends masterlocked).

Sides (sides, as the Temple Creator draws them): open / shutter / item -> door; small-key -> locked on the end the hero reaches first (BFS over the JSON, every non-wall side and link open) and door on the far end, so the two ends never both want a key; big-key -> masterlocked both ends; bomb -> bombable both ends; wall -> wall. Every way into the boss cell is masterlocked on both ends. A wall the dressing grew a room behind becomes a door to it.

Stairs are stairs-up/stairs-down by floorHeight, with targetLevel and targetCoords pointing at where the twin flight stands now (following a flight that moved). content.locked comes from ClassicLink.lock (small-key -> small, big-key -> master), and the flight whose landing was the boss's cell is locked master (the handoff's Hera/Skull/Ice rule; the boss's displaced landing sits beside him behind the Boss door as well). The hole-or-teleport-into-the-boss case is not needed by Hera and is left for the temple that needs it. A locked flight has its own token and its landing is free (every room has its own token).

Circles name their target: the cell their link names when it is a circle, else the circle on that floor that leads back here, else that floor's first circle. A lone hole is a pit.

Chests: big-key -> master-key; big-chest -> plan.relic; map, compass, small-key -> the item; other -> 25 gold; a Bomb chest -> { chest: 'consumable', items: ['Bomb'] }. Puzzle -> { puzzleType: plan.puzzle }.

Bomb chests (lead ruling): with every crack read as a wall, each cracked wall the hero stands before and cannot pass gets a Bomb in the nearest empty converted cell in reach before it (a room added beside it if none), those cracks count as open and the walk is tried again until the boss is in reach. Whether or not a shop is reachable.

Chores (lead ruling): the plan lists starters first. A mini-boss is a must-clear room whose door opens straight on the Boss Key chest or the Big Chest (post-move cells); short of the plan's count, the must-clear rooms nearest the Boss Key chest, then the Big Chest, are promoted; beyond it, the rest stay must-clear monsters; mini-boss templates go by path order. The must-clear monster rooms take the plan's first N chores, easiest first in path order (stable); a plan shorter than N cycles and lists the repeats. Starters beyond N get side rooms nearest the entrance (overflow); other extras leave (left). Remaining side rooms cycle their floor's must-clear templates (per-floor counter), a floor with none borrowing the nearest floor's (lower first), a temple with none the plan's first chore (reported as a repeat). Fight content mirrors the generator's: difficulty: 'easy' | 'normal' by template difficulty, theme: plan.theme, lockOnEnter on sealed rooms, the boss with element and difficulty: 'boss', the mini-boss hard.

Extras: free converted cells nearest the entrance (path hops, ties by floor order then reading order): one shop (specialItem: plan.shop), one save, then one camp per floor (healthPoints: 15), the rest empty. Bomb chests take free cells first.

Trim: trailing empty rows and columns of a floor are cut (the converter sizes a grid to the dungeon's extent). Leading ones cannot be cut without moving every cell, so Hera's 3F keeps its 17 rows (see Concerns).

The Storm Temple (Tower of Hera): every move, repeat and override

6 floors (B1, 1F, 2F, 3F, 4F, 5F), 31 rooms, entrance [1, 1, 0] (1F), 2 must-clear rooms, 1 mini-boss, 0 Bomb chests, size 22 (>= 21). Plan: mustClear bed-basilisk 1 daily am, shoe-shrew 1 daily, toybox-troll 2 daily, mess-hog 2 daily, piece-poltergeist 2 weekly (today's beasts, starters first); mini-boss under-bed-bogey 5 weekly; boss clutter-chimera 8 weekly; relic Lightning Rod; no shop; trap and puzzle as today (unused: Hera has no trap cell and both riddles were displaced).

Rooms: B1 Tile Room = stairs up (the locked flight's free landing), Tridorm = mini-boss (its south door opens on the Boss Key vault), Basement Cage = stairs up, Torches = Boss Key chest; 1F Hera Back (0,0) = stairs down locked small, (0,1) = map chest, Lobby = entrance, Up Stairs Landing = stairs up; 2F Startile Wide (0,0) = side fight, (0,1) = stairs up, Corner = must-clear shoe-shrew, Beetles = must-clear bed-basilisk (nearest the entrance, easiest first is a tie); 3F (0,0)/(0,1) stairs, (1,0) side fight, (1,1) Lightning Rod, Fairies = circle; 4F (0,0) stairs down, (0,1) stairs up locked master, (1,0) circle to the fairies, (1,1) side fight; 5F boss, landing, Master Room.

Moves (11): small-key chest B1 (1,0)->(2,0) added; side fight B1 (1,0)->(2,1) added; side fight B1 (1,1)->(1,2) added; map chest 1F (0,0)->(0,1); side fight 1F (0,0)->(0,2) added; stairs 1F (1,0)->(1,2) added (the lobby's flight to the cage); stairs 2F (1,1)->(1,2) added (Beetles' landing from 1F); big-chest 3F (0,0)->(1,1); compass chest 3F (0,1)->(2,1) added (a wall position inside the 17-row grid); teleport (the hole into the fairy room) 4F (0,1)->(1,0); stairs 5F (0,0)->(0,1) added (the boss's landing, behind the Boss door).

Dropped (10): the riddles of the Tile Room and Torches (displaced by stairs / the Boss Key chest); the star-tile holes of 2F (0,0), (1,0), (1,1), the holes of 3F (0,0), 4F (0,0) and the boss cell, the warps of 3F (0,0) and the boss cell: each lands on a cell that is no circle. Merged (8): the lesser screens of Hera Back, Startile Wide, Hera 4F, Hera 5F.

Overrides: overflow: ['toybox-troll'] (the third starter has a side room at 1F (0,2), the first side room past the map); left: ['mess-hog', 'piece-poltergeist']; repeats: []. Side rooms repeat bed-basilisk (B1 (2,1), 2F (0,0), 3F (1,0), 4F (1,1)) and shoe-shrew (B1 (1,2)); the boss's element is storm.

Solver notes accepted: four R8 twin notes for the two relocated flights (each end of 1F (1,1)<->2F (1,2) and 1F (1,2)<->B1 (1,0)); solvable holds, every room is in reach, the audit passes, and with the Boss Key chest turned to gold the solver is stuck on "the master key is out of reach".

The layout switch (Review Focus 1)

importAllTemples.ts: a refresh first starts every hero's run in that temple over (startRunsOver): a run the engine still holds is dropped (forgetTempleRun, so neither refreshRun nor a queued save carries it over), and every saved record is rewritten with progressForNewLayout (new in templeReset.ts, with entranceOf): the new entrance, no keys, Boss Key, map or compass, nothing visited, won, opened or emptied; keyItems (the Pegasus), the cleared badge, timesCleared, bloodMoons and a scheduled moon stay. TempleProgressDb.getProgressForTemple(templeId) is new. A failure is warned about and never blocks the layout write. importAllTemples.spec's "never touches hero progress" is reworded to what is now true (it still passes: its fake progress store is not the app's).

The e2e (Step 6)

tests/e2e/specs/classic-hera.spec.js seeds the battle fixture, signs hero-1 in, opens /temple/storm-temple/hero-1 and walks the solver's critical path with the arrow controls: east to the lobby's relocated flight, down to the cage, the small key chest, back up and north to the locked flight ("Unlock and climb", the key count goes 1 -> 0), the Tridorm (won through the Battle Field: Tackle each chore of the Under-Bed Bogey's checklist), the Boss Key chest, back up, the stairs to 2F (the floor-wake prompt's Onward when it shows), Bed Basilisk and Shoe Shrew (both sealed, both won), through the side room to 3F and 4F, "Unlock and climb" with the Boss Key, the Boss door (door-unlock), Clutter Chimera, then the Master Room, where it asserts the Pegasus chest opens ([data-cy=pegasus-reveal]). It reads where the hero stands through a small dev-only seam added beside __xpTemples.save: __xpTemples.state(templeId) (the route only names a cell when a battle hands the hero back). It walks the critical path, not every room of order (the fairy room, the compass nook and the three dead-end side rooms are off it).

Cypress result: NOT PASSING (the lead stopped the iterations at five). Run against the worktree's own server (pnpm vue-serve --port 8103 --host 127.0.0.1, stopped afterwards; the shared 8100 untouched) with pnpm vue-cli-service test:e2e --headless --browser chrome --url http://localhost:8103 --spec tests/e2e/specs/classic-hera.spec.js. What each run taught (screenshots 01-05 in tests/e2e/screenshots/classic-hera.spec.js/ are from the farthest run):

  1. The arrows, the climbs, the locked flight and the seam all work; a chest room has no choice sheet (a tap brings the chest reveal up itself). Fixed.
  2. The chest's Take wants its loot rows ticked first. Fixed.
  3. Test 1 passed (the small key, the locked flight, the key count 1 -> 0). Test 2's fight: a dormant mini-boss plays as a classic fight with the temple's own beast (the Storm Drake, one task), not the template's Under-Bed Bogey, so the enemy-name and checklist assertions were wrong. Fixed with a generic fight loop (Tackle the first chore not done until the victory card).
  4. Farthest run: test 1 passed; test 2 beat the Tridorm, took the Boss Key (shots 04, 05) and failed at the climb back: after a battle hands the hero back, an earlier copy of the temple page lingers hidden (.ion-page-hidden) and the sheet selectors matched that copy (<div.choice-reveal.kind-sheet> under a page with display: none). Fix: scope the sheet and the room controls to the shown page.
  5. Scoping every selector to the page broke test 1 at the first chest: the chest reveal (and the door and Pegasus reveals) render outside the page element. The committed spec scopes the sheet and the room controls (run 4's need) and leaves the three reveals unscoped (runs 3 and 4 proved them); that exact state has not been run (no sixth iteration, per the lead). Tests 3 and 4 never ran past their first step in any run (they start where test 2 ended).

Still open for whoever picks it up: run the committed state; if the lingering page also carries a hidden chest reveal, scope those three reveals to the page's own sibling (the reveal is a teleported overlay: find its mount point) or match on :visible; then the Boss door step (door-unlock, the hero may or may not step through on done) and the Pegasus assertion are untested in the browser.

TDD evidence

RED (before any implementation; the three new specs all failed to compile on the missing modules, then the refresh spec failed on behaviour once dressPlans.ts existed):

chemx test tests/unit/ClassicTemples.spec.ts
  ✖ Test suite failed to run  TS2307: Cannot find module '../../tools/temples/classic/dress'
chemx test tests/unit/ClassicDress.spec.ts
  ✖ Test suite failed to run  (import { dressDungeon } from "../../tools/temples/classic/dress")
chemx test tests/unit/ClassicTempleRefresh.spec.ts
  ✖ Test Failures (3 failed out of 4)
    puts the hero on the new entrance with no keys ...: expected currentLevel "1F" ... (the old run was kept)
    starts every other hero's run in that temple over too: ... (kept)
    drops a run the engine still holds in memory ...: expected undefined, received {"currentLevel": "2F", "currentPosition": [3, 4], "playerKeys": 1, "hasMasterKey": true, ...}

Step 3 (the move, byte-identical): chemx test tests/unit/ShippedTemplesAudit.spec.ts -> All tests passed (218 passed).

GREEN (final):

chemx test tests/unit/ClassicDress.spec.ts          All tests passed (24 passed)
chemx test tests/unit/ClassicTemples.spec.ts        All tests passed (20 passed)
chemx test tests/unit/ClassicTempleRefresh.spec.ts  All tests passed (4 passed)
chemx test tests/unit/ShippedTemplesAudit.spec.ts   All tests passed (215 passed)   # 218 before: the classic block merged three small tests
chemx test tests/unit/ClassicDungeonJson.spec.ts    All tests passed (106 passed)
chemx test tests/unit/importAllTemples.spec.ts      All tests passed (24 passed)    # its subject changed, so I ran it

Two implementation bugs the specs caught on the way: the handoff's hole-above-fight precedence made Hera unsolvable (ClassicTemples: solver + reachability), and the anchor tie-break's sign was flipped (the Cage's beasts hung off the Boss Key vault instead of behind the Cage's key chest; now pinned in the Storm block).

Tests written: ClassicTemples.spec.ts (the brief's Step 1 list over CLASSIC_IDS, plus the Storm pins); ClassicDress.spec.ts (the rules on five hand-made dungeons in classicFixtures.ts: doors, a key drop, a pot key, the mini-boss before the Boss Key, a displaced Boss Key chest, chores dealt and repeated, extra mini-bosses, trim and growth, the Bomb chest on the only way, a lone hole, a circle from a hole, a dropped riddle, the boss reached by stairs, the extras and borrowed beasts); ClassicTempleRefresh.spec.ts (Review Focus 1, with stormBeforeHera, the old layout verbatim); ShippedTemplesAudit's split (seven regenerated, a classic block, sizes and counts from the dressed rooms, "once" per sealed room, deduped starter fill).

Audit

npx --yes [email protected] audit --no-index --unroll > /tmp/audit-t7.txt, run three times (after the first implementation, after the fixes, after the seam). First run named my files under CONTROL_FLOW_INLINE_BOOLEAN (4 sites) and CONTROL_FLOW_DISPATCH_SWITCH (3 switches, 39 against the ratchet's 36) and one CRITICAL nested ternary: all fixed (named conditions, lookup tables, early returns). Final run: none of my files under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN or TYPE_COLOCATION; DISPATCH_SWITCH back at 36; no CRITICAL. The only mention left is UNGUARDED_LOGGING for the CLI's console.log lines in dress.ts (the same lines regenerate.ts has always had). chemx-ratchet.json restored with git checkout -- chemx-ratchet.json after each run; never committed.

Files changed

Created: tools/temples/questPlan.ts, tools/temples/renderModule.ts, tools/temples/classic/dress.ts, dressBoard.ts, dressChores.ts, dressDoors.ts, dressPlans.ts, dressRooms.ts, tests/unit/ClassicTemples.spec.ts, tests/unit/ClassicDress.spec.ts, tests/unit/ClassicTempleRefresh.spec.ts, tests/unit/classicFixtures.ts, tests/e2e/specs/classic-hera.spec.js. Modified: tools/temples/regenerate.ts, src/lib/engine/temples/storm-temple.ts (generated), src/lib/engine/core/dungeons/importAllTemples.ts, templeReset.ts, templeFreshness.ts (the state seam), src/lib/databases/TempleProgressDb.ts, tests/unit/ShippedTemplesAudit.spec.ts (499 lines), tests/unit/importAllTemples.spec.ts. Every file I created or grew is under 500 lines. Not run (per the rules): typecheck, lint, verify, build.

Concerns

  1. Starter overflow (lead ruling vs Hera). The ruling wants every starter beast of today's plan in a must-clear room; Hera has two must-clear rooms and today's Storm plan holds three starters. Smallest deviation: Toybox Troll keeps a side room of its own, nearest the entrance, so quest-toys still fills a room (classicReport.overflow); the side room is unsealed, as side rooms are. The alternative (dropping the third starter) loses a starter fill.
  2. Precedence differs from the handoff's parenthetical in two places (must-clear fight above stairs; hole last), forced by solvability and the shutter gating; recorded above. A relocated flight leaves R8's twin note in the solver's reasons (solvable is untouched; play and the solver follow targetCoords); the classic specs accept exactly that note and nothing else.
  3. Size. With two must-clear rooms the Storm Temple reaches 22 only because side rooms (repeats) count toward templeSize, as the audit spec's size check reads them; the distinct chores add up to 15.
  4. Hera 3F stays 17 rows (the fairy room sits at row 16): the brief's trim applies to trailing rows only, since cutting leading rows would move every cell. The map shows 14 empty rows between the compass nook and the fairy room.
  5. No shop, save or camp in Storm: the tower has no empty converted cell. The plan's trap is unused by the dressing (no converted cell is a trap); kept in the interface as the brief names it.
  6. Big-key doors between two ordinary rooms (Hera's Startile Corner/Wide) are written masterlocked as the table says, but the engine's Boss door rule applies only to doors into a boss room, so in play and in the solver they are plain doors (the Boss Key still gates the boss through the locked climb and the Boss door).
  7. The CLI's unhandled rejection guard: dressChores reads STARTER_QUESTS, which pulls the app's browser stores along; under node their init rejects after the modules load. The CLI block swallows exactly that message and rethrows anything else. A leaner starter list would remove it.
  8. The hole/teleport-into-the-boss gate (Skull, Ice) is not implemented; the stairs case is, with a test (DROP fixture).
  9. ClassicTemples.spec compares the module on disk with renderClassicModule(id, dressClassic(id)); any change to the dressing needs the CLI run again (as with regenerate.ts).

Fix round 1 (task-7-fix1.md)

Status: DONE_WITH_CONCERNS (every item of the list done; the e2e's one attempt got two of four tests green and is skipped with a STATUS note, as ruled). Commits on feat/classic-temples-2, tree clean:

  • b8577b6c feat(temples): a pit can name where it drops, and a flight that names its landing is paired there
  • 760f7812 fix(temples): the dress step keeps every hole, gates a fall onto the boss, and compacts far-off island cells
  • 8d007bdc fix(temples): opening a temple waits for the shipped temples' seed sync, and the sync's header says what it does to runs
  • d5ae254a test(e2e): the Storm Temple walk reaches the star-tile rooms, and is skipped until it is green

1. Pits land where the dungeon says

  • Engine: hazards.ts (pitFloor, fallInPit(room, ctx)) reads content.targetLevel (any lower floor) and targetCoords ({x: col, y: row}) and lands there first; a pit that names none drops as before (the next floor down, the same cell, else the first stairs up). onEnterRoom's "floor below" check uses the same.
  • Solver: templeGraph.linkPits reads the same fields; the R8 note names the cell the pit named when it is a wall.
  • Dress: every hole becomes a pit with the target of its JSON link (dropTarget); a displaced hole is never dropped (relocated to the nearest free cell, added beside its source when none; moves says so); no hole becomes a circle any more. A circle whose landing is no circle (Hera's fairy room warps onto a stairs cell) gets a circle added beside its landing, pointed back (placeLandingCircles), so no circle fizzles and no island is a trap.
  • Two deviations recorded: the boss room's own exits are settled last (its hole is a pit beside his landing, never touching the boss cell, which would be a second door into his room; its warp keeps the displaced-circle rule and is dropped when its landing is no circle); a displaced circle still follows the old rule (kept only when it lands on a circle), the fix list ruling on holes only.
  • Specs: RoomInteractions.spec ("names its landing", "a one-room floor"), TempleSolver.spec ("R8 Pits: ... any lower floor"), ClassicDress.spec (DROP: the lone hole's target, the moved hole, the landing circle), ClassicTemples.spec ("drops through every hole of its dungeon exactly once, where the dungeon says").

2. A boss reached by a fall still needs the Boss Key

dressLinks.placeAntechamber: when any hole or circle lands on the boss's cell, an added circle beside the boss (the Boss door between, both ends masterlocked) takes the landing, paired with a circle added beside the entrance (the way back out once the boss is beaten, which R2 wants). Pits and circles that named the boss land in it. Spec: the FALL fixture (ClassicDress.spec "a boss reached by a fall alone"): solvable with the Boss Key, "the master key is out of reach" without it; ClassicTemples.spec checks a fall onto the boss lands beside him behind a masterlocked side.

3. The e2e walk (one attempt)

Run against the worktree's server on 8103 (stopped afterwards): 2 passing, 2 failing (e2e-hera-6.log). Passed: test 1 (the small key, the key count 1 -> 0, the locked flight) and test 2 (the Tridorm won through the Battle Field, the Boss Key chest). Test 3 beat the Bed Basilisk and the Shoe Shrew (screenshot: "Shoe Shrew was defeated", a level up) and failed leaving the Shoe Shrew's victory card: the hero levelled up, the card was still counting when the spec tapped it, and the Battle Field stayed up past the 30 s timeout (expected '/battle/hero-1/temple/storm-temple/2F/0/1' to equal '/temple/storm-temple/hero-1/0/1'). Test 4 cascades (never ran past its first step). As ruled: the describe is describe.skip with a STATUS note; leaveVictory (tap the card until the temple is back) is written in but unrun. Screenshots 01-06 in tests/e2e/screenshots/classic-hera.spec.js/. The three reveals read their visible copy ([data-cy=chest-reveal]:visible ...), the sheet and room controls the shown page.

4. The smaller fixes

  • Big-Key doors between ordinary rooms: drawn door (the boss's own doors stay masterlocked), listed in classicReport(id).plainBigKeyDoors (Hera: 2F (0,0) south, Startile Wide to the Corner). Specs in both classic specs.
  • Many-screen must-clear rooms: a flight displaced from a must-clear fight skips every cell of that LTTP room (PlacementLimits.avoidRoom); the TWO SCREENS fixture lands it beside the fight. ClassicTemples.spec checks that blocking the fight screen blocks the room as a passage (the outside reach is the same as with the whole room blocked).
  • Sparse floors: compactIslands moves an island (walls on every side) that lies outside the ring around the other cells' bounding box to the nearest free position on that ring, before anything is read; classicReport(id).compacted lists it and every link follows (board.converted). Hera's 3F is 4 rows (the fairy room at (2,0)), not 17.
  • R8: the engine pairs a flight with the stairs its targetCoords name (no note); ClassicTemples.spec asserts every flight's landing is a stairs room of the opposite kind and solveTemple(...).reasons is [] (no filter). This also fixed TempleSolver.spec's R12 block, which the Storm module had been failing since f9b5cefa (not in round one's gate).
  • Bomb chests: only a crack whose closing alone cuts the boss off earns one (two side by side: the nearest does); the chest's cell is reached from the entrance over unlocked doors (hopsFrom(graph, start, passable)), nearest the wall's near end, else a room added beside it. CRACK fixture: a dead-end crack gets none; CRACK locked: the yard before the small lock gets it, not the nearer anteroom.
  • No empty cell: one shop added beside the entrance (Hera: 1F (2,0)), no save or camp. CORRIDOR, TOWER, FALL, Storm.
  • ShippedTemplesAudit.spec: 458 lines; the regenerated count is SHIPPED_IDS; the classic block moved into ClassicTemples.spec (400 lines) as one test.
  • importAllTemples.ts's header says the one case it touches progress; the fake progress test is gone from importAllTemples.spec (it kept a temple-store-only check); ClassicTempleRefresh.spec gained the real one: a current or an edited layout leaves every run alone.
  • First launch: useTemple.openTemple awaits ensureBuiltInTemples() before ensureTempleFresh; new tests/unit/TempleOpenOrder.spec.ts holds the sync and shows the record is not read until it settles.

Storm Temple after the round

6 floors, 39 rooms (was 31), 2 must-clear, 19 moves, 4 dropped (the two riddles, the two warps of 3F (0,0) and the boss cell), 0 Bomb chests, size 22; compacted 3F (16,0) -> (2,0); pits at 2F (0,2), (1,2), (2,0), 3F (2,1), 4F (1,0), (0,2), 5F (1,1), each with its target; the fairy room's circle at 3F (2,0) rides to an added circle at 4F (1,2); the shop at 1F (2,0); the compass at 3F (3,0). Every rule of ClassicTemples.spec holds (25 tests); the Storm pins are updated. The e2e walk's 2F landing is now (2,1), under the Beetles room.

Gate

chemx test tests/unit/RoomInteractions.spec.ts      All tests passed (148 passed)
chemx test tests/unit/TempleSolver.spec.ts          All tests passed (53 passed, 1 skipped)   # the pre-existing it.skip
chemx test tests/unit/ClassicDress.spec.ts          All tests passed (31 passed)
chemx test tests/unit/ClassicTemples.spec.ts        All tests passed (25 passed)
chemx test tests/unit/ClassicTempleRefresh.spec.ts  All tests passed (5 passed)
chemx test tests/unit/importAllTemples.spec.ts      All tests passed (24 passed)
chemx test tests/unit/ShippedTemplesAudit.spec.ts   All tests passed (210 passed)
chemx test tests/unit/ClassicDungeonJson.spec.ts    All tests passed (106 passed)
chemx test tests/unit/TempleOpenOrder.spec.ts       All tests passed (1 passed)

RED first each time: the two engine specs failed on the untargeted fall / the unnamed pit door; the dress specs failed to compile on compacted / plainBigKeyDoors; the open-order spec was written against the new behaviour.

Audit (npx --yes [email protected] audit --no-index --unroll > /tmp/audit-t7f.txt, three runs, ratchet restored after each): none of my files under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN, TYPE_COLOCATION or HOOK_RETURN_OVERLOAD; the counts match the ratchet (LINE_BUDGET 36/4, INLINE_BOOLEAN 5/4, HOOK_RETURN_OVERLOAD 19, DISPATCH_SWITCH 36, CASCADE_GUARDS 14, after one nested ternary and one guard cascade of mine were fixed on the way); only the CLI console.log lines of dress.ts and regenerate.ts remain (UNGUARDED_LOGGING, as before).

Concerns

  • The e2e is skipped, two of four tests short (above); the remaining walk (3F, 4F, the Boss Key climb, the Boss door, the Pegasus) is untested in the browser.
  • The boss room's own exit links and displaced circles follow my rules above, not the fix list's (which rules on holes).
  • A fall-only boss floor gets two added circles (the antechamber, the way back beside the entrance): Earth and Ice will show that shape; the circle beside the entrance takes the nearest free cell, grown when none.
  • dressBoard.ts imports dressLinks.ts and back (functions only, called after both load); fine for jest and esbuild, worth knowing.

Fix round 2 (task-7-fix2.md)

Status: DONE_WITH_CONCERNS (every item done; the e2e stays skipped as ruled in round 1; two design concerns below). Commits on feat/classic-temples-2, tree clean:

  • dfdede88 test(temples): TempleSystemCleared's seed-sync mock answers ensureBuiltInTemples, which openTemple now awaits
  • c9d25086 fix(temples): the dress step keeps every circle, lands falls in dead ends, rides back beside the fall, and dresses Earth and Ice

1. The specs that mount useTemple

TempleSystemCleared.spec's mock of importAllTemples answers ensureBuiltInTemples (resolving). Run one at a time, all green: TempleSystemCleared 31, TempleSystemFloors 20, DragonEggRoom 50, FightDrops 42, TempleChestTake 21, TempleKeyCount 7, BossKeyChest 19, TempleMoveIcons 6, TempleProgress 34 (the one more spec chemx q/grep found that imports useTemple), TempleOpenOrder 1. The specs without a mock run the real sync on their in-memory storage: it creates the nine records and touches no run (only a refresh does), so nothing broke.

2. The antechamber's way back

dressLinks.placeAntechamber pairs the antechamber's circle with one added beside the room the hero fell from (fallSource): the cell the hole was converted in when the pit was pushed out of it (that cell holds what stayed, the Ice Palace's Antechamber fight), else the converted neighbour the hole's cell opens on (Skull Woods' Spike Corner before the Final Drop), else, for a hole in an island, the entrance. Specs: FALL (the circle back lands under the vault, the room before the drop; the shortest route to the Boss door is four doors, the same as through the hole); the in-memory Earth and Ice (the ride back lands within two cells of the fall's source, floor shift and compaction applied).

3. A relocated hole never lands on a passage

placeInDeadEnd: a free dead-end cell (one non-wall side) of the hole's own LTTP room, else a room added beside its source (tryOpenBeside with addedOnly, so a neighbouring room's dead end is never taken), else the nearest added room; placeNearest gained addedOnly. Holes are settled before every other displaced thing, so a pit gets a room's one dead end before a circle or a chest. Specs: CROSSROADS (the pit takes the room's east dead end, never the passage between), DROP and ClassicTemples ("no pit cell has more than one open side unless it is the hole's own cell"). Ice's B1 is whole again (R1 gone).

4. The Skull Woods rulings

  • Displaced circles are relocated like holes and never dropped; placeLandingCircles pairs a circle whose landing is no circle with the circle on that floor that leads back to it, else adds one beside the landing. report.dropped now holds riddles only (Storm: two).
  • openNear (tryOpenBeside, else the nearest added room) replaces every throwing openBeside: the Master Room, the antechamber, landing circles, the shop.
  • The Bomb chest with no reachable empty cell before the crack is added beside the first reachable room on the entrance side (the wall's near end first) that has a free side, else the nearest added room; the addition is a recorded move. Earth no longer throws (its chest sits at 1F (2,5)).
  • shiftFloors (after compactIslands): a floor whose cells all start past the first row or column moves up and left; report.shifted lists the shift per floor and nowAt in the specs applies it. Ice's B6 is 5x3, not 17x3. (trimmed keeps cutting trailing rows and columns; the leading ones are cut on the board, before any link is read.)
  • Acceptance: ClassicTemples.spec "the next temples, dressed in memory" dresses earth-temple and ice-temple under the Storm plan as a stand-in (no module written): both build, solveTemple(...).solvable, no R1, the audit passes, the Boss Key is needed, and the ride back from the boss lands beside the fall's source. Ice still carries an R4 note (its Boss Key chest is reachable without passing a mini-boss): a plan matter for Task 8 (which must-clear room becomes the mini-boss), not a reachability one.

Storm Temple after the round

43 rooms (was 39): Hera's two warps are kept as circles (the Big Chest screen's at 3F (3,0), riding to a circle added beside the locked flight on 1F (2,0); the boss's at 5F (1,1), riding to one beside the 3F stairs at (0,2)), the holes pushed out of their screens sit in dead ends (2F (0,2), (1,2), (2,0); 3F (2,1); 4F (0,2), (1,2); 5F (0,2)), the compass at 3F (3,1), and the one empty converted screen left (4F (1,0), whose beasts merged into the room's fight) is the shop, so none is added beside the lobby. Dropped: the two riddles. plainBigKeyDoors unchanged.

Gate

chemx test tests/unit/ClassicDress.spec.ts          All tests passed (35 passed)
chemx test tests/unit/ClassicTemples.spec.ts        All tests passed (32 passed)
chemx test tests/unit/ClassicTempleRefresh.spec.ts  All tests passed (5 passed)
chemx test tests/unit/ShippedTemplesAudit.spec.ts   All tests passed (210 passed)
chemx test tests/unit/TempleSolver.spec.ts          All tests passed (53 passed, 1 skipped)
chemx test tests/unit/RoomInteractions.spec.ts      All tests passed (148 passed)
+ the ten useTemple specs above

RED first: the CROSSROADS, FALL way-back and in-memory Earth/Ice tests failed on the old tool (the circle beside the entrance, the pit on the passage, Earth's throw); the mock fix turned TempleSystemCleared from 9 failures to green.

Audit (npx --yes [email protected] audit --no-index --unroll > /tmp/audit-t7f2.txt, ratchet restored): none of my files under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN, TYPE_COLOCATION or HOOK_RETURN_OVERLOAD; every count as the ratchet (CASCADE_GUARDS 14, INLINE_BOOLEAN 5/4, LINE_BUDGET 36/4, DISPATCH_SWITCH 36, HOOK_RETURN_OVERLOAD 19), no CRITICAL; only the CLI console.log lines and useTemple.ts's pre-existing entries.

Concerns

  • Circles are two-way in the engine while LTTP's warp tiles are one-way. Keeping Hera's two displaced warps (the ruling) gives a two-way pair between a circle beside the lobby's locked flight and the Big Chest screen's circle on 3F; from there the fairy circle rides to 4F. The Boss Key still gates the boss, but a hero who finds the ride can skip 2F's two sealed star-tile chores. Options: make a relocated warp whose JSON landing is no circle one-way (an engine flag), or let a plan drop it; the lead decides.
  • The shop follows "the nearest empty cell" and so sits on 4F for Storm; the added-beside-the-lobby shop only applies when no empty cell exists. If a shop near the entrance is wanted regardless, that is a one-line rule change.
  • Ice's R4 note (above) is for Task 8's plan.
  • The e2e remains skipped (round 1's STATUS); the walk's 2F landing and the new rooms do not change its first three tests' path.

Fix round 3 (task-7-fix3.md)

Status: DONE_WITH_CONCERNS (every item done; the e2e stays skipped as ruled in round 1). Commit on feat/classic-temples-2, tree clean:

  • b48bcaf1 fix(temples): the dress step drops a warp whose ride back skips a seal, locks shifted floors on the right end, and keeps every pit out of passages

1. Storm's warps

dressDoors.demoteShortcutWarps, run right after the board is built: for each warp that got a landing circle added (board.warpPairs, recorded by placeLandingCircles), the must-clear rooms that block the boss are computed twice on the solver's graph, with the ride back cut (its door removed) and with it open; a room that blocks only with the ride cut means the ride goes around a seal, so both ends become empty rooms (kept: Storm's compass hangs off 3F (3,0)) and the warp is listed in dropped ("its ride back from ... opens a way around a must-clear room"). Pairs are judged one at a time in the order they were added. Storm: the Big Chest screen's warp is dropped (its ride from beside the lobby skipped the two star-tile seals); the boss's warp (3F (0,2) to 5F (1,1)) and the fairy room's pair stay. Spec, for every dressed temple: a sealed room that blocks the boss when the converted one-way warps take no ride back still blocks the boss in the dressed temple (ClassicTemples "keeps every must-clear room that blocks the boss when warps ride one way").

2. The small lock on a shifted floor

smallLockEnd looks the two ends up by the converted cells' own row and column (slot.cell), which the entrance-hop map is keyed by, not by slot.at (moved by shiftFloors). SHIFTED fixture (cells on rows 2-3): the floor moves up two rows, the lock sits on the pots' east side, the guard's west side is a plain door, solvable with no reason.

3. A flight pushed out of a seal

PlacementLimits.besideFirst: a flight displaced from a must-clear fight is added beside its source (tryOpenBeside with addedOnly), else the nearest added room, never a converted cell (Shadow's flight at 1F (6,1) no longer lands at (7,4) behind two cracked walls: Shadow solves again). TWO SCREENS gained an empty closet beyond the seal, the nearest free cell of the floor, which the flight must not take.

4. The lead's rulings

  • A hole in a passage (its own cell has two or more open sides) is displaced to a dead end added beside it and the passage stays an empty room (PASSAGE fixture; Moon's GT 5F (2,1) no longer cuts eight rooms off: Moon solves). ClassicTemples now asserts no pit of any dressed temple has more than one open side.
  • The shop (placeShop): on the entrance floor, in the empty converted cell nearest the entrance that is reached with the sealed fights and the boss blocked; with none, a room added beside the entrance. Storm's shop sits beside the lobby at 1F (2,1); the save takes 4F's empty screen.
  • addBombChestNear tries one room at a time and stops at the first free side.
  • Comments: dressBoard.ts's header (what moves where), dress.ts's shop rule, openNear's doc (why it never fails).
  • Guard spec: "every classic JSON, dressed in memory under the stand-in plan" pins, per temple, solvable, the solver's exact reasons and the audit's missing names as the tool makes them today: Earth and Light clean; Shadow, Ice, Fire and Moon solvable with one R4 note each (the mini-boss their plans will choose); Water stuck on the locked flight at 1F (0,8) with no key placed by the stand-in plan (R2) plus R4; Wind (the Eagle) with no Boss Key chest (the audit's "Boss Key", R2, R4). Task 8 flips each line as it dresses that temple; a tool regression turns it red.

Storm Temple after the round

44 rooms: the two warps' rooms stay (1F (2,0) and 3F (3,0) empty, the compass off the latter), the boss's warp pair and the fairy pair kept, the shop at 1F (2,1), the save at 4F (1,0); dropped: the two riddles and the 3F warp; plainBigKeyDoors unchanged. The e2e's first three tests walk the same cells as before.

Gate

chemx test tests/unit/ClassicDress.spec.ts          All tests passed (37 passed)
chemx test tests/unit/ClassicTemples.spec.ts        All tests passed (41 passed)
chemx test tests/unit/ClassicTempleRefresh.spec.ts  All tests passed (5 passed)
chemx test tests/unit/ShippedTemplesAudit.spec.ts   All tests passed (210 passed)
chemx test tests/unit/TempleSolver.spec.ts          All tests passed (53 passed, 1 skipped)

RED first: the SHIFTED, PASSAGE and closet tests and the one-way pin failed on the round-2 tool (36 of 37 in the dress spec, then the Storm pins); the nine-temple guard was pinned from the tool's output after the fixes.

Audit (npx --yes [email protected] audit --no-index --unroll > /tmp/audit-t7f3.txt, ratchet restored): none of my files under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN, TYPE_COLOCATION or HOOK_RETURN_OVERLOAD; every count as the ratchet (CASCADE_GUARDS 14, INLINE_BOOLEAN 5/4, LINE_BUDGET 36/4, DISPATCH_SWITCH 36, HOOK_RETURN_OVERLOAD 19), no CRITICAL; dressBoard.ts at 492 lines.

Concerns

  • The warp judgement is greedy, pair by pair in the order added; a dungeon where two warps only together open a way around a seal would keep both. None of the nine does today (the guard spec would show it as a lost seal only through the one-way pin, which runs on every dressed temple).
  • A demoted warp that was a lone circle in an island would leave the island with no way out; no dungeon has one (the fairy rooms' pairs are kept), and the one-way pin plus the solver would say so.
  • The e2e remains skipped (round 1's STATUS).

Fix round 4 (task-7-fix4.md)

Status: DONE_WITH_CONCERNS (every item done; all nine JSONs keep every boss-blocking seal with no exception; the concerns are judgment calls the data forced, listed at the end). Fresh implementer. Commit on feat/classic-temples-2 (only my paths), tree clean:

  • adee573d fix(temples): the dress step keeps every seal of its dungeon: what a sealed fight held waits behind it, warps land in front of it, keys stay before their locks

Structure

dressBoard.ts (492 lines) lost its placement code to two new modules; every file is under 400 lines: dressBoard.ts 351 (reading the cells, the settling order, the Master Room, buildBoard), dressPlace.ts 356 (where a displaced thing goes, the rooms the dressing adds), dressReach.ts 103 (the converted dungeon's walk without one cell: convertedHops, moved out of dressDoors; the board's walk with nothing in hand: walkFrom; isSeal, isPit, isLandedOn, walksThrough). ClassicTemples.spec.ts was 525 lines: its two in-memory blocks (the nine-JSON pin and "the next temples") moved to the new tests/unit/ClassicDungeonGuards.spec.ts (382) with the new guards; it is 456.

1. A flight (or anything) pushed out of a sealed fight

  • Settling order (settleRank): what a sealed fight's cell held goes first, its flight before the rest, then holes, then everything else. Moon's 5F Frozen Over flight now takes 5F (1,0) beside the fight (the hole moved off the Moldorm ledge passage used to take it).
  • placeBeyondSeal: beside the fight (a nook that opens on it alone, a wall position, a grown row or column), else beside a room the hero walks to only through the fight and no other seal (converted cells the converted dungeon reaches only through the fight, and rooms added off them; never a room a link brings the hero into from in front), else a row or column grown before the first when the fight is on the top or left edge, else (a chest or a key) the fight hands it out itself once won, else beside a room beyond a further fight (walked to from the fight with nothing in hand, so nothing waits on itself), else it throws. Never "the nearest added room anywhere".
  • A flight or a circle whose other end the hero reaches without the fight (in LTTP it landed the hero inside the fight) stands beside the fight or in front of it (openInFront, item 3's rule), never beyond: placed beyond, its twin would bring the hero past the seal.
  • A boxed-in fight's drops: Item.holds; dressRooms' dropsOf writes one Dungeon Item, further small keys as "key" rows and the gold summed (chestLoot and the grants read all three). Used by Ice B2 (1,2) Ice Right H (map + pot key: { chest: "dungeon", dungeon: "map", items: ["key"] }) and Moon B1 Bob's Torch (its small-key chest).
  • Growth before the first row or column (growBefore): every room of the floor moves down or right in place (anchors, targets and moves follow); the report's shifted takes it as a negative shift. Only a last resort (Storm and every fixture are untouched by it); used by Moon B1 (a column: GT DMs Room's gold chest), Fire 1F and Shadow B1/1F.
  • DROP's assertion is strict again: the flight takes the nook that opens on the fight alone (2F (0,3), grown: false), 3F's landing names { x: 3, y: 0 }. The dressBoard.ts header is rewritten to match.

2. The nine-JSON seal pin (ClassicDungeonGuards.spec.ts)

On the converted JSON's own graph (every side but a wall open, stairs both ways, holes and warps one way from their own cell; locks ignored), every must-clear fight (the screen with the most enemies of a room that shuts or drops a key) that blocks the boss must still block him in the dressed temple (dressed graph, every door open). Known exceptions: none. Boss-blocking seals kept / lost: Storm 2/0, Earth 4/0 (was 3/1), Ice 4/0, Wind 2/0, Fire 0/0, Light 2/0, Moon 15/0 (was 14/1), Shadow 0/0, Water 1/0.

Further guards, all nine: what leaves a sealed fight's cell is reached on foot only through it (or stands in front of it when its link brings the hero in, or is the fight's own drop); a landing circle for a warp onto a sealed fight opens on the fight or on rooms of its entry side; a key, Big Key, map, compass or Big Chest that moved is reached from its cell with nothing in hand; the shop is on the entrance floor before any key, Bomb or seal; every move lands on what it moved; every chest and key of the JSON is handed out exactly once (drops included); and the solver/audit pins.

3. Earth's East Lobby (1F (4,2))

placeLandingCircles now opens a landing with openInFront: beside the landing, else beside a room walked to from it with nothing in hand and no seal passed, on the entry side of a sealed fight (never beyond it). Earth: the West Lobby warp's landing circle is 1F (2,1), opening on the Back Drop screen west of the fight; the fight's own circle (1,2) hangs beyond the Big Key mini-boss (via (2,2)-(3,2)); the third section's warp back to the entrance is dropped (its ride back went around the fight: the round-3 rule). Guard "Earth: ... still guards Skull Woods' third section": pass.

4. A displaced key chest stays before its lock

placeWithinReach for a key, Big Key, map, compass, Big Chest (and gold): beside its cell (a nook walked into with nothing in hand, or an added room), else the nearest cell walked to from it with nothing in hand and no seal, or beside one of those, else beside it in a line grown before the first, then the old nearest free cell. Water: the entrance's small-key chest is 1F (0,7), beside (0,8), before the locked flight (guard "Water: ... never behind it": pass); the Water pin now reads solvable: true, reasons: ["R4: the master key at B1 (5,2) loot can be reached without passing the mini-boss"]. The guard comment no longer calls Water's R2 a property of the data. Nooks (isNookOf) must open on their room through a side walked with nothing in hand and be reached by no link (Earth's Big Key used to take the Lone Pot behind the Big Key room's cracked wall).

5. Smaller

  • placeShop reaches free cells over doors no key or Bomb opens and no seal (hopsFrom with a passable test); with none, openInFront(entrance). Earth's shop is no longer behind the entrance's cracked wall; the CRACK fixtures' shops moved off the cellar (behind a crack) and the anteroom (behind a small lock). placeExtras re-reads the board after the shop (a grown line moves positions).
  • demoteShortcutWarps removes a dropped pair's own moves (the warp's move out of its cell and its landing circle): Storm's moves no longer list "landing circle 1F(0,0)->1F(2,0)" or "teleport 3F(0,0)->3F(3,0)"; 21 moves (was 23).
  • dressBoard.ts's header says what is now true.

Storm Temple after the round

44 rooms. 2F: the Beetles' flight from the lobby is 2F (1,2), opening on the fight alone (was (2,1)); the two star-tile pits sit under their fights at (2,0), (2,1). 3F (3 rows, was 4): the Lightning Rod opens beside the stairs down at (0,2) (the Big Chest screen keeps its stairs), the compass takes the room's empty screen (1,1), the boss warp's circle is (0,3), the dropped warp's room (1,2). Everything else as round 3. The skipped e2e walk's 2F landing is updated to (1,2) (not run). ClassicTemples' entrance check reads nowAt (a grown line moves the entrance); its relic and gold counts include drops.

Gate

RED (new expectations against round 3's tool: b48bcaf1's dress*.ts checked out, then restored):
chemx test tests/unit/ClassicDress.spec.ts           5 failed out of 38 (TOWER Boss Key chest, both CRACK shops, DROP landing circle, DROP strict flight)
chemx test tests/unit/ClassicDungeonGuards.spec.ts   18 failed out of 73 (written first: seals lost on Earth and Moon, Moon's flight,
                                                     Water's key chest, Earth's landing, the shop, the dropped warp's moves, beyond/reach guards)
GREEN (committed state, one at a time):
chemx test tests/unit/ClassicDress.spec.ts           All tests passed (38 passed)
chemx test tests/unit/ClassicTemples.spec.ts         All tests passed (28 passed)   # 14 in-memory tests moved out, 1 Storm pin added
chemx test tests/unit/ClassicDungeonGuards.spec.ts   All tests passed (82 passed)
chemx test tests/unit/ClassicTempleRefresh.spec.ts   All tests passed (5 passed)
chemx test tests/unit/ShippedTemplesAudit.spec.ts    All tests passed (210 passed)
chemx test tests/unit/TempleSolver.spec.ts           All tests passed (53 passed, 1 skipped)

Storm regenerated with the esbuild CLI ("storm-temple: 6 floors, 44 rooms, 2 must-clear, 21 moved, 3 dropped, 0 Bomb chests"). Audit (npx --yes [email protected] audit --no-index --unroll > /tmp/audit-t7f4.txt, twice; git checkout -- chemx-ratchet.json after): none of my files under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN, TYPE_COLOCATION or HOOK_RETURN_OVERLOAD (two inline ifs of mine in dressRooms were named after the first run); counts as the ratchet (INLINE_BOOLEAN 5/4, LINE_BUDGET 36/4, DISPATCH_SWITCH 36, CASCADE_GUARDS 14, HOOK_RETURN_OVERLOAD 19); only the CLI console.log lines of dress.ts remain (UNGUARDED_LOGGING, as before).

Concerns

  • Moon B1 Bob's Torch (JSON (2,2)) is boxed in on every side; its flight's twin is GT's lobby, so the flight stands in front of the fight (on its south, Big Chest side), not beside it: a hero coming down from the lobby reaches the fight's entry side without fighting it (nothing beyond it opens; the guards check). LTTP landed the hero inside the fight. A flight beside it would need a column inserted inside the grid (corridor rooms across it), not built.
  • A boxed-in sealed fight hands out its cell's chests and keys as drops (Ice B2 Ice Right H, Moon Bob's Torch): the hero takes them from the fight's victory rather than from a chest room. My call; the ruling did not foresee it.
  • Growth before the first row or column gives shifted negative values (Moon B1, Fire 1F, Shadow B1/1F). The specs' nowAt handles them; any other reader of shifted must not assume it is never negative.
  • Gold ("other") chests follow the chest rule but keep the old nearest-free-cell fallback (not in the ruling's list): Moon's Hope Room gold chest is not reached from its cell on foot with nothing in hand.
  • A landing circle for a landing that is no seal keeps openNear's "nearest added room" as its very last resort; no dungeon reaches it, and the landing guard covers sealed landings only.
  • The e2e walk stays skipped (round 1's STATUS); its 2F step is updated, not run.

Fix round 5 (the coordinator's message: Moon's Bob's Torch flight, the lead's rulings 1-3)

Status: DONE_WITH_CONCERNS (every ruling done; 0 extra cells in all nine; the concerns are below). Commit on feat/classic-temples-2 (only my paths), tree clean:

  • 3f3ac3d8 fix(temples): in front of a seal means in front of every seal, and the dress step opens no way past its seals

Ruling 1: "in front" is in front of every seal

  • dressPlace.frontCells(board, farEnd): the converted cells the dungeon reaches from the entrance with every must-clear fight and the boss shut (dressReach.convertedWalk, the converted walk generalized: a start, a set of barred cells). When the link's far end lies past a seal, the cells reached from that end without crossing one and not from the entrance (so the ride back opens nothing the entrance does not).
  • besideInFront(board, from, farEnd) for a sealed from enters only those cells: first those walked to from the fight (that walk reads locks and cracked walls open, as the dungeon does: walkFrom(..., crossesLocks)), else the front cells of its floor nearest it. Used by openInFront for a flight that faces its fight (far end: the twin, linkEnd(item)) and for a landing circle on a sealed fight (far end: the warp's own cell, circle.item.origin, from dressLinks.placeLandingCircles). A landing that is no seal keeps the round-4 walk (applying it there grew a row on Storm's 1F for a warp that is dropped anyway).
  • Moon: Bob's Torch's flight is back at the base spot, B1 (2,7) of the shifted grid = JSON (2,6), beside Speed Torch Upper behind the Hope Room's small lock; Moon stays solvable (R4 only, as pinned). DROP: the fairies ride in from a cell past the climb's seal, so their circle opens on the climb alone, in a row grown above 2F (the DROP expectations moved down a row; the flight still takes the nook, now 2F (1,3)).

Ruling 2: no way past the seals, all nine

ClassicDungeonGuards.spec "opens no way past its seals": with every sealed fight and the boss blocked, the converted cells the dressed temple reaches (each room mapped back to its JSON cell through shifted/compacted; rooms the dressing added are walked through, not counted) must be a subset of what the converted JSON reaches with every must-clear fight and the boss blocked (holes and warps one way, locks ignored). Result: 0 extra cells for all nine (Moon had 20 lines of diff, the review's 18 cells, before the fix). The in-front guard (standsInFront) now reads the JSON (frontOf), not the dressed temple that already held the new flight: every converted cell the room reaches on foot past no seal lies in front of every seal as read from the entrance or from its link's far end.

Ruling 3: latent fixes

  • dressDungeon deals the chores last: Bomb chests and extras read the plain layout (layoutNow), then planChores runs on the final board, so a line grown before a floor's first cannot shift the roles. Storm and the other eight are unchanged by it.
  • addBombChestNear takes the near end's slot first and writes the move from: nearSlot.at (it follows a grown line).
  • The guard's fightFrom counts a fight as kept only while it still seals (lockOnEnter).
  • dressBoard.ts's header says where a flight that faces its fight goes (beside it, or in front of every seal).

Gate

RED (the round-5 guard spec against round 4's tool, adee573d's dress*.ts checked out, then restored):
chemx test tests/unit/ClassicDungeonGuards.spec.ts   2 failed out of 91 (Moon: "opens no way past its seals"; Moon: the in-front check)
GREEN (one at a time, with the committed tool):
chemx test tests/unit/ClassicDress.spec.ts           All tests passed (38 passed)
chemx test tests/unit/ClassicTemples.spec.ts         All tests passed (28 passed)
chemx test tests/unit/ClassicDungeonGuards.spec.ts   All tests passed (91 passed)
chemx test tests/unit/ClassicTempleRefresh.spec.ts   All tests passed (5 passed)
chemx test tests/unit/ShippedTemplesAudit.spec.ts    All tests passed (210 passed)
chemx test tests/unit/TempleSolver.spec.ts           All tests passed (53 passed, 1 skipped)

Storm: storm-temple.ts unchanged (the bundle's CLI rewrote it identically each run; git status clean, and ClassicTemples' "is exactly what dress.ts writes" passes). Audit (npx --yes [email protected] audit --no-index --unroll > /tmp/audit-t7f5.txt, git checkout -- chemx-ratchet.json after): none of my files under LINE_BUDGET_FILE, CONTROL_FLOW_INLINE_BOOLEAN, TYPE_COLOCATION or HOOK_RETURN_OVERLOAD; counts as the ratchet (INLINE_BOOLEAN 5/4, LINE_BUDGET 36/4, DISPATCH_SWITCH 36, CASCADE_GUARDS 14, HOOK_RETURN_OVERLOAD 19); only dress.ts's CLI console.log lines (UNGUARDED_LOGGING, as before). Files: dressPlace.ts 385 lines, dressReach.ts 117, dressBoard.ts 353, ClassicDungeonGuards.spec.ts 437.

Concerns

  • Moon's Bob's Torch flight stands behind the Hope Room's small lock and Speed Torch Upper's (the ruled base spot): a hero comes down from the lobby there; the solver clears the temple (R4 only).
  • Shadow B1's first row holds one room, an empty dead end: a row was grown before the first for a landing circle whose warp was then dropped (its ride back went around a seal), and the room stays. Out of scope, as ruled.
  • Non-boss seals bypassable one at a time, and gold chests, are out of scope (ruled); the round-4 concerns stand.
⚠️ **GitHub.com Fallback** ⚠️