You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm resuming work on FactorioMapWebUI in /Users/ericjohnson/GitHub/FactorioMapWebUI (branch main, clean tree, HEAD e68f617).
Context
Static Vue 3 SPA for authoring Factorio map-generation presets, plus a client-side preview that ports the game's noise/autoplace generation to TypeScript. Current goal: get Vulcanus cliff accuracy to 100%, tracked in issue #84.
Issue #84 stays OPEN until Vulcanus cliffs are 100% or Eric says to close it. Do not close it for having an empty item list - re-scope it instead. (Eric's instruction, 2026-08-01. It was auto-closed once already: a commit message reading Closes #84 item 4 closed the whole issue, because GitHub parses Closes #84 and drops the qualifier. Write "closes item N of #84" instead, and check gh issue view 84 --json state after any merge.)
Current accuracy
recall
precision
wrong orientation
Nauvis
1.0000
1.0000
0 / 334
Vulcanus (shipping)
0.9720
0.9713
37 / 1531 = 2.4%
Per region: [0,0] 283/283, [1500,1500] 885/900, [-1200,800] 401/387. Session start was recall 0.806/0.938/0.853 with 12.5% wrong orientations.
#88 hit 13/13 by shrinking the box past what the engine uses, and the excess shrinkage also absorbed an unrelated defect: 4 of the 6 cliffs the correct box still rejects are cells whose orientation we get wrong, so we load the wrong box entirely. A model that scores perfectly by hiding a second bug is worse than one that leaves it exposed. When a fix lands on a metric perfectly, treat that as a prompt to find independent evidence, not as the evidence. The same failure happened the day before in #86.
Next task
Two leads remain, both well characterised. Take the orientation one first.
1. The orientation residual - 37 cells, ONE edge each, one-directional
Every one runs the same way: the game says -to-none, we say a crossing. So we over-detect a crossing on one edge, 37 times in ~6100 edge evaluations (0.6%), and never under-detect.
Per region 7 / 26 / 4. Which edge: L11 / R6 / T7 / B13 - no directional bias.
Two causes are already ruled out - do not re-derive them.
crossesCliff is exact. I disassembled CliffGenerator::crossesCliff (0x10160c914 under 2.1.12 - the notes' 0x101606d08 has moved) and src/noise/cliffs/cliffPlacement.ts reproduces it line for line: the a < 0 || b < 0 early-out, the band derivation, the boundary < e0 check, the strict cliffAvg > 0.5 gate and the strict crossing comparisons. There is no >= vs > slip to find.
cliffiness_basic is exonerated. Substituting the game's own corner cliffiness (from oracle-vulcanus-cliff-corner-fields-entity-regions.seed123456.json) leaves the count at exactly 37 / 1531 - no change at all. Control: substituting the game's 1-tile elevation from the same fixture gives exactly 175 wrong / 1400 matched, the pre-#83 figures to the digit, which proves the harness is live and re-confirms #83.
So the defect is in the grid-4 cliff-elevation field - the one input in the chain with no direct per-corner oracle. #83 established that channel with a contour-shift experiment, never a per-corner comparison.
The shape is pinned in test/cliffOrientationResidual.spec.ts (e68f617): one edge, always over-detecting, spread across edges and regions. If under-detections ever appear there, the cause has changed.
Concrete next step: capture the game's cliff_elevation at the 4-tile corner lattice around the 37 disputed cells, using sampleCliffEntitiesFull's probeExpression + probeProperty (register an expression at the data stage and route it onto cliff_elevation, so the cliff generator becomes the readout rather than calculate_tile_properties). Compare per corner. A small offset there would explain a one-directional over-detection.
2. Over-placement below elevation 120
test/vulcanusElevationLevels.spec.ts sweeps cliff_elevation_0; the low/high ratio gap is 0.024 (worst-low 1.0415 at level 40, worst-high 1.0177 at level 130). Pure over-placement now, no recall cost. Note this figure has been reported three times today (0.067 -> 0.018 -> 0.024) as upstream fixes landed - treat it as provisional.
pnpm perf is FMW_PERF=1 vp test test/render-cost.perf.spec.ts. It must run on a quiet machine - it is wall-clock render timing, and this repo has already recorded a 3001ms baseline reading 16729ms on identical code under load. Do not run it alongside other work, and do not run it in parallel in a worktree; a worktree isolates files, not CPU.
Read these first (authoritative - don't re-derive)
/Users/ericjohnson/GitHub/FactorioMapWebUI/CLAUDE.md - project conventions. Read fully before touching anything.
/Users/ericjohnson/GitHub/FactorioMapWebUI/docs/noise/vulcanus-cliffs-NOTES.md - the STATUS banner has current numbers. The last three sections each correct the one before it - read all three in order or you will act on a superseded number. The final one, ## The collision box, settled by disassembly, is current.
/Users/ericjohnson/GitHub/FactorioMapWebUI/docs/noise/cliffs-NOTES.md - STATUS banner, then ## ROOT CAUSE, 2026-08-01.
gh issue view 84 - the itemised remainder plus tonight's two analysis comments.
Claude Code memory at /Users/ericjohnson/.claude/projects/-Users-ericjohnson-GitHub-FactorioMapWebUI/memory/ - MEMORY.md is the index. Most relevant: cliff-collision-box-is-raw-rect.md, comparison-must-run-the-same-rules.md, keep-84-open-until-100.md.
Conventions / gotchas
pnpm run verify is the gate (vp check + check:vue + vp test + preview:test), ~65-90s local, ~4 min CI. pnpm vp check --fix for format+lint+types. Never add a tsc typecheck script.
Branch for work; PR and merge with gh pr merge <n> --squash --delete-branch=false. Do not commit to main directly.
pnpm refs:sync --check before trusting any reading of game data. Currently in sync at 2.1.12.
Disassembly is cheap and beats tuning models against a metric - the binary is unstripped. Recipe: nm -n "$BIN" | c++filt for the VA, then lldb -b -o "disassemble --start-address <VA> --end-address <VA>" "$BIN". The --name form silently returns nothing on many symbols. VAs move between builds - always re-derive from nm. Binary at ~/Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/MacOS/factorio.
Also grep factorioLuaAPI/ (API docs) and ~/GitHub/factorio-data (prototype Lua) before WebFetching anything Factorio.
Adding a fixture means adding its test/fixtures/PROVENANCE.json entry - a test enforces it.
git checkout <file> is not an undo for uncommitted edits. I wiped a fix that way tonight while reverting a planted failure. Copy to the scratchpad or commit first.
Assert non-vacuity on every measurement. Two silent-zero results tonight came from a hand-derived helper whose fields were undefined; both looked like clean findings.
Temp/scratch files go in the session scratchpad, never /tmp or the repo.
Put decisions to Eric via an interactive prompt with grounded options, not prose questions.
I'm resuming work on FactorioMapWebUI in
/Users/ericjohnson/GitHub/FactorioMapWebUI(branchmain, clean tree, HEADe68f617).Context
Static Vue 3 SPA for authoring Factorio map-generation presets, plus a client-side preview that ports the game's noise/autoplace generation to TypeScript. Current goal: get Vulcanus cliff accuracy to 100%, tracked in issue #84.
Issue #84 stays OPEN until Vulcanus cliffs are 100% or Eric says to close it. Do not close it for having an empty item list - re-scope it instead. (Eric's instruction, 2026-08-01. It was auto-closed once already: a commit message reading
Closes #84 item 4closed the whole issue, because GitHub parsesCloses #84and drops the qualifier. Write "closes item N of #84" instead, and checkgh issue view 84 --json stateafter any merge.)Current accuracy
Per region:
[0,0]283/283,[1500,1500]885/900,[-1200,800]401/387. Session start was recall 0.806/0.938/0.853 with 12.5% wrong orientations.What's done this session (all merged to main)
e027566(test(cliffs): the "excess" was the lava rejection, not a second defect (#84) #86) - Vulcanus cliffs: the 2.4% remaining after the multisample grid fix #84 items 1 and 2 were one measurement error: our side never rantryToAddCliff's lava-collision rejection while the game always does. 185 of the "187-cell excess" was that unapplied rule. Precision 0.8719 -> 0.9743.b843eaf(test(noise): channel audit - cliffs ARE the only coarse-grid consumer (#84) #87) - channel audit (Vulcanus cliffs: the 2.4% remaining after the multisample grid fix #84 item 4) closed.multisampleappears in exactly one place in all of factorio-data, so the audit reduced to "who consumes Vulcanus elevation": cliffs (grid 4), tiles+temperature (grid 1, measured), and nothing else - resources and rocks contain 0 references to elevation.e68f617(test(cliffs): pin the SHAPE of the orientation residual (#84) #92) - pins the SHAPE of the orientation residual as a permanent test.8243b65(fix(cliffs): rotbb collision boxes are ROTATED, not their bounding box (#84) #88) - WRONG, superseded by fix(cliffs): the collision box is the RAW rectangle - #88 was wrong (#84) #90. Claimed the collision box isrotbb's rectangle rotated 45 degrees.64b5b79(fix(cliffs): the collision box is the RAW rectangle - #88 was wrong (#84) #90) - the correction. Disassembly shows the engine uses the RAW stored rectangle and discards the box's1/8orientation tag.The most important lesson of the session
#88's model scored best on every metric and was wrong.
#88 hit 13/13 by shrinking the box past what the engine uses, and the excess shrinkage also absorbed an unrelated defect: 4 of the 6 cliffs the correct box still rejects are cells whose orientation we get wrong, so we load the wrong box entirely. A model that scores perfectly by hiding a second bug is worse than one that leaves it exposed. When a fix lands on a metric perfectly, treat that as a prompt to find independent evidence, not as the evidence. The same failure happened the day before in #86.
Next task
Two leads remain, both well characterised. Take the orientation one first.
1. The orientation residual - 37 cells, ONE edge each, one-directional
Measured tonight:
-to-none, we say a crossing. So we over-detect a crossing on one edge, 37 times in ~6100 edge evaluations (0.6%), and never under-detect.Two causes are already ruled out - do not re-derive them.
crossesCliffis exact. I disassembledCliffGenerator::crossesCliff(0x10160c914under 2.1.12 - the notes'0x101606d08has moved) andsrc/noise/cliffs/cliffPlacement.tsreproduces it line for line: thea < 0 || b < 0early-out, the band derivation, theboundary < e0check, the strictcliffAvg > 0.5gate and the strict crossing comparisons. There is no>=vs>slip to find.cliffiness_basicis exonerated. Substituting the game's own corner cliffiness (fromoracle-vulcanus-cliff-corner-fields-entity-regions.seed123456.json) leaves the count at exactly 37 / 1531 - no change at all. Control: substituting the game's 1-tile elevation from the same fixture gives exactly 175 wrong / 1400 matched, the pre-#83 figures to the digit, which proves the harness is live and re-confirms #83.So the defect is in the grid-4 cliff-elevation field - the one input in the chain with no direct per-corner oracle. #83 established that channel with a contour-shift experiment, never a per-corner comparison.
The shape is pinned in
test/cliffOrientationResidual.spec.ts(e68f617): one edge, always over-detecting, spread across edges and regions. If under-detections ever appear there, the cause has changed.Concrete next step: capture the game's
cliff_elevationat the 4-tile corner lattice around the 37 disputed cells, usingsampleCliffEntitiesFull'sprobeExpression+probeProperty(register an expression at the data stage and route it ontocliff_elevation, so the cliff generator becomes the readout rather thancalculate_tile_properties). Compare per corner. A small offset there would explain a one-directional over-detection.2. Over-placement below elevation 120
test/vulcanusElevationLevels.spec.tssweepscliff_elevation_0; the low/high ratio gap is 0.024 (worst-low 1.0415 at level 40, worst-high 1.0177 at level 130). Pure over-placement now, no recall cost. Note this figure has been reported three times today (0.067 -> 0.018 -> 0.024) as upstream fixes landed - treat it as provisional.3. Perf (#84 item 3) - untouched
pnpm perfisFMW_PERF=1 vp test test/render-cost.perf.spec.ts. It must run on a quiet machine - it is wall-clock render timing, and this repo has already recorded a 3001ms baseline reading 16729ms on identical code under load. Do not run it alongside other work, and do not run it in parallel in a worktree; a worktree isolates files, not CPU.Read these first (authoritative - don't re-derive)
/Users/ericjohnson/GitHub/FactorioMapWebUI/CLAUDE.md- project conventions. Read fully before touching anything./Users/ericjohnson/GitHub/FactorioMapWebUI/docs/noise/vulcanus-cliffs-NOTES.md- the STATUS banner has current numbers. The last three sections each correct the one before it - read all three in order or you will act on a superseded number. The final one,## The collision box, settled by disassembly, is current./Users/ericjohnson/GitHub/FactorioMapWebUI/docs/noise/cliffs-NOTES.md- STATUS banner, then## ROOT CAUSE, 2026-08-01.gh issue view 84- the itemised remainder plus tonight's two analysis comments./Users/ericjohnson/.claude/projects/-Users-ericjohnson-GitHub-FactorioMapWebUI/memory/-MEMORY.mdis the index. Most relevant:cliff-collision-box-is-raw-rect.md,comparison-must-run-the-same-rules.md,keep-84-open-until-100.md.Conventions / gotchas
pnpm run verifyis the gate (vp check+check:vue+vp test+preview:test), ~65-90s local, ~4 min CI.pnpm vp check --fixfor format+lint+types. Never add atsctypecheck script.gh pr merge <n> --squash --delete-branch=false. Do not commit to main directly.pnpm refs:sync --checkbefore trusting any reading of game data. Currently in sync at 2.1.12.nm -n "$BIN" | c++filtfor the VA, thenlldb -b -o "disassemble --start-address <VA> --end-address <VA>" "$BIN". The--nameform silently returns nothing on many symbols. VAs move between builds - always re-derive fromnm. Binary at~/Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/MacOS/factorio.factorioLuaAPI/(API docs) and~/GitHub/factorio-data(prototype Lua) before WebFetching anything Factorio.test/fixtures/PROVENANCE.jsonentry - a test enforces it.git checkout <file>is not an undo for uncommitted edits. I wiped a fix that way tonight while reverting a planted failure. Copy to the scratchpad or commit first.undefined; both looked like clean findings./tmpor the repo.Also open (not this thread)
#24 cliff/ore separation, #22 approximations register, #16 cross-overlay occupancy, #27 Fulgora island finder, #58 Renovate dashboard.