Files
VoxelForge/OPSTACK-HANDOFF.md
T
Fr0zka f7cccb044b docs: add OPSTACK-HANDOFF.md; install the 39 Unreal skills
Handoff: a pasteable resume prompt at the plugin root -- read order, exact state
(Phase 0.5 green, Phase 1 done and measured, step 3 written but not compiled),
the immediate next action, hard rules, open items, and the method lesson from
this session.

Skills: Jahni's UE library was at .claude/skills/core/<name>/SKILL.md, two levels
deep, where Claude Code discovers skills one level deep -- so none of the 39 were
loading. Flattened; 124 reference files intact, all frontmatter valid, folder
names already matched their name: field. core/category.md left as documentation.
Confirmed loading.

They are untracked and cannot be tracked without un-ignoring .claude/ itself
(git cannot re-include a file whose parent directory is excluded). Same shape as
AUDIT P1; flagged, not actioned.

Note for the next session: module-and-build-system documents PCHUsage, shared
PCHs and IWYU -- the exact mechanism that blocked C10's settling experiment. That
skill was in the repo, undiscovered, while it was worked out the slow way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 15:11:26 +02:00

6.4 KiB

Handoff — VoxelForge operator stack, 2026-07-27

Paste the block below into a fresh session. Everything it refers to is on disk and in git.


You're picking up an agreed refactor of the VoxelForge UE5 voxel plugin, on branch experimental (already checked out — do not create another). I'm Jahni. Design and progress are written down so you don't re-derive them.

Read first, in this order

  1. CLAUDE.md — project rules. Rule #1 is absolute: never build, compile, or run the editor. I build everything myself. When code is done, stop, say "ready to build", list the likely compile-error spots, and wait.
  2. OPSTACK-PROGRESS.md — THE LAST ENTRY FIRST. Append-only log of what is built vs merely written. This is the resume point.
  3. OPSTACK-PLAN.md — the plan. §2.5 op taxonomy, §2.6 acceptance bar, §4 phases, §5 invariants.
  4. OPSTACK-DECOMPOSITION.md — all 8 archetypes broken into ops, every param traced. §10.4 is the port order, §11 the open questions.
  5. AUDIT-2026-07.md §C10 — read before touching the Maze equivalence test. Six builds were spent there; it is parked deliberately.
  6. CODEMAP.md — navigation. Trust symbol names over line numbers.

Where things stand

Phase 0.5 — DONE, green. Five automation tests, all passing (Session Frontend → Automation, filter VoxelForge): DensityPurity, ClassifyTileSoundness, DiffLayerContention, LiveEditInvalidation, OpStack.BoxVerdictFold, OpStack.MazeEquivalence. They found and fixed a real bug (AUDIT C2 — five caches missing LayoutVersion).

Phase 1 — DONE and verified. Maze decomposes into seven ops with no contortion: ConstantRock → LatticeCorridor → SdfRoughness → SdfCarve → Spine → Seal → Passage. Measured: SDF reproduced bit for bit, zero isosurface crossings out of 20 000, window-invariant across worker threads, every box verdict survives brute force, and 23 of 60 tiles proved uniform where ClassifyTile proves zero for any cave archetype. That last number is the perf case for the whole refactor, measured rather than argued.

Phase 1 step 3 — WRITTEN, NOT COMPILED. The stack is wired into GetDensityAt behind UVoxelStrateDefinition::bUseOperatorStack, with the ported-archetype list living only in UVoxelStrateManager::UsesOperatorStackForChunk(). This is the immediate next thing to build.

The immediate next action

  1. I build. Likely error spots: the else switch form in GetDensityAt, FVoxelOpStack as a thread_local (it is move-only, reset by move-assigning a temporary), the new include.
  2. Then the visual A/B, which is the last thing Phase 1 needs: tick bUseOperatorStack on a Maze strate, regenerate, compare against the same seed with it off. Pass = recognisably the same maze (same corridor scale, connectivity, feel). That is OPSTACK-PLAN §2.6's bar — judged on a screenshot, not a diff.
  3. Then Phase 2, starting with the cheapest win: FlatPlain + CrystalChamber collapse into one op (OPSTACK-DECOMPOSITION §3). Ask me §3.1 first — whether the slab floor noise's Z term is intentional; if it can go, both archetypes become XY-pure and start skipping tiles.

Hard rules that prevent real bugs

  • Density sign: negative = solid, positive = air at the mesher. Inside the op stack the convention is INTERNAL (positive = solid), negated once by the caller. The SDF channel uses standard SDF convention, so min() means opposite things on the two channels. This is the #1 source of confusion in the plugin — VoxelDensityOp.h explains it.
  • Never run both density paths in one world, and never compare them for equality (§C10).
  • Every cache key includes LayoutVersion; ProcessQueue stays EQueueMode::Mpsc; Epoch carries through every async path; don't "optimize" the ARCHITECTURE §8.10 perf invariants.
  • Never edit Binaries/, Intermediate/, *.generated.h. Comments are French + English — match the surrounding file.
  • Commit per coherent unit with a real message. Never push. main is the known-good fallback.
  • Update CODEMAP §3 for new/renamed symbols, ARCHITECTURE §8 for design changes, tick phases in OPSTACK-PLAN.md, and append to OPSTACK-PROGRESS.md.

⚠️ The lesson from this session — it cost six builds

Chasing a 1-ULP difference in the Maze port, I proposed five hypotheses and every one was refuted by measurement: the FVector round-trip, a transcription slip, /fp:fast across translation units, inlining context, and compile-time-constant vs runtime parameters. Each was plausible. Each cost a build. What actually produced information was instrumenting — a bisect, a three-way comparison, a variable-isolation harness.

Worse, twice I reasoned confidently from a premise I hadn't checked (a build flag that had landed in the wrong module; a comparison that was circular by construction and could not fail).

So: on any numeric discrepancy, instrument before hypothesising, and verify the premise before reasoning from it. And know when to stop — Jahni called it, correctly, once the port was proven correct on every axis that affects the game and only the last rounding was unexplained.

New this session: 39 Unreal skills are installed

.claude/skills/<name>/SKILL.md — they were nested one level too deep and invisible; now flattened and loading. Directly relevant: automation-and-testing, coding-standards, module-and-build-system (covers PCHUsage / shared PCH / IWYU — the exact thing that blocked §C10's settling experiment), game-thread-performance, memory-and-gc, navigating-engine-source. Use them — the one that would have explained the shared-PCH failure was sitting in the repo, undiscovered, while I worked it out the slow way.

Known open items, deliberately not done

  • AUDIT C1 — unbounded SeedF, one line at six sites. Dormant at small seeds; applying it re-rolls the world's noise and forces a re-tune, so it wants its own build. Still a real bug.
  • IWYU debt — seven headers use engine types they never include; the plugin only compiles because of the shared PCH. Blocks FPSemantics experiments. Worth clearing on its own terms.
  • AUDIT C10 — the parked ULP residue. Read it before reopening; five hypotheses are already refuted in a table there.
  • OPSTACK-DECOMPOSITION §11 — five design questions for me, most importantly whether the two-channel Eval (already implemented) stays.