Skip to content

Vertical slice completion (PROCEED), Art Bible, and Pre-Production propagation - #111

Open
CryNoxTheKid wants to merge 312 commits into
Donchitos:mainfrom
CryNoxTheKid:session/slice-to-preproduction
Open

Vertical slice completion (PROCEED), Art Bible, and Pre-Production propagation#111
CryNoxTheKid wants to merge 312 commits into
Donchitos:mainfrom
CryNoxTheKid:session/slice-to-preproduction

Conversation

@CryNoxTheKid

Copy link
Copy Markdown

Summary

This session takes The Last Seal from mid-vertical-slice to the end of Pre-Production propagation — 81 commits covering the slice's final feature packages, its formal conclusion (verdict: PROCEED), the completed Art Bible, and the propagation of all playtest findings into GDDs and ADRs.

Vertical slice — final packages (prototypes/last-seal-vertical-slice/, throwaway code)

  • Stonehearth-style build workflow: build/editor mode, draft-first blueprints, flush floor excavation
  • Persistent build projects: grouping, per-project release/pause/cancel, click-selection with outline, change orders, worker-executed demolition, plan-only undo
  • Dig orders, draft eraser, ghost-anchored picking; hover highlight + build grid; Timberborn-style slice view; room/roof/house tools
  • Anti-stuck package: unstuck watchdog + seal prevention with F3 telemetry (a real rescue fired during the automated E2E run)
  • Two typed-array crash fixes with a regression call in the headless gate (LOOP_TEST PASS on every commit)

Slice conclusion

  • REPORT.md — verdict PROCEED; velocity log (5 active build days, ~1 feature package/day); experience gaps recorded (loop payoff under-communicated, mood the weakest axis)

Art Bible completed (design/art/art-bible.md, all 9 sections approved)

  • S5–S9 authored with ux-designer + technical-artist passes; 7 user taste decisions recorded inline; 16 style prohibitions each traced to a lived incident; measured technical constraints (draw-call ledger, 16k = storage problem)

Propagation into design & architecture

  • 6 GDDs updated (~61 new TRs, registry at 528 entries), change-impact report, 5 ADRs amended in place
  • NEW ADR-0015 Large-World Storage & Residency — Proposed (paged region-file residency; 5 measurable spike pass criteria gate acceptance)
  • NEW ADR-0016 Build Project Entity Lifecycle — Accepted (prototype-validated)
  • ticks_per_second 2.0 → 4.0 authoritative (re-tuning pass tracked); furniture removal job-gated
  • design/ux/projects-panel.md authored and APPROVED via /ux-review

Reviewer notes

  • Prototype code is deliberately relaxed (see .claude/rules/prototype-code.md) and is reference-only — production is written from scratch.
  • The ADR-0015 storage spike (prototypes/storage-residency-spike/) is still in progress in a live session and is intentionally NOT included in this PR; its results gate ADR-0015's Accepted status and will follow separately.
  • Open before the Production gate: AD-ART-BIBLE sign-off (skipped in lean mode), storage spike results, re-tuning pass, single documented playtester.

🤖 Generated with Claude Code

crynox98-hash and others added 19 commits July 20, 2026 18:52
… 'missing faces' cause

User: 'nothing changed at all' after every mesh/look fix — because it was
never the mesh. The orbit camera's target sat at a FIXED height (y=10) and
nothing kept the EYE out of the terrain: panning/zooming toward any raised
terrace put the camera INSIDE the hill, and a backface-culled voxel world
viewed from inside reads exactly as reported — 'only outer faces render,
see through to bedrock', while top-down views look perfect. None of the
mesh audits could catch it (mesh and data were always correct).
Fix: camera target now rides the terrain surface (height provider wired
from GameWorld) and the eye is clamped >= 2 cells above the terrain at its
own footprint. LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (user-found defects)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onts are CLOCKWISE

User diagnosis was correct all along ('you only render the inner faces'):
the mesher wound every face counter-clockwise-outward (OpenGL front-face
convention), but Godot treats CLOCKWISE as front — cull_back therefore hid
the OUTSIDE of everything. All prior symptoms were this one bug seen from
different angles: see-through cliffs, hollow trunks, inside-out canopies,
'floating' lip faces (inner surfaces of far walls), 'nothing ever changes'
(every fix polished the visible subset). All audits passed because they
validated winding against the mesh's own stored normals — self-consistently
wrong, never against the engine convention.
Fix: cull_mode = CULL_DISABLED on the world material (immediate, correct
from every angle; ~2x face cost absorbed by the 10x frame headroom).
Follow-up noted for REPORT/production: rewind faces to Godot's clockwise
convention and re-enable culling; audit rule = validate winding against
ENGINE convention, not self-stored normals. Also: 8-cell terrace steps
(4-cell steps were perceptually invisible from the colony camera).
LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (user-found root cause)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Characters are now 2 blocks tall (capsule 1.4+head; walk clearance stays 3,
logic untouched) — blocks read smaller, world reads bigger. Landforms
widened (continent freq 0.0022, core plateau r=60 blend to 150, tree
clearing r=70), trees taller (trunks 4-6). Three villagers spawn (Hilda,
Bruno, Mira) and breathers are rarer/shorter (8 jobs before break, 40
ticks) — 'they should work more': hut build time in the loop test dropped
from ~3400 to 650 ticks. Camera default distance 24. Loop test expects 3
villagers. GDD propagation notes (production): character scale 2 blocks,
bed footprint 2 cells (multi-cell furniture), 16k world.
LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (user direction: scale + industry)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User request for the testing phase: toggleable overlay (F3) showing
performance (FPS, frame/physics ms, draw calls, triangles, RAM/VRAM, node
count), simulation state (pause/warp, chunk count, camera position),
building state (blueprints/claimed/furniture/tool), per-villager rows
(state, cell, sleep, mood band, distress) and a timestamped event log
(construction, furniture, invalid commits, rooms, warnings, state and
distress changes). Wall-clock refresh, read-only taps on public APIs.
First live numbers: 240 fps, 6.4 ms frame, 265 draws, 84k tris — well
inside every budget. LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (test tooling)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ats line

User request for better simulation testing: TimeTickSystem gains warps
10 and 20 (HUD speed radio picks them up automatically; +/- keys step
through the warp list), a lifetime tick counter drives a sim clock, and
the time-controls panel shows a live stats line (sim time, villager count,
beds, open build jobs). Note: at 20x (80 ticks/s) the 10-ticks-per-frame
cap engages below ~120 fps — excess sim time is discarded by design (GDD
tick-cap rule), fine for testing. LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (test tooling)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed inline literals

User crash on Block tool after building: _render_tool_preview demanded
Array[Vector3i] but call sites pass '[cell] if valid else []' inline
literals (plain Array). Param untyped with rationale comment. LOOP_TEST PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t, flush floors

User direction: (1) BUILD/EDITOR MODE — 'Bauen' master toggle in the HUD;
tools arm only inside build mode (arming auto-enters), Esc chain exits the
mode last; outside it clicks stay with villager selection. (2) DRAFT-FIRST
— blueprints are drafts (alpha 0.30) villagers ignore; 'Bau starten (N)'
releases them (alpha 0.45) to the job queue. (3) FLUSH FLOORS — floor
drags starting on terrain replace the picked surface plane (dips fill),
undo restores captured pre-values (restore_value) instead of assuming AIR.
Loop test releases drafts explicitly + asserts the release count.
CONTRACTS.md updated with the new APIs. LOOP_TEST PASS.

GDD propagation notes: building-ui Rule 1 (mode), building-system Rules
4/5 (excavation, draft phase), voxel-world terrain-removal exception.

Task: Roadmap Step 2 — Vertical Slice (user direction: build workflow)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ndow

Stonehearth-style building entities (user decisions 2026-07-22):
contiguous draft cells (26-neighborhood, batch-merge) group into
projects with states DRAFT/BUILDING/PAUSED/DONE. Per-project release
('Bau starten'), pause/resume, cancel/demolish (un-builds via
restore_value). claim_job gated on BUILDING + tracks claiming
villager; projects expose worker_ids. HUD: projects panel
(bottom-left cards: status, progress bar, workers, state buttons)
replaces the global release button. release_drafts() kept as compat
wrapper. Loop test asserts 1 merged DRAFT project for the hut,
BUILDING on release, DONE with full built count; bed is its own
project. Resource costs deferred to Production (user decision).
LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (user direction: build projects)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ixes

User direction (2026-07-22): (1) DRAFT ERASER — removal tool in build
mode erases DRAFT-project blueprint cells instantly (carve a door gap
out of a drawn wall); (2) TERRAIN DIG ORDERS — removal tool over
terrain (1..5) creates dig projects ('Abbau N', red ghosts); villagers
excavate after release through the same claim/write pipeline,
restore_value undo/cancel; (3) GHOST SNAPPING — raycast_cells gained
an extra_solid predicate so picking anchors on blueprint ghosts (roof
on drafted walls). Fixes: water no longer solid for picking; removing
a floor-replace block restores terrain (restore_value) instead of
carving a hole — the reported 'invisible floor' cause (chunk-boundary
remesh was already correct; chunks are full-height XZ columns);
dig jobs exclude the stand-on-target onsite offset (villager dug the
block under its own feet and got trapped). LOOP_TEST PASS with new
eraser/dig/regression assertions.

Task: Roadmap Step 2 — Vertical Slice (user direction: editor feedback)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…house tools

User-prioritized UX package (brainstorm 2026-07-22):
(1) HOVER HIGHLIGHT + BUILD GRID — wireframe box + face quad on the
cell a click would affect (reuses the pick raycast), 9x9 terrain grid
overlay around the cursor in build mode.
(2) SLICE VIEW (Timberborn-style) — chunk material converted to
ShaderMaterial (chunk_terrain.gdshader: atlas + vertex-AO + y_cut
fragment discard, no remesh needed); PageUp/PageDown/Home + HUD
'Ebene' controls; ghosts/highlight filtered, villagers hidden above
the cut.
(3) ROOM TOOL (Raum, key 6) — ground rect drag -> perimeter walls at
wall height with auto door gap on the camera-facing edge.
(4) AUTO-ROOF (Dach, key 7) — one click roofs the clicked project's
bbox at top height (thatch), joins the same project.
(5) HOUSE TEMPLATE (Haus, key 8) — 7x7 starter house stamp (flush
floor, walls + door gap, roof) as one draft project; known limitation:
redo after undo rebuilds only single-material batches.
hud.setup gained voxel_world param; CONTRACTS.md updated.
LOOP_TEST PASS incl. new room/roof/house/slice assertions.

Task: Roadmap Step 2 — Vertical Slice (user direction: build UX)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n check

_uniform_cell_values (new in the UX package) required Array[Vector3i],
but _render_tool_preview deliberately passes plain Arrays (same class
as the c05e4a1 crash). Untyped the param and added a loop-test
regression call that drives the preview path with plain Arrays so the
headless gate catches any retyping (runtime type errors surface as
SCRIPT ERROR in the gate grep).

Task: Roadmap Step 2 — Vertical Slice (user crash report)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n + ghost visuals

User decisions 2026-07-23: (1) grid lines ~30% alpha; ghosts full
texture at 0.50 draft / 0.70 released (ghost-face audit: no culling
bug — low alpha was the culprit; DEPTH_DRAW_ALWAYS added for sort
stability); inflated overlay markers on affected terrain (orange =
floor-replace, red = dig/demolition). (2) Projects persist after DONE;
clicking any project cell (any mode, no tool armed) selects it —
panel card gold-highlighted + scroll-into-view, cyan wireframe outline
in world, Esc deselects before exiting build mode. (3) Change orders:
new drafts adjacent to BUILDING/PAUSED/DONE projects ATTACH to them
(reverses the old no-absorb rule); 'Änderungen geplant' status +
per-project release. (4) Rückbau nur über Aufträge: removal tool on
built project cells creates demolition orders (released -> villagers
tear down block by block, restore_value honored); cancel/Abriss
converts built cells to released demolition orders, project vanishes
only when empty; undo/redo touch PLAN entries only — never built
cells. Loop test: persistence, change-order, demolition, undo-safety,
selection assertions. LOOP_TEST PASS.

Task: Roadmap Step 2 — Vertical Slice (user direction: persistent projects)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User scope decision 2026-07-23 (safety net, not build-order planning):
(1) WATCHDOG: villagers with no legal step (or non-standable cell)
while TRAVELING/WORKING for 12 ticks (~3s) are teleported to the
nearest standable unoccupied cell; claimed job released back to the
queue; villager_unstuck signal + total/per-villager telemetry in the
F3 console event log (data collection for the Production fix).
(2) SEAL PREVENTION: report_on_site refuses a build write that would
leave the builder with zero legal steps — claim released for an
outside villager; livelock guard allows the write after 3 abandons by
the same villager (watchdog rescues). Digs/demolitions exempt.
Loop test: burial-rescue + mock-corridor seal-refusal assertions; a
real watchdog rescue fired during the E2E build itself. LOOP_TEST PASS.
Production follow-ups (GDD list): build-order planning, scaffolding,
stuck telemetry requirement.

Task: Roadmap Step 2 — Vertical Slice (user direction: anti-stuck)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slice concluded 2026-07-23 after user debrief: full build->furnish->
live loop validated (headless gate green every commit), feasibility
~1 feature package/day over 5 active build days. Experience gaps for
Production: loop payoff under-communicated, atmosphere/mood weakest
axis (primary Art Bible input). Tester-prioritized frictions: worker
intelligence, build comfort, world content, performance/16k world.
README status Concluded + findings; prototypes/index.md gains the
Vertical Slices table. CD-PLAYTEST skipped (lean review mode).

Task: Roadmap Step 2 — Vertical Slice (Phase 5-6 wrap-up)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
S5 Characters (2-block chibi, personal accent colors, Corruption enemy
hue, personality via motion), S6 Environment (northern homestead
vernacular, no-PBR rule, Ambient Life & Motion table with confirmed
first wave: smoke/foliage-sway/idle-behaviors/clutter/torch-flicker —
direct answer to the slice debrief's 'mood lacking'), S7 UI/HUD
(ux-designer conflict pass resolved: Hearth Gold dual-channel,
red->State Orange with mandatory shapes, Dusk Test QA gate, typography
+ panel anatomy, open handoffs incl. projects-panel UX spec), S8 Asset
Standards (16px texel tier + 16 voxels/cell user decisions, naming,
palette bands, review checklist + technical constraints: measured
draw-call ledger ~1293/2000, 16k world flagged as a STORAGE problem
for the ADR-0014-successor spike, reject-if gate), S9 Style
Prohibitions (16 rules, each traced to a locked rule or a lived
incident). Seven user taste decisions recorded inline. AD-ART-BIBLE
gate skipped (lean mode) — required before the Production gate.

Task: Pre-Production — Art Bible completion (post-slice, REPORT.md input)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 of the slice propagation batch (source: REPORT.md + user
playtest decisions 2026-07-12…23). All changes tagged '(Slice
revision 2026-07-23)', existing requirement IDs untouched.

- game-concept: 16,000x16,000x32 production target (gated on the
  storage/streaming spike — ~11 GB naive, 3x over the 4 GB ceiling;
  a storage problem, not rendering), 2-block character scale.
- voxel-world (TR-050..053): floor terrain-replace with restore_value,
  villager-executed terrain dig orders, CW-winding+culling hard
  requirement (CULL_DISABLED documented as temporary), 16k scope note.
- building-system (TR-102..126, AC52..77): build/editor mode,
  draft-first blueprints grouped into persistent BUILD PROJECTS
  (26-neighborhood merge, per-project release/pause/cancel, worker
  attribution, change orders, click-selection), job-gated demolition +
  Abriss, plan-only undo/redo, floor excavation, dig/mining projects,
  draft eraser, multi-cell furniture, higher-level tools contract,
  door/window items + worker-safety/telemetry forward requirements.
  Flagged judgment call: furniture removal stays instant (carve-out).
- building-ui (TR-075..088): build-mode toggle + Esc chain, click
  routing, ghost-anchored picking, hover highlight + grid, ghost
  alpha-as-commitment, replace/dig markers, room/roof/house tools,
  slice view, projects panel (layout provisional pending
  design/ux/projects-panel.md), door-gap affordance, undo scope.
- villager-ai-behavior (TR-097..109): claim attribution, 2-cell body
  column naming, unstuck watchdog + rescue BFS, seal prevention +
  livelock escape, dig on-site exclusion, telemetry as sizing input
  for production build-order planning/scaffolding. Flagged: GDD
  tick-rate (2.0) vs slice tick-rate (4.0) discrepancy.
- resource-item-database (TR-052..054): furniture footprint field
  (bed 1x2) + boot validation; cost field anticipated, not designed.
- design/registry/entities.yaml: bed footprint, watchdog/seal constants.

Task: Pre-Production — slice propagation Phase 1 (GDDs)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dates, ADR-0015/0016

Change-impact report (change-impact-2026-07-23-slice-batch.md) with
per-ADR assessment: 9 ADRs unaffected, 5 updated in place — ADR-0009
(body-column occupancy, watchdog teleport as second sanctioned
discrete mutation, seal-prevention consumer), ADR-0010 (Build-Mode-
gated click routing + villager/project selection arbitration),
ADR-0014 (CW winding + culling-enabled hard requirement, extra_solid
picking predicate ownership, boot-allocation clause under ADR-0015
review), ADR-0012/0013 (impact notes gated on ADR-0015).

NEW ADR-0015 Large-World Storage & Residency — PROPOSED pending the
storage spike (user decision: paged region-file residency as the
load-bearing mechanism, sparse far-terrain regen as fast-follow
within it, compression-only rejected). Five measurable spike pass
criteria encode the Accept gate (page-in latency, flat memory over a
16k traverse, write-to-unloaded correctness, eviction under budget,
region-file save round-trip).

NEW ADR-0016 Build Project Entity Lifecycle — ACCEPTED (prototype-
validated; cites slice commits + REPORT.md): draft/release/pause/
done + job-based demolition incl. furniture, 26-neighborhood merge,
change orders, worker attribution, plan-only undo, reverse-index
selection, persistence-until-empty.

User resolutions applied: ticks_per_second 2.0 -> 4.0 authoritative
(time-tick-system.md + registry; re-tuning pass tracked as Open
Question — per-tick rates now ~2x real-time), furniture removal
job-gated (building-system.md TR-127, atomic multi-cell demolition).
requirements-traceability.md + tr-registry.yaml (528 entries) updated;
technical-preferences ADR log extended.

Task: Pre-Production — slice propagation Phase 2 (architecture)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New design/ux/projects-panel.md (Draft, pending /ux-review): claims
HUD zone Z7 (bottom-left, villager-panel baseline lifted above it),
tier-first sort (BUILDING > PAUSED > DRAFT > DONE+changes > settled),
bounded visible cards (6) with issues-anchor-style overflow row,
selected card always pinned, no-modal Abriss policy (job-gated delay
substitutes for friction, revisit with economy), state shape
proposals for the Art Bible §7.3 shape-budget table, 9 tracked open
questions (incl. required hud.md Z7/visual-budget follow-up edit).

Task: Pre-Production — projects-panel UX spec (Art Bible §7.6 / TR-building-ui-086)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/ux-review first pass: NEEDS REVISION (0 blocking, 4 advisory).
Applied: States & Variants section with explicit no-loading-state
rationale; Abriss/Abbrechen irreversibility note (no undo entry for
Built cells, TR-building-ui-088/TR-building-system-119) in
Confirmation Policy, Interaction Map and Events Fired; priority-
tiered bounded list + Selection Pin flagged as candidate
interaction-patterns.md entry; Z4/Z7 baseline claims separated.
Re-review verdict: APPROVED (0/0). Ready for /team-ui Phase 2.

Task: Pre-Production — projects-panel UX spec (review cycle)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@CryNoxTheKid
CryNoxTheKid requested a review from Donchitos as a code owner July 23, 2026 12:59
crynox98-hash and others added 10 commits July 23, 2026 15:33
Paged region-file residency prototype for the 16k world (synthetic
16,000x16,000x32, real slice terrain-gen cost). Round 1 (fixed
2-items/frame budget, 120 c/s): C3/C5 PASS (byte-identical write and
save round-trips; save 4.76MB vs 7.63GB naive — ~1600x; 99.5% of
page-ins regen from seed), C1/C2/C4 FAIL (backlog under 12x demand).
Round 2 (time-based 4ms budget + capped async prefetch, measured at
the REAL max camera speed 144 c/s derived from camera_input.gd, and
120 c/s): C2 now PASS and flat (39-44MB peak), C1/C4 remain FAIL
narrowly — single indivisible work items (15-17ms chunk regen,
48.6ms disk op) exceed a frame alone; 2.3ms over at 120 c/s. Found
and fixed an unbounded async-dispatch defect (10.1s hitch) via
MAX_CONCURRENT_ASYNC_TASKS=16. Spike verdict: architecture validated
(residency, region files, load-before-write, sparse regen, save
format); remaining C1/C4 is a tuning problem (async cap, bounded
per-chunk gen cost), not an architecture problem. Full data in
results/storage_residency_spike.csv; findings in README.md.

Task: Pre-Production — ADR-0015 acceptance gate spike

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decisive lever: NO synchronous fallback anywhere — a chunk the async
pool cannot take stays queued instead of running on the main thread.
Worst frame drops to a uniform 12.0-14.5ms across all four
speed(144/120 c/s) x cap(32/64) combos; io_worst/regen_worst 0.00ms
by construction. Async eviction writes with a read-through in-flight
cache close the disk tail (48.6ms -> 0) and a real race (chunk
re-needed mid-flush must be served from memory). Cap size stopped
mattering once the fallback was gone (32 vs 64: ~0.3ms). C2 flat
44-84MB; C3 20/20; C5 40/40 (0.77MB vs 7.63GB naive). All five
ADR-0015 validation criteria PASS at the game's real max camera
speed (144 c/s, derived from camera_input.gd) — Accept recommended
with time-based budget clause, no-sync-in-frame-path rule,
read-through-cache edge case, and one-time region-header I/O noted.

Task: Pre-Production — ADR-0015 acceptance gate spike (round 3)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Spike evidence (afb609c, 799ddbc): all five validation criteria PASS
at the real max camera speed (144 cells/s, derived from
camera_input.gd) and 120 c/s, async caps 32 and 64. Four validated
clause updates applied: time-based page/evict budget (4.0ms), NO
synchronous disk I/O or terrain-gen in the per-frame path (the sync
fallback was the failure mode), read-through in-flight-write cache
as a load-before-write edge case, one-time region-header I/O noted
as the accepted sync exception + completion-driven drain loop for
production. ADR-0014's full-world-at-boot clause formally superseded.
ADR-0012 resolved: save format = region files (Alt-C escape hatch
exercised via 0015). ADR-0013 resolved: offset stays 100_000 —
absolute gap 84,000 units vs the 16k span; increasing it trades
imperceptible for perceptible float32 ULP jitter. ADR log updated.

Task: Pre-Production — ADR-0015 acceptance (spike-gated)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… AD sign-off APPROVED

Full gate run (lean mode, all four directors): CD/TD/PR/AD all
CONCERNS, none NOT READY. Validation artifacts all pass (slice
PROCEED, 16 ADRs Accepted, art bible signed off — AD-ART-BIBLE
APPROVED recorded retroactively in the bible header during the AD
panel). FAIL is the missing planning layer only: control-manifest
refresh (stale, pre-0015/0016), Production milestone + estimate
re-baseline with slice velocity, epics+stories, main-menu/pause-menu
UX specs, first sprint plan. Non-blocking: external playtests before
scope lock, /asset-spec inventory, ambient-life wave 1 + loop-payoff
as early stories. Producer panel's stale ADR-0015 claim corrected in
the report (Accepted, spike-validated). Chain-of-verification: 5
questions, 3 tool actions, verdict unchanged.

Task: Pre-Production — gate check (Pre-Production -> Production)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, epics, menu specs

Pre-Production->Production blocker path items 1-4 (gate report
2026-07-23): (1) control-manifest.md refreshed to 2026-07-23 —
~39 new/expanded rules from ADR-0015/0016 + slice-propagation
amendments (never sync I/O in frame path, built cells job-only,
CW winding, body-column occupancy, engine facts). (2) Milestone 01
Foundation+Core (~22 expected days, 13 exit criteria, CD-protected
items + all three tech debts in scope; loop-payoff mechanic
deferred to M02 with rationale) + estimate re-baseline: 32 expected
days (26-42) vs the uncalibrated 38d, slice-velocity-derived.
(3) Epics: 6 Foundation (incl. cross-cutting foundation-spine)
+ 2 Core, each with governing ADRs, engine risk, M01/M02 tags;
ambient-life + loop-payoff deliberately NOT force-fitted (tracked
in index for a Presentation pass). (4) main-menu.md and
pause-menu.md authored and APPROVED via /ux-review fix cycles
(notable: overwrite-modal default focus safety rule, E11 dim
supersession, Esc-chain 5th-step proposal flagged for propagation,
three-spec confirmation-policy set flagged for one pattern-library
entry). Remaining for the gate: stories per epic, sprint plan.

Task: Pre-Production — gate blocker path (items 1-4)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…stories

/create-stories passes across the M01 epic set (manifest 2026-07-23
embedded, slice code declared reference-only, GdUnit4 evidence paths,
dependency-ordered): foundation-spine 5, scene-world-management 3
(boot-vs-menu conflict carried as NEEDS-DECISION, not a block),
time-tick-system 7 (re-tune story with cross-epic coordination flag),
resource-item-database 9 (footprint TR-052..054), voxel-world 17
(CW-rewrite, fresh ADR-0015 residency in 5 stages, C1/C4 tuning,
TR-050/051 write paths), camera-input 9 (extra_solid contract,
pause-halt note), building-system 33 (Block A pre-slice foundation
019-033 build-first + Block B slice block 001-018 with explicit
depends-on graph; TR-102..127 fully covered), villager-ai-behavior 25
(all determinism ACs as same-seed tests; watchdog telemetry).
Cross-epic sequencing flags recorded (camera before residency, 16k
config-gated until measured, save/load VS-tier). tr-registry TR-123
stale instant-removal clause corrected (superseded by TR-127).

Task: Pre-Production — gate blocker path item: stories

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sprint 1 (production/sprints/sprint-01.md + sprint-status.yaml):
goal = Foundation spine boots behind the RID gate + three track-
starters; 8 stories (5 Must: spine-001/002/003, rid-001/002; 3
Should as cut lever: tick-001, cam-001, vox-001), 10 days with an
explicit 20% buffer absorbing the measured wiring overhead; risks
carried in-doc (single-specialist throughput, quality-bar delta,
NEEDS-DECISION on scene-world 001 deferred to S2, re-tune halves
deferred to the integrated build). Gate report addendum: all five
blockers resolved same-day -> Final Verdict PASS. Forward
recommendations: /qa-plan sprint before /dev-story, external
playtests before scope lock, /asset-spec inventory.

Task: Pre-Production — gate blocker path complete

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gate 2026-07-23 re-check PASS (all five blockers resolved same-day,
AD-ART-BIBLE APPROVED, director panel 4x CONCERNS / none NOT READY).
User-confirmed stage advance.

Task: Production entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lete

Story: foundation-spine story-001 (EPIC foundation-spine)
GameWorld root scene + injected-tier DI scaffold per ADR-0001/0005:
@export injected_tier_modules array, explicit setup() invocation from
the orchestration root (never _ready() ordering), ReferenceInjectedModule
as the canonical module shape (typed @export deps, setup() asserts,
no _ready() override). Establishes neues-spiel/src/<module>/ layout.
Tests: 7 new (3 integration DI-scaffold + 4 unit reference-module),
full suite 10/10 green headless, parent-verified (exit 0). Guardrail
greps clean (no Autoload @export injection, no _ready() dependency
reads). One-time global class-cache rebuild documented as a CI note.
Also: QA plan sprint 1 (production/qa/qa-plan-sprint-1-2026-07-23.md —
8/8 stories classified, all BLOCKING, smoke scope defined) and
test-evidence paths normalized to neues-spiel/tests/ across story
files (QA-plan finding). Story status Complete; sprint-status updated.

Task: Sprint 1 — spine-001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r-only view

Story: resource-item-database story-001 (Sprint 1)
ItemDefinitionResource (11 @export schema fields incl. footprint
Vector2i) + ItemDefinition getter-only RefCounted wrapper per
ADR-0006 (wraps, never copies; zero setters; Object.get()/set()
reflection bypass documented as accepted residual risk). 3 unit
tests at the QA-plan evidence path; setter check scoped to the
class's own declared API via get_script_method_list() (inherited
engine set_* methods excluded — documented test fix). Full suite
13/13 green headless, parent-verified.

Task: Sprint 1 — rid-001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
crynox98-hash and others added 30 commits July 27, 2026 18:36
The settlement and player-view captures re-taken after vox-022/vox-023,
with the persisted regions cleared first so no stale single-colour chunk
could page in and make the picture lie.
These are the frames behind decision D12: four terrain colours genuinely
render, and the composition is wrong — grey and pale peak dominate where
a valley should be green, because D11's provisional band anchoring puts
the boundaries at [2,4,5] in a world whose columns top out at y=7. The
lever passed; the picture did not. Both are true and both are recorded.

Task: Sprint 12 — evidence refresh

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: villager-ai story-024 — the blocking defect filed this morning
A room drawn through the hosted WallTool now reaches FULL ENCLOSURE.
Verified directly: wall_room_no_plateau_test PASSED, and an isolated
column completes at the shipped height of 3 AND at 6, so the fix is
structural rather than tuned to one height.
THE RULE, confirmed empirically and not merely theorised: VillagerNavGraph
can never connect two cells in the same column. A step is defined as
inherently horizontal, and is_standable requires the cell below to be
solid — so two stacked cells can never both be standable graph points at
once. A wall's layer 2 is reachable from always-standable ground in one
combined step. Layer 3 needs a second such extension, but its only
same-column neighbour at the required height is layer 2's own cell, which
goes solid and leaves the graph in the SAME patch_cells call that adds
layer 3. Layer 3 enters as an ISOLATED point: has_point true, find_path
from every occupied cell returns [] on every tick up to 5000. Job
selection excludes it before any claim — which is exactly why the stuck
cells read PLANNED, claimed_by -1, abandon_count 0.
This also refutes Sprint 12's spike-plateau hypothesis, which assumed
seal-prevention refusals. That path is never consulted at all.

  PLATEAU-CHECK cell=(2, 1, 3) state=2 claimed_by=1  abandon_count=0
  PLATEAU-CHECK cell=(2, 2, 3) state=2 claimed_by=1  abandon_count=0
  PLATEAU-CHECK cell=(2, 3, 3) state=0 claimed_by=-1 abandon_count=0

THE FIX IS SYMMETRIC: a builder standing where the graph cannot reach is
moved discretely, at the two moments it happens — climbing ONTO the
self-sealed cell, and stepping back DOWN when the column is done. The
obvious alternative, widening stuck detection into WANDERING, was
rejected: villager-ai-019 deliberately makes a walled-in wanderer STAY
PUT, and a walled-in villager is indistinguishable from a stranded one by
flood-fill alone. It reads as what a real builder does. ADR-0009's "two
sanctioned mutation points" is amended, control-manifest follows.
Seal prevention and the livelock escape are intact — 13/13 and 12/12,
both suites unchanged.
AND A DEFECT OF MY OWN, found here: payoff_loop_demo.gd has not PARSED
since commit 6ddb153 — two while-true helpers with no trailing return,
written by the scene-009 attempt and committed without the tool ever
being load()-ed once. The tool that exists to catch "shipped but never
run" was itself shipped and never run. Fixed, with the reason recorded at
both sites. Its new roof stage is written but unexecuted, so AC5/AC6 ship
as a named debt rather than a claim.
Suite 1580/1580 green, parent-verified.

Task: Sprint 12 — villager-ai-024

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
villager-ai-024's AC5/AC6 are RUN and NOT MET, recorded rather than
rounded up. The demo reached 27/30 wall cells and 9/12 roof cells, and
never reached the bed claim inside its 300s cap. 06-claimed.png and
07-sleeping.png do not exist, correctly — the tool refuses to photograph
a stage it did not reach.
The wall fix is real: 19/30 before it, 27/30 after, same cap, and the
unit test reaches 30/30 on a tick budget. The gap between 30/30 in test
and 27/30 live is not a second defect. It is D10, and the tool listed
every instance instead of smoothing them:

  D10 check: 6 SLEEPING episode(s) observed this run
    episode 1: GROUND sleep at (992, 9, 1003) (no bed owned yet)
    ... six of them, every one on the ground, none in a bed ...

The villager spends more of the run exhausted than building. It builds
its own house, gets tired, lies down in the dirt beside it, gets up,
builds a little more, and never gets far enough to claim the bed it
placed itself.
So D10 stops being a curiosity and becomes a blocker: scene-009 cannot
show a villager sleeping in a bed it built until the villager can stay
awake long enough to finish the room. The decision is the creative
director's and is logged with three options and a recommendation — NOT
taken, because unlike the lighting values it is not reversible by
editing one number without changing how the early game feels.

Task: Sprint 12 — villager-ai-024 AC5/AC6 outcome

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: presentation-experience story-005 (authored and implemented; the
story file did not exist, which is why Sprint 12's gate G2 never really
closed for this lane)
A placed bed existed in the registry, had a Mesh in the game data, and
had nobody to draw it. It now has a view, hosted in Valley and reported
through get_injected_tier_modules like every other module.
THE LEVER IS THE LIVE-TREE COUNT — the same technique that found the
missing camera — measured against the unchanged build first:

  SCRATCH BASELINE: placed bed item_id=1, registry now holds 1 item(s)
  SCRATCH BASELINE: furniture view nodes in live tree AFTER a real
    placed bed = 0
  SCRATCH BASELINE: Valley.get_child_count() = 29

After: exactly one view per footprint, confirmed by a real tree walk and
not by asking the presenter how many it thinks it made.
Deletion probe run and recorded, not asserted:
  Assertion failed: Valley: FurniturePresenter.furniture_registry must be
  wired to the SAME hosted FurnitureRegistry instance -- never left null,
  never a second one
Restored byte-for-byte.
READ-ONLY BY CONSTRUCTION, grep-guarded: the view reads the registry's
signals and never writes. BV-1 forbids furniture from reaching the voxel
grid, and that prohibition is exactly what makes build-validation-002's
transparency guarantee true — so the guard protects a guarantee, not a
convention.
Per-entity view/presenter, mirroring presentation-003's landed pair. The
MultiMesh-versus-per-item ruling was named non-blocking and never
arrived; the MVP has one multi-cell furniture type, so the scaling
argument does not bite yet. Recorded so a later ruling can overturn it
knowingly.
ONE AC STAYS OPEN AND IS NOT ROUNDED UP: there is no fresh screenshot.
Two demo runs stalled at 27/30 walls and never reached the furniture
stage — villager-ai-024's plateau compounded by D10's pacing, not this
story's code. Rendering is proven headlessly against the real bed.tres
mesh. The picture is owed, and the stale 05-furnished.png on disk (14:13,
before the terrain recolour) must not be passed off as evidence for it.
23 new tests. Suite 1603/1603 green, parent-verified.

Task: Sprint 12 — presentation-005

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pacing

The user caught this and they are right. I reported the villager
'sleeping six times on the ground' and escalated D10 as blocking two
stories. The coordinates say otherwise: y=9 and y=10, against a site at
y=6 with walls to y=8 — every sleep ON TOP of its own structure, with
state=5, WANDERING.
The demo's 'GROUND sleep' label means 'no bed owned', not 'on the
ground'. I read my own instrumentation literally and drew the wrong
conclusion from it.
What actually happened: the villager climbed up, could not get down,
wandered the roof plane and slept there because nothing else was
available. That is this story's own stranded-on-an-isolated-graph-point
case, and it shows the symmetric descend fix does not cover this path —
it fires when a column completes, not when a builder is left stranded on
a finished structure.
So D10 stays a legitimate design question but is NOT blocking. Stranding
is. Recorded here and in the session state; the tool's misleading wording
needs fixing so it cannot mislead again.

Task: Sprint 12 — correction to villager-ai-024's AC5/AC6 note

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s ten gaps

Story: building-system story-034 (032 and 033 were taken; noted in the
file). The user proposed scaffolding as the real fix for the
reachability gap and ruled on all four boundary conditions; this writes
that down faithfully rather than redesigning it.
THE KEY PROPERTY IS THAT IT IS NOT SOLID. Minecraft's scaffolding has no
collision: you pass through it, stand on top, climb it from inside. Two
risks vanish for free — a non-solid cell can never read as a wall or
roof, so no phantom rooms; and nobody can seal themselves in behind
something they can walk through, so seal prevention needs no
scaffold-awareness at all.
The nav amendment is deliberately bounded: a scaffold cell is standable
without solid ground beneath it, and vertical edges are legal ONLY when
BOTH endpoints are scaffold. Two ordinary stacked cells still never both
stand. Proposed ADR-0007 wording is in the story, flagged for the TD
rather than edited in.
THE LEVER IS TWO REAL-BOOTED-GAME ASSERTIONS, and they fail differently:
find_path to the top course is empty today (measured, every tick to
5000), and find_path from the finished roof back to the ground is ALSO
empty — which is the stranding the correction found. Lever 1 can pass
while lever 2 still fails, so both are needed.
TEN GAPS THE FOUR RULINGS DO NOT ANSWER, all recorded as open decisions
with options and a recommendation, none decided. The sharpest: a scaffold
cell cannot live in the voxel grid at all, because any non-zero block id
is solid to every reader; scaffold-as-project-member would stop the
project ever reaching DONE, which is the very state that triggers
dismantling; the game has no gravity, so a bottom-up collapse would
strand the worker and reintroduce the bug; and AC1's automatic trigger
has no implementation, because the pre-claim reachability skip is
documented as silent by design.
Also fixed here: the demo's misleading label. It said GROUND sleep,
meaning only "no bed owned", and I read it literally as asleep on the
ground — which sent a pacing question to the creative director that was
never the cause. It now says BEDLESS and points at the cell height, with
the reason recorded: a label that reads as a location must not describe
an inventory fact.

Task: Sprint 12 — scaffolding authoring

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ten open decisions ruled, two deliberately left to the user, and three
load-bearing corrections to the story's own proposed wording.
THE CRUX (D1): a scaffold cell cannot live in the voxel grid — any
non-zero block id is solid to every reader. It gets its own Building
System registry, and the shared walkability predicates take occupancy as
an explicit, defaulted parameter. Build Validation passes nothing and
therefore stays scaffold-blind STRUCTURALLY rather than by convention.
THREE CORRECTIONS THE TD MADE TO THE STORY, each of which would have
been a real defect:
 1. The _after_write predicates MUST be scaffold-aware. A villager on
    scaffolding has AIR beneath it, so a blind check would report
    would_trap_builder for nearly every write. "Seal prevention needs no
    scaffold-awareness" is true of the gate, not of its inputs.
 2. is_step_legal must now EXPLICITLY refuse same-column steps. Today it
    would already return true; the only thing preventing that edge was
    that two stacked cells can never both be standable — and the new
    self-supporting clause removes exactly that guard.
 3. A scaffold write is not a voxel write, so cell_changed never fires
    and the nav graph would silently never learn scaffolding exists.
    New synchronous patch signal added.
Also caught: the story proposed reusing a 3D Chebyshev helper for the
6-cell cantilever. It is 3D, which would have silently converted the
user's OVERHANG cap into a HEIGHT cap. Ruled horizontal-only.
D3 resolved the self-contradiction: scaffolding is its own project kind
linked by owner id, so the owning project can actually reach DONE — the
state that triggers dismantling. And it IS persisted, because
non-persistence would strand a villager across a save/load, which is the
exact bug being fixed.
D4: cancel checks whether a worker is aboard — occupied means top-down
worker-ridden, unoccupied means the fast bottom-up collapse. No fifth
sanctioned mutation point.
LEFT TO THE USER, correctly: D7's tick values (pacing/feel) and D2's
visual identity (art direction). The 6 itself was already the user's.
RETIREMENT OF THE CLIMB HACK: ruled retire ON EVIDENCE, not on landing.
ADR-0009's two discrete mutations stay, each gains a telemetry counter,
and the anti-vacuity levers require both to read ZERO for the build
phase — so a firing FAILS the lever instead of masking a scaffolding
failure. ADR-0009 is untouched today.
ADR-0007 -> v1.1; control-manifest -> 2026-07-27 with three new required
bullets and one new forbidden bullet ("never make scaffolding solid,
never generalize the vertical edge").

Task: Sprint 12 — story-034 TD rulings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commit d214c7f was meant to carry the TD's one-line ADR-0007 log entry in
.claude/docs/technical-preferences.md. It also carried 26 lines that are
NOT mine: the user's "Bundled Fonts" table (Cinzel, EB Garamond,
MedievalSharp, Caveat and the reserve face, with their licence note) and
their "UI Quality Gates" section, including the contrast checker that had
never been run before today and immediately found three failures.
That file was explicitly on my do-not-touch list, because the user is
working on assets and UI in parallel. I passed the path anyway without
checking what else was in it — the same mistake as this morning's
sweep, just narrower, and caught by reading my own diff instead of by
luck.
Nothing is lost and nothing is reverted: the content is the user's, it
belongs in the repo, and rewriting already-pushed history to re-attribute
it would cost more than it is worth. This note exists so the record is
accurate rather than tidy.
Practice restored: check `git show --stat` on any shared-file path before
committing it, not only after.

Task: Sprint 12 — accidental payload in d214c7f

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: building-system story-034. A full implementation pass was written
and then deliberately backed out. The gate was red and the work
regressed a previously green test, so it was not committed. The entire
diff and every new file are preserved at
production/parked/story-034-scaffolding-WIP.patch — nothing is lost and
the next pass starts from there.

TWO REAL DEFECTS WERE FOUND AND FIXED DURING TRIAGE, both kept in the
parked patch:
 1. The same-column gate broke ADR-0007 section 1b's own binding promise
    that a caller passing no scaffold source sees EXACTLY pre-amendment
    behaviour. It refused every same-column pair; the predicate used to
    fall through and return true. The TD rationale — two stacked cells
    can never both be standable, so the case never arises — holds only
    for callers that check standability first, and not every caller
    does. Correct gate: both scaffold is legal, exactly one is illegal
    (that is what closes general climbing), neither falls through.
 2. The same defect existed a SECOND time, copied verbatim into
    villager_ai's _is_step_legal_after_write. Those twins feed
    would_trap_builder, so the builder believed far more writes would
    trap it than actually would.
Fixing both turned walkability_predicates_test 18/18 and the new
scaffolding levers 5/5 green.

WHAT STOPPED IT, unresolved: villager-ai-024's regression test drops
30/30 to 27/30 with an entire CORNER COLUMN stuck, all three cells
PLANNED and never claimed — a different shape from 024's original
top-layer symptom. Ruled out: the job-selector change is purely
additive, and report_unreachable only sets a flag that claim_job clears.
Prime suspect, unconfirmed: the new same-column escape loop tells
would_trap_builder an upward escape exists onto the block being placed.
That escape is real only through 024's sanctioned discrete climb, not
through a legal step, so seal prevention now permits writes it used to
refuse, and one of them seals the corner approach.

Also recorded, not acted on: the reachability corpus measured 60.83s
against its 60s ceiling on an idle machine with the tree restored to the
last green commit; the same corpus measured 45.78s earlier today on
identical code. That is load drift, not a regression. Its Cut-Lever
Policy would permit shrinking the corpus and the lever was deliberately
NOT pulled — coverage is not traded away for a transient.

Task: Sprint 12 — story-034 first pass parked

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: building-system story-034, second pass. The first pass was parked
red (4b73f47); this restores it from the parked patch and resolves what
stopped it. Suite 1640/1640 green, 0 orphans, parent-verified.

THE CORNER-COLUMN REGRESSION, pinned by bisect rather than guessed.
villager-ai-024's regression test fell 30/30 -> 27/30 with an entire
corner column left PLANNED and unclaimed. Two hypotheses were tested and
DISPROVEN first — the job selector change is purely additive, and the
new vertical escape loop was not it either (guarding it scaffold-only
changed nothing). Disabling D5's reporting made the test pass; keeping
the report but dropping the cooldown write ALSO made it pass. That
isolates it exactly:

  _unreachable_retry_after_tick is read by the CANDIDATE FILTER.

D5 wrote every pre-claim probe miss into it, so the villager stopped
re-probing those cells for 20 ticks, built other cells first, and sealed
the corner off. A pre-claim probe is cheap and must repeat every pass,
because a cell that is unreachable now routinely becomes reachable as
the build progresses.
FIX: the throttle gets its OWN map. The TD's intent — no report storm —
is honoured; selection is left alone. Reporting is throttled, reachability
is not.

THREE MORE DEFECTS FIXED, each instructive:
 1. The same-column gate broke ADR-0007 section 1b's binding promise that
    a caller passing no scaffold source sees EXACTLY pre-amendment
    behaviour. It refused every same-column pair; the predicate used to
    fall through and return true. Correct gate: both scaffold legal,
    exactly one illegal (that is what closes general climbing), neither
    falls through.
 2. The same defect existed a SECOND time, copied verbatim into
    villager_ai's _is_step_legal_after_write, which feeds
    would_trap_builder.
 3. A new test asserted the blanket refusal — it encoded the wrong
    semantics and was corrected, not deleted, with the reason inline.
    Another gave a villager no escape route at all and then demanded
    would_trap_builder answer false; it now has a real scaffold cell
    below, because the predicate was right and the fixture was wrong.
 4. The registry idempotence test counted signal emissions in an int
    captured by a lambda — GDScript captures scalars BY VALUE, so the
    counter read 0 forever. Array-and-append. This is the exact trap
    named in every briefing today, and it still landed in code written
    under that briefing.

CUT LEVER PULLED on reachability_property_corpus_test, PAIRS_PER_SEED
5 -> 4, exactly as that file's own policy directs: reduce pairs before
seeds, re-measure once, escalate. The ceiling was NOT relaxed and the
test was NOT skipped. Measurements: 45.78s at midday, 60.83s in the
evening ON IDENTICAL CODE with an idle machine and the tree restored to
the last green commit — the baseline drifted, the algorithm did not.
Scaffolding then added 2.4% (60.83 -> 62.27), inside ADR-0007 v1.1's own
+5% budget. After the cut: 49.11s. Earlier today I said I would not pull
this lever because the drift looked transient; it is not, and the
evidence changed the decision.
ESCALATED: if the baseline keeps drifting, the next lever is not another
cut but a TD ruling on whether a wall-clock ceiling guards the right
thing, since it measures the machine as much as the code.

STILL NOT DONE, named not hidden: ScaffoldRegistry,
ScaffoldErectionCoordinator and ScaffoldPresentation are NOT yet wired
into Valley, so scaffolding does not yet erect itself in the running
game — the levers pass against the production classes called directly.
The live dismantle orchestrator on project DONE and on cancel is also
outstanding, as is the payoff-loop demo and the telemetry-zero proof.
D7 (tick values) and D2 (visual identity) remain the user's.

Task: Sprint 12 — building-034 second pass

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… of them real

Story: building-system story-034, third pass (the Valley hosting). Parked
at production/parked/story-034-valley-wiring-WIP.patch; the tree is back
at 6cc9002 and green. Second consecutive park on this story, which is
itself the finding: it is too large to land in one pass and the next
attempt should be cut into hosting / erection / dismantle as separate
landable pieces.

WHAT THE PASS PROVED, and it is the important part — the D5 to erection
path works IN THE RUNNING GAME, observed directly against a real boot:
the real hosted ConstructionJobQueue.report_unreachable fires the real
job_reported_unreachable signal, the real hosted
ScaffoldErectionCoordinator plans and registers real SCAFFOLD-kind
BuildProjects in the real BuildProjectRegistry (ids 2/3/4, kind=2,
owner=1), and those cells build to completion through the real
ConstructionTickLoop into the real ScaffoldRegistry. Both ADR-0009
telemetry counters read ZERO in that configuration — the climb hack no
longer fires. Pre-change verbatim, before any wiring:

  PROBE cell=(1032,  8, 1039) state=1 claimed_by=0  is_unreachable=false
  PROBE cell=(1032,  9, 1039) state=0 claimed_by=-1 is_unreachable=true
  PROBE cell=(1032, 10, 1039) state=0 claimed_by=-1 is_unreachable=true

WHY IT DID NOT LAND — five failures, 1633 cases:
 1. gameworld_e2e_loop_test — exact injected-tier module count moved to
    27. Mechanical, expected, needs a conscious update.
 2. world_root_valley_attach_test — exact hosted-child count moved.
    Mechanical, same.
 3. scaffolding_hosting_test lever1 — the new test file itself is
    unstable. It drives claims and positions directly, bypassing real AI
    travel, and a queue-level release_claim does not reset VillagerAi's
    own pursuit state, so the villager re-fires its detection mid-loop
    and spawns extra scaffold structures that shift the geometry the test
    assumes. Also only 1 of its 3 test functions executes per invocation,
    undiagnosed — so lever2, the dismantle proof, has never actually run.
 4. transition_persistence_test — a GREP GUARD: scene_world_management
    source must never reference TimeTickSystem, and the wiring introduced
    exactly such a reference. A clean architectural violation, not a test
    problem. Whatever the dismantle orchestrator needs from the clock has
    to arrive by injection.
 5. wall_room_no_plateau_test — villager-ai-024's regression test is RED
    AGAIN. This is the one that matters. With scaffolding live the room
    should build BETTER, not worse, so something about erecting real
    scaffolding mid-build changes the geometry or the ordering
    destructively. Not diagnosed.

NEXT PASS SHOULD, in this order:
 1. Fix (4) first — it is a one-line architectural violation and cheap.
 2. Diagnose (5) BEFORE writing anything new, with the same bisect
    method that pinned the last corner-column regression: disable one
    seam at a time against the real hosted boot until the wall test goes
    green, then name the mechanism.
 3. Land hosting alone, with only the boot invariant and the count
    updates as its lever. That is a small, provable piece.
 4. Land erection second, using the direct observation above as its
    lever rather than the unstable claim-driving test.
 5. Land dismantle last, with a test that constructs BUILD and SCAFFOLD
    projects directly and never involves the AI — the shape lever2 was
    reaching for and never got to run.

Task: Sprint 12 — story-034 Valley wiring parked

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: building-system story-034, fourth pass. The third pass tried to
land hosting, erection and dismantle together and was parked red
(da7dc89). This lands the piece that is provable on its own and holds
the rest back for a MEASURED reason, not for tidiness.

WHAT LANDS: Valley hosts one ScaffoldRegistry, injects that SAME
instance into every hosted villager (the default one and every roster
member), subscribes the nav graph to it, and hosts ScaffoldPresentation.
Boot invariant added. Suite 1643/1643 green, 0 orphans.

WHAT IS HELD BACK, and why it is a finding rather than a cut:
connecting ScaffoldErectionCoordinator to job_reported_unreachable makes
villager-ai-024's regression test fall 30/30 -> 27/30 with a whole
corner column left PLANNED and unclaimed. Isolated by bisect, not
guessed: with the erection response disabled the test passes; with it
enabled it fails; the boundary is exactly that one signal connection.
Scaffolding is supposed to MAKE cells reachable, so this is a real
defect and not a tick-budget effect, and it must be understood before it
ships. Dismantle waits with it — there is nothing to dismantle until
erection runs.

TWO REAL DEFECTS FIXED ALONG THE WAY:
 1. GREP-GUARD VIOLATION: the wiring resolved /root/TimeTickSystem
    directly from valley.gd, and Scene & World Management is forbidden
    to reference TimeTickSystem at all. The clock is now taken from
    ConstructionTickLoop, which already resolves and holds it — both
    compliant and a guarantee that the two coordinators share one clock
    instead of racing two.
 2. A LATCH IN THE ERECTION COORDINATOR: it marked a cell "served"
    BEFORE checking whether a plan existed, so a cell whose plan failed
    once was suppressed forever, even after the world changed and a plan
    became possible. A failed plan is a "not yet", never a "never".
    Fixed and kept, though the coordinator does not run yet.

THE BOOT INVARIANT ASSERTS ONLY WHAT THIS PIECE WIRES. The parked
version asserted the erection and dismantle coordinators too; asserting
on something this piece deliberately does not construct would be a lie
about what is wired. Their invariants land with them.

The new hosting test is deliberately small and AI-FREE. Its predecessor
drove villager claims and positions directly and was unstable, because a
queue-level release_claim does not reset VillagerAi's own pursuit state,
so the villager re-fired detection mid-run and moved the geometry the
assertions assumed. This one asserts identity — same registry instance
in the presentation tier, the tick loop and every villager — plus
ADR-0007 section 1b's binding promise that a caller passing no scaffold
source sees exactly pre-amendment behaviour. That last one is the
invariant whose first implementation broke it and turned the suite red.

One fixture bug of my own, fixed and recorded: the section-1b assertion
first probed a cell at y=40, outside max_y=16, where every read is
treated as blocking — so the cell was unstandable for a reason that had
nothing to do with scaffolding. The predicate was right; the fixture was
wrong.

Two exact-count assertions moved (26->27 modules, 30->31 children),
updated consciously with the reason inline.

Task: Sprint 12 — building-034 occupancy tier

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: building-system story-034, erection tier. The occupancy tier
landed in c3036df with this deliberately held back, because connecting
it dropped villager-ai-024's regression test from 30/30 to 27/30. That
defect is now understood and fixed. Suite 1645/1645 green, 0 orphans.

THE CAUSE, measured rather than guessed. Instrumenting the coordinator
against a real hosted build of a 30-cell room printed 20+ unreachable
reports — and `planned=true` for EVERY ONE, including cells that were
perfectly reachable moments later:

  SCAFFPROBE report cell=(1002, 7, 1006) owner=1 planned=true cells=1
  SCAFFPROBE report cell=(1003, 8, 1006) owner=1 planned=true cells=2
  ... 20+ of them, essentially every wall cell in the room ...

A pre-claim probe calls a cell unreachable whenever it is unreachable AT
THAT INSTANT, and with one villager walking around a site that is most
cells most of the time. Erecting on the first report therefore floods
the queue with scaffold jobs — and since a villager may hold only ONE
claim, the actual wall cells starve. The corner column that stayed
PLANNED was not blocked; it was outcompeted by scaffolding for cells
that never needed any.

THE FIX IS THE USER'S OWN RULE, made literal: scaffolding is built only
when it is NEEDED. A cell must now report unreachable
REPORTS_BEFORE_ERECTING times before it earns a scaffold. One report
means "not right now"; repeated reports mean "not without help". A
momentarily blocked cell recovers on its own and costs nothing.

Two tests, both AI-free by design — an earlier attempt drove villager
claims directly and was unstable, because a queue-level release_claim
does not reset VillagerAi's own pursuit state, so the villager re-fired
detection mid-run and moved the geometry the assertions assumed:
 - a single report never erects anything (the persistence gate itself,
   and the assertion that would have caught this defect on day one);
 - the coordinator is not merely constructed but actually SUBSCRIBED to
   the real seam — hosted-versus-wired being this project's most
   repeated failure mode.

One API slip of my own, caught by the test and fixed: I called
get_project_ids() on BuildProjectRegistry, which has get_projects().

Still to come: the dismantle tier, with a test that constructs BUILD and
SCAFFOLD projects directly and never involves the AI.

Task: Sprint 12 — building-034 erection tier

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Story: building-system story-034, dismantle tier. Completes the wiring
the occupancy tier (c3036df) and the erection tier (9542c68) began, and
finishes what two earlier all-at-once attempts could not land.

Valley now constructs ScaffoldDismantleCoordinator and connects it to
both triggers the ruling names: the owning project reaching DONE, and
the player cancelling it mid-build. The clock comes FROM
ConstructionTickLoop rather than being resolved here — Scene & World
Management is forbidden to reference TimeTickSystem at all, there is a
grep guard on the directory, and taking the tick loop's own instance
also guarantees the two coordinators share one clock instead of racing
two. The boot invariants for both coordinators are restored now that
Valley genuinely constructs them; while it did not, asserting on them
would have been a lie about what was wired.

TWO TESTS, AI-FREE ON PURPOSE. Two earlier attempts at proving this half
drove real villagers and were unstable: a queue-level release_claim does
not reset VillagerAi's own pursuit state, so the villager re-fired its
detection mid-run and moved the geometry the assertions assumed. These
construct the projects directly, so what is under test is the ORDERING
and the TRIGGER, never the pathfinder.
 - Both connections are asserted by IDENTITY, not by the coordinator
   merely existing. Constructed-but-unsubscribed is this project's most
   repeated defect and the reason six such gaps were closed yesterday.
 - Top-down ordering is asserted against deliberately SHUFFLED input, so
   the planner has to impose the order rather than inherit it.
 - SC-INV-1: a cell a worker's body column occupies is skipped, and the
   one below is taken instead. Without that, "top-down" would delete the
   floor from under the worker riding the structure down — the exact
   stranding scaffolding exists to prevent.

One trap of my own, worth recording because I have warned every agent
about it all day and still walked into it: the occupied-body-column
fixture used an untyped inner array literal, which crashes on assignment
to Array[Vector3i]. Typed, with the reason inline.

Suite 1648/1648 green, 0 orphans — verified on a tree that also carries
the user's in-flight world-height work (max_y 16 -> 96), so this figure
covers both.

Task: Sprint 12 — building-034 dismantle tier

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…defect

First payoff-demo run with scaffolding fully wired, in the 96-height
world. 30/30 wall cells BUILT in 16.1s — every previous run of this demo
stalled at 27/30 and hit its cap. Scaffolding is visible in 04-built.png
as orange blocks (placeholder; visual identity is still the user's call).
The user's own design proposal is now load-bearing in the game.
AND IT EXPOSED THE NEXT DEFECT. The roof reached only 10/12 before its
cap, the bed claim was never reached, and there were five bedless sleeps
— every one at y=10, above the walls (6..8) and above the roof plane
(9). The villager is stranded on top again.
Working hypothesis, not yet confirmed: dismantle fires when the WALL
project reaches DONE and removes the scaffolding while the villager is
still up there building the roof, taking away its way down. SC-INV-1
protects the cell a villager STANDS IN; it does not protect the route it
needs to descend. If so, the dismantle trigger is too eager in precisely
the way the erection trigger was — 'the project I served is done' is not
'nobody needs this structure any more' — and the fix mirrors the
persistence gate that fixed erection.
The demo label corrected last night earned its keep immediately: it now
prints 'a villager asleep above the build site is stranded, not merely
tired' with the cell height. The old GROUND-sleep wording is exactly
what made me misread this same symptom as a pacing question.

Task: Sprint 12 — building-034 visual proof and follow-on finding

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…V-2)

Story: building-system story-034. Found by the first payoff-demo run that
scaffolding actually completed — 30/30 wall cells in 16.1s, a first — and
then stalled anyway: the roof reached only 10/12 and the villager slept
five times at y=10, above the walls (6..8) and above the roof plane (9).

THE DEFECT: dismantle fired the moment the WALL project reached DONE.
The roof is a SEPARATE project, drafted afterwards, so the scaffolding
came down while the builder was still up there working on it — taking
away its descent. SC-INV-1 protects the cell a villager STANDS IN, which
is not enough: a builder that climbed up to lay the top course has its
own cell untouched and no way back to the ground.

SC-INV-2 is the missing half: a structure is not dismantled while any
villager's body column sits at or above the structure's own base within
its footprint. The served project being finished is not the same as
nobody needing the structure — the exact over-eagerness the erection side
had before its persistence gate, one level up.

A GAP IN MY OWN FIX, caught before it shipped: a deferred owner would
never have been revisited, because the construction-completed trigger
fires once and is then gone for good. Deferred owners now go on their own
list that the tick re-polls, and they are retried BEFORE the already-
draining ones so a villager that has just climbed down does not wait an
extra tick.

The new test constructs the projects directly and never involves the AI —
the third time that shape has proven necessary in this story, because a
queue-level release_claim does not reset VillagerAi's own pursuit state.

Suite 1667/1667 green, 0 orphans.

Task: Sprint 12 — building-034 SC-INV-2

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decision D11's own intent, finally implemented. The bands were supposed
to be anchored to the achievable terrain range; they shipped as absolute
Y values, so they did not travel — and every world-size change broke
them in a different direction.

BOTH ABSOLUTE ANSWERS WERE WRONG, which is what makes the fractional one
right rather than merely tidier:
 - `[2, 4, 5]`, written for the 16-high world: raise the world to 96 and
   terrain runs y≈16..64, so EVERY cell sits above the top boundary and
   the whole world renders in one band. Observed as a uniform cream mass.
 - `[24, 48, 72]`, quarters of the WORLD as art bible §4.3 reads it: the
   top band then lands entirely above the reachable ground, so that
   material never appears at all. A dead band instead of a single one.
   (The parallel biome session had noted these and reached the same
   conclusion independently.)

Boundaries are now derived from `band_split_fractions` across
`base_height ± amplitude` — the range terrain generation actually
produces — so "half way up" means half way up at any world size.

THE DESIGN CHOICE IS SURFACED, NOT BURIED. Art bible §4.3 thinks of the
bands as quarters of the WORLD, because a snow line is an absolute
height; that reading is self-consistent and it is exactly why a flat
world has no snow. Anchoring to the achievable range guarantees all four
materials appear at every world size, at the cost that in low relief the
peak material sits on a hilltop rather than a mountain. The default is
even quarters, which keeps the art bible's proportions; pushing the last
fraction up (e.g. [0.3, 0.6, 0.9]) reserves the peak for genuine crests.
That dial belongs to the art director, so it is data, and the trade-off
is written where they will read it.

An explicit `band_boundaries` list still wins outright, so one specific
world can be hand-tuned. `validate()` learned the derivation mode and now
checks the fractions instead — and two things that would otherwise have
been rejected: a single-band world needs no split points at all, and the
band_ids family checks must keep running in derivation mode rather than
being skipped by an early return. Both were caught by existing tests.

Test expectations now read `effective_band_boundaries()` — the same
source production uses — instead of the raw field, which is what a test
computing "what should this config produce" always meant.

Suite 1667/1667 green, 0 orphans.

Task: Sprint 12 — D11 implemented properly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Second payoff-demo run with SC-INV-2 and the derived bands. Walls again
30/30 in 16.2s, so scaffolding's win is reproducible. Everything else is
identical to the run before the fix: roof still 10/12, the same five
bedless sleeps at the same five cells. The evidence PNGs came out
byte-identical, which incidentally confirms end-to-end determinism.
So the hypothesis was wrong. Dismantle taking the builder's descent is
not what stalls the roof. SC-INV-2 stays — it provably defers dismantle
while a builder is above the structure and its test passes on its own
terms — but it was not this defect, and claiming otherwise would be
taking credit for a coincidence.
Three refuted hypotheses in one night: the vertical escape loop, the
served-cells latch, and now the dismantle trigger. Every one was
plausible from reading. Every one fell to a bisect. Reading identified
no real cause tonight; measurement identified all of them.
The defect is now sharper: y=10 is on top of the roof plane (walls 6..8,
roof 9), so the villager stands on built roof cells and cannot descend,
and the two missing cells are the interior ones reachable only by
standing on already-built roof — villager-ai-024's 'cannot chain two
extensions', one storey up.
Next diagnostic written down and deliberately not yet run: the two
ADR-0009 telemetry counters at the end of the demo, and the
ScaffoldRegistry contents during the roof stage. Non-zero counters would
mean the villager climbed via the hack and scaffolding never served the
roof at all.

Task: Sprint 12 — building-034 roof defect narrowed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es not

Two diagnostics added to the payoff demo, plus a control run, because my
earlier claim that scaffolding took the walls from 27/30 to 30/30 rested
on a before/after coincidence rather than evidence.

DIAGNOSTIC RUN:
  room walls: all 30 cells reached BUILT after 16.3s
  roof construction result: 10 / 12 cells reached BUILT
  scaffold cells standing at roof stage: 0 []
  ADR-0009 climb mutations: self_seal_climb=40 marooned_relocation=9

CONTROL RUN, erection response disabled, everything else identical:
  room walls: WAIT CAP (220s) hit with 27/30 cells BUILT
  roof construction result: 9 / 12

THREE THINGS ARE NOW SETTLED, and one of them corrects me.
1. Scaffolding's contribution is real and is exactly the last three wall
   cells. Disable erection and the demo falls back to precisely the
   historical 27/30; enable it and the room closes. I should have had
   this control before making the claim, not after.
2. The roof gets NO scaffolding at all — zero cells standing at that
   stage. So the roof stall was never a dismantle problem: the erection
   trigger simply never fires for roof cells. The defect is now located
   instead of suspected, which also explains why SC-INV-2 changed
   nothing there.
3. The ADR-0009 retirement criterion is nowhere near met. The TD ruled
   the climb mutations retire on EVIDENCE — the counters reading zero —
   not on scaffolding landing. They read 40 and 9. Both mechanisms carry
   the build today and scaffolding is the smaller contributor; retiring
   the climb hack now would drop the walls back below 27/30.

The likely reason the roof gets nothing is written into the story: a roof
cell's support would have to rise from inside the room the walls just
enclosed, and the planner prefers the column directly below the target.
Deliberately NOT acted on — the next step is one more print and one run,
because three hypotheses were read confidently tonight and all three were
refuted by bisect.

Task: Sprint 12 — building-034 contribution measured

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…efect

One print, one run, and the answer matched none of the three earlier
hypotheses.

  SCAFFDIAG cell=(993, 9, 1002) seen=3 PLANNED=true cells=1
  SCAFFDIAG cell=(993, 9, 1003) seen=3 PLANNED=true cells=1
  scaffold cells standing at roof stage: 0

Exactly two roof cells ever report unreachable — the two interior ones,
matching 10/12 exactly. Both clear the persistence gate. Both plan
SUCCESSFULLY. And nothing is standing at the roof stage.
The coordinates settle it: the four wall segments enclose x 992..994,
z 1001..1004, so the interior is the single column x=993, z=1002..1003 —
exactly where the two missing roof cells are. Their scaffold support
would have to stand INSIDE that interior, and the interior is sealed.
The 30/30 wall success closed the room completely: four segments, no
gap, NO DOOR. Nobody can get in, so the scaffold is never built, so the
middle of the roof is never finished.
Scaffolding behaved correctly throughout — it detected the need, cleared
its gate and produced a valid plan. It planned a structure in a room
nobody can enter.
Worth noticing: this was invisible while the walls stalled at 27/30,
because an unfinished wall IS a doorway. Fixing the walls is what sealed
the room. Every fix tonight exposed the next problem one layer up —
walls, then descent, then entry.
Next step specified in the story: give the demo's wall drafting a
deliberate one-cell doorway at ground level and re-run. That is a demo
change, not a system change, and it is the smallest thing between this
tool and a finished roofed room.

Task: Sprint 12 — building-034 roof stall located

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ncept

The specified next step was to give the demo a doorway. Done through the
real RemovalTool, and measured:

  doorway at (992, 6, 1002): erased
  room walls: WAIT CAP (220s) hit with 24/29 cells BUILT
  roof construction result: 9 / 12

Against 30/30 with no doorway. The doorway made the walls WORSE, so the
change was reverted rather than kept and tuned until the numbers looked
better.
The reading that fits: with an opening the villager walks INSIDE and
builds from within, and as the remaining cells would close it in, seal
prevention correctly refuses them. A builder must not trap itself — and
it has no rule saying 'leave, then finish from outside', so those cells
never get built at all. Without a doorway it worked only because the
villager was never inside.
SO THE FINDING IS NOT THAT THE DEMO LACKS A DOOR. It is that the
building system has no DOOR CONCEPT. An improvised gap is not a door, it
is a hole that disorders the build. A real door would be a component
that room recognition treats as a legal opening rather than a breach,
that the build plan knows to place LAST and from OUTSIDE, and that seal
prevention can reason about — today it can only see that a write would
trap someone, never that a doorway means it would not.
Until then a finished room is either sealed (no entry, interior roof
unbuildable) or open (builder trapped inside a shell it refuses to
close). Both are reachable today and neither is a house.
Nothing here retracts scaffolding: its measured contribution on the
sealed geometry stands at 27/30 -> 30/30. Recommended next story is
building-system: doors, sized as a real story, because it blocks the
roof, therefore the bed, therefore scene-009, therefore milestone
criterion Donchitos#5 in the product.

Task: Sprint 12 — door concept identified as the blocker

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous commit said the doorway run failed because the villager
walks inside and seal prevention then refuses the closing cells, and
called that 'the reading that fits'. It does not fit. The position data
contradicts it:

  villager after construction wait: current_cell=(992, 9, 1004)

y=9 with walls at 6..8 is ON TOP of the wall, not inside the room. The
villager was never trapped in a closing shell. That was a guess wearing
the clothes of an analysis, and since it is in the permanent record it
gets a correction rather than a quiet edit.
What remains measured: 30/30 walls with the room sealed and the roof
stuck at 10/12 because the interior roof cells need scaffolding inside
and nothing can enter; 24/29 with the improvised doorway; 27/30 without
scaffolding erection at all. What is NOT established is WHY the doorway
made the walls worse — candidates exist (project re-partitioning,
changed job order, lost scaffold support, different seal-prevention
evaluation around an opening) and none is tested. Tonight's score for
hypotheses formed by reading is 0 for 4.
The door-concept conclusion still stands, but on the sealed-room
measurement, which is direct and does not depend on explaining the gap
regression. If anything, a hole that makes things worse for reasons
nobody can name is a further argument that a hole is not a door.
Recorded as a precondition: bisect the doorway regression BEFORE writing
the door story, or it builds on sand.

Task: Sprint 12 — correction to the doorway analysis

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the erase does not

The correction set a precondition: bisect this before writing a door
story. Done, in one run.
Same project mutation, different geometry — erase the TOP cell of a wall
column instead of the GROUND cell. One drafted cell removed either way;
only one creates an opening a villager can walk through.

  BISECT erase TOP cell (992, 8, 1002): erased
  room walls: all 29 cells reached BUILT after 15.6s

Against 24/29 when the erased cell was at ground level.
So the erase itself costs nothing: removing a cell from a released
project does not re-partition it, does not disorder the jobs, does not
starve the scaffold planner. 29/29 in 15.6s is the same speed as the
untouched 30/30 run. The cost is the ground-level OPENING — the fact
that a villager can now get inside.
That puts the original 'builds from within, then correctly refuses to
seal itself in' reading back on the table, this time on measured ground.
What refuted it earlier was the villager's position at the END of the
wait; that says nothing about where it was DURING the build, and I
over-read it. The mechanism inside that window is still unmeasured, and
the next step is one print: which cells stay unbuilt, and where the
villager is each time the count stalls.
What this already settles for the door story: a hole is not a door, and
now there is a number attached. An opening a builder can enter is not a
neutral gap — it changes how the wall gets built, measurably and for the
worse. A door component cannot be 'a cell we skip'; it has to be
something the build plan and the seal-prevention rule both understand.

Task: Sprint 12 — doorway regression bisected

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r inside

One print, one run, and it kills two of my own readings at once.

  STALLDIAG room walls villager=(993, 9, 1004) state=5 pending=5
    [(994,6,1004) (994,7,1004) (994,8,1004) (992,7,1002) (992,8,1002)]
  STALLDIAG room walls villager=(992, 9, 1004) state=3 pending=5

The interior is x=993, z=1002..1003 at y=6..8. The villager sits at y=9
throughout — on the wall crown, not inside — wandering, then sleeping,
not building. The pending set never changes.
So "walks in and correctly refuses to seal itself in" is dead, and so is
"the erase disorders the project". What is happening is the same defect
as the roof: a builder that climbs onto something it built cannot get
down.
The pending cells corroborate it. Two of them sit directly ABOVE the
erased door cell — a floating column with its support gone, the hardest
possible reach. The other three are an untouched corner column on the
far side, unbuilt only because the single builder is marooned on a wall
several cells away.
THIS REFRAMES THE DOOR CONCLUSION. "The building system needs doors" was
the right observation attached to the wrong cause. The recurring defect,
now seen in three guises tonight, is that a villager who climbs onto its
own work cannot reliably get off it: walls layer 3 (solved by
scaffolding, 27/30 -> 30/30), roof interior (no scaffolding is ever
built there), and the wall crown with a doorway present (builder strands
and five cells go unbuilt).
SC-INV-2 defers dismantling while someone is up there, which is
necessary but not sufficient — it protects a descent that EXISTS. It
does nothing where no scaffolding was ever built. The ADR-0009 climb
mutations firing 40 and 9 times a run are the same story from the other
side: that is how villagers get UP, with no counterpart for getting DOWN
that does not depend on scaffolding happening to be there.
Next story should therefore be a DESCENT GUARANTEE, not doors: assert on
a real booted game that after any construction job completes,
find_path(villager, settlement ground) is non-empty. That fails today in
three distinct geometries, all reproduced above. Doors remain a real and
separate need — a sealed room still cannot be entered — but a door story
written now would inherit an unsolved descent problem and look like it
failed.

Task: Sprint 12 — stall window measured, descent identified as the root

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The root cause behind three separate stalls, written up from measurement
rather than from reading. villager-ai-024 gave builders a way UP — the
discrete climb, plus scaffolding as the real mechanism. Nothing
guarantees the reverse, so a builder ends up marooned on a wall or a
roof, wandering and then sleeping there while the rest goes unbuilt.
Three geometries, all from the real hosted demo:
 1. Wall layer 3 — SOLVED, and it proves the mechanism: 27/30 without
    scaffolding erection, 30/30 with it. Scaffolding supplies ascent and
    descent together, so nothing strands.
 2. Roof interior — the two cells are detected, clear the persistence
    gate and PLAN successfully, yet zero scaffold cells ever stand,
    because the support would sit inside a room the finished walls
    sealed. Roof stops at 10/12.
 3. Wall crown with a ground-level opening — villager at y=9 throughout,
    WANDERING then SLEEPING, pending frozen at five cells that never
    move.
The story records the five hypotheses already refuted tonight so nobody
re-treads them, and carries the working rule that produced every real
answer: measure in the stall window, because end-of-run positions say
nothing about what happened during it — reading them as if they did
produced two of the five wrong answers.
Anti-vacuity is two assertions in two different geometries so neither
can mask the other: crown descent (trips within seconds today) and roof
descent (empty path at y=10 today).
Doors are explicitly OUT of scope and stay a separate need — a sealed
room still cannot be entered — because a door story written on top of an
unsolved descent would inherit this failure and look like its own.

Task: Sprint 12 — villager-ai-025 authored

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An implementation pass on villager-ai-025 produced a test that
reproduces the defect and fails for exactly the right reason: the
villager never regains a path to settlement ground after stranding on
its own finished column. Parked as evidence rather than landed, because
it fails and this project does not land failing tests.
It also is not the story's lever. It builds a synthetic TestEnv, and
AC1 says the guarantee must be asserted on a REAL booted game. A proof
at the wrong level of abstraction is the half-proof this whole line of
work exists to avoid — five hypotheses fell tonight because something
plausible was checked instead of the real thing.
Its own TICKDIAG output says the same from another angle: climb=0
marooned=0 throughout, so in that fixture the ADR-0009 mutations never
fire at all, while the real demo fires them 40 and 9 times. The
geometry does not match, which is precisely why a fixture cannot stand
in for the boot here.
The pass also left a diagnostic print inside
villager_seal_prevention_gate.gd; reverted, tree verified parseable and
clean in my own areas.
It lands as a live test once story-025's real-boot lever exists and
passes.

Task: Sprint 12 — villager-ai-025 reproduction parked

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… success retracted

Story: villager-ai-025, third attempt. Lands the plumbing and says so;
the payload is NOT proven and is parked.

THE REAL DEFECT THIS UNCOVERED, in already-committed work: the
constructor-time clock connection silently did nothing. Valley builds
both scaffold coordinators during _wire_build_project_lifecycle, and at
that moment ConstructionTickLoop has not run its own setup, so its
time_tick_system is still null and the connect was skipped. Consequence,
precise and invisible: SC-INV-2's deferred dismantle retry could NEVER
fire in the shipped game — a dismantle deferred because a builder was
still on the structure would simply never be retried.
No test covered it. scaffold_dismantle_live_test asserts the deferral
DECISION and the construction/cancel signals, never the tick that
retries. It surfaced only from a diagnostic printing
dismantle_connected=false on a real boot. Both coordinators now take a
deferred connect_tick(), called from world genesis after the tick loop
has resolved its clock, and scaffold_tick_wiring_test pins it by
connection identity.
A FALSE SUCCESS IS RETRACTED. An earlier run appeared to show more
scaffolding and I described it that way. It was invalid — it predated
the connect_tick fix, the trigger was inert, and its numbers were
byte-identical to the baseline. The extra scaffolding in that frame came
from the doorway geometry. There is still NO valid post-fix measurement
of either lever; both pre-fix numbers stand, recorded in the evidence
file.
WHAT IS PROVEN of the descent trigger: it receives ticks, and a single
marooned observation never erects anything — the persistence discipline
that stops this repeating the queue flood which starved the walls. WHAT
IS NOT: a persistently marooned villager does not actually get a descent
scaffold erected. That assertion is parked with its verbatim failure.
Its body was lost while splitting the file and is recorded as lost
rather than reconstructed from memory.
Shipping the trigger as "wired, therefore working" would add another
hosted-but-inert instance while fixing one, which would be absurd. It
ships as plumbing.
Suite 1670/1670 green, 0 orphans.

Task: Sprint 12 — villager-ai-025 plumbing, payload parked

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd one regression

First run in which the descent trigger was actually connected.

LEVER 2, ROOF DESCENT — PASSES:
  before: villager=(994, 10, 1003)  find_path(...).size()=0   empty=true
  after:  villager=(1008, 4, 1000)  find_path(...).size()=11  empty=false
The villager is off the roof and on the ground with an eleven-step path
home. That is the guarantee the story exists for.

LEVER 1, CROWN — improved, not passing:
  before: villager=(993, 9, 1004) state=5 pending=5  final 24/29
  after:  villager=(998, 6, 1010) state=3 pending=2  final 27/29
No longer marooned on the crown; y=6 is ground. The two cells left are
exactly the pair above the erased door cell, floating with no support —
a different problem from stranding.

The mechanism is visibly working: 21 scaffold cells standing where there
were 0, and the bed completed 2/2 for the first time ever.

CAVEAT, stated before the conclusions rather than after: two other Godot
processes were already running when this started. Every wall-clock-capped
stage was competing for CPU. Position results are unaffected; completion
counts are not trustworthy until repeated on a quiet machine.

AND A REGRESSION THAT NEEDS THAT RE-RUN: roof fell from 10/12 to 0/12.
Either scaffolding work is eating the tick budget the roof used to get,
or the run was starved by the foreign processes. Both plausible, neither
measured — and tonight's record for concluding from plausible-looking
evidence is 0 for 5, so it stays unconcluded.

Telemetry unchanged at 40/11: the climb mutations still carry the
ascent, and the TD's retirement criterion remains far off. This story
never claimed to reach it.

Task: Sprint 12 — villager-ai-025 first valid measurement

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…irst time ever

The payoff loop's last step ran for the first time. The demo produces
these frames only when the stages genuinely happen, so their existence
is the result:

  stage 6: villager id=0 claimed bed cell (993, 6, 1002), owned by
           BuildProject id=1
  stage 6: worker_ids=[0] — built by claiming villager id=0: true
  stage 7: villager id=0 is SLEEPING at (993, 6, 1002)

It claimed a bed it built itself, and the attribution is checked rather
than assumed.

MILESTONE CRITERION Donchitos#5 IS STILL NOT MET, and that must not be blurred:

  shelter verdict for (993, 6, 1002) = false
  credited recovery = 0.3500 -> matches UNSHELTERED (x0.70);
                               sheltered would be 0.5000

It sleeps in its own bed, in the open. The roof reached 0/12 this run, so
the room does not classify as sheltered and the villager is credited the
worse rate. Criterion Donchitos#5 requires it to sleep BETTER in a room you built.
The first half now holds; the second does not.
The chain that works today: builds wall -> builds bed -> claims it ->
sleeps in it. What is missing is the roof — and the roof count is exactly
the number this run cannot be trusted on, because two foreign Godot
processes were competing for CPU throughout.

Task: Sprint 12 — payoff loop stages 6 and 7 reached

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous note said the roof count could not be trusted because two
foreign Godot processes were competing for CPU. Repeated with none
running, and the outcome is identical:

  construction result: 27 / 29 wall cells
  roof construction result: 0 / 12
  scaffold cells standing at roof stage: 19
  bed construction result: 2 / 2
  stage 7: villager id=0 is SLEEPING at (993, 6, 1002), recovery 0.3500

So the regression is mine, not the machine's. Roof was 10/12 before this
change and is 0/12 after.
THE TRADE, stated plainly. Gained: descent works (off the roof with an
eleven-step path home, off the crown at y=6 instead of y=9), three more
wall cells, the first completed bed ever, and the payoff loop's stages 6
and 7 reached for the first time — the villager claims a bed it built
itself and sleeps in it. Lost: the roof, entirely.
Most likely cause, held as a HYPOTHESIS and not a finding: nineteen to
twenty-one scaffold cells are real construction jobs and there is
exactly one villager, so the attention that used to reach the roof now
goes into building the descent scaffolding. If that is right the fix is
not to weaken the trigger but to stop erecting scaffolding the villager
does not need, or to make a descent scaffold cheaper than a wall.
Written down to be tested, not acted on — tonight's record for
plausible-reading hypotheses is 0 for 5. Cheapest test named in the
note: how many of the 19 cells are descent scaffolds versus job
scaffolds, and how many ticks each consumed.

Task: Sprint 12 — roof regression confirmed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants