Files
settled-reach/docs/workshops/body-map-viewer/tyre-round2.md
T
jpmschweitzerandClaude Fable 5 29c22cb728 docs(meta): body-map-viewer workshop — rounds, measurements, outcomes, as-built briefing
The complete workshop record: four round-1 positions, five round-2 syntheses
(incl. Troblum's adversarial pass with addendum + final scorecard — all seven
findings resolved), both lead interviews, Qatux's round notes and the 8-section
workshop-outcomes.md (the lakes message-crossing documented as process
history), measurement ⑥ (set_pixel/c1) + the population-survey and chunk/S2
addenda in the measurement docs, the brief's appendix updated through ⑥, and
architecture-briefing-final.md — Jeroen's outline written back as-built
(six-level ladder, lakes, ~9MB resident global tier). README row: Complete.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 10:57:45 +02:00

1234 lines
90 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Body Map Viewer — Tyre's Round 2 Synthesis"
description: "Final amendment texts (record-ready), tagged-envelope governance disposition, step-ladder tables, and the deprecation-sweep draft (Expected Output 2)"
type: workshop
status: active
workshop: body-map-viewer
created: 2026-07-25
---
# Tyre — Round 2 Synthesis
*cracks knuckles* — interview 1 upheld both my load-bearing round-1 calls
(gridunit snaps to D-243; cliffs Phase-4) and added two rulings I now fold in:
the **map-time TTL-split with the storage/staleness axes distinction** (Jeroen's
new eviction amendment), and **cliffs as a sparse `CliffSegment` feature list**
(aligning my round-1 "new fields, not stolen bits" text with Araminta's sparse
shape and Jeroen's ruling). This document is the filing package: paste §(a) into
`governance/decisions/architecture.md` with only ID-claiming left, run the §(d)
sweep at wrap-up.
Four sections, in the coordinator's order: (a) final amendment texts, (b)
envelope mechanics + the ceiling-rule disposition (with Dudley on wire/serving),
(c) step-ladder tables (with Dudley on costs), (d) deprecation-sweep draft.
---
## (a) AMENDMENT TEXTS — FINAL, record-ready
These incorporate every interview-1 ruling. Each is written to paste directly
into the named record. **A new D-record** (the body-map-viewer render
architecture) is claimed separately at filing — these are the *amendments to
existing records*; the new-record text lives at the end of this section as
§(a.0) so the whole package is in one place.
### (a.0) NEW RECORD — body-map-viewer render architecture (claim a D-id at filing)
> **D-NNN — Body-map-viewer stepped render architecture (supersedes the
> T-1143 continuous-ladder mechanism).** *Claimed [date]. Domain:
> architecture.*
>
> - **Decision:** The Atlas map handler is rebuilt on three locked premises:
> **(1) content determination is server-side** — the CPU/Rust server answers
> "what is at this world coordinate at this zoom step" as a per-step data
> canvas; the client never invents geometry. **(2) The client is a map-art
> function** — it colorizes, styles, and annotates the server's canvas via a
> render-to-texture terrain layer (texel-exact, drawn at the display ratio) +
> an unscaled screen-space sibling layer for vector annotations. **(3) Zoom is
> stepped** — discrete gridunit-spacing levels, one server-canvas fetch per
> crossed step boundary, cursor-anchored, with edge-scroll pan and a hard
> full-zoom-out reset to the canonical Global (rung-0) body-surface frame.
> - **The step ladder** is **six levels** — the **Global** map opener (rung 0)
> plus **five fixed metre rungs** (Dudley's Option D, every fixed rung
> measured): **Global (rung 0, body-surface view) → Region (204.8 km, rung 1)
> → District (2,048 m, rung 2) → Quarter (512 m, rung 3) → Block (128 m,
> rung 4) → Chunk (64 m, rung 5, deepest)**. **Rung 0 (Global) is the map
> opener and is *variable*-extent, not a fixed metre rung** (Jeroen, interview
> 2 correction): it is the whole body surface at **one gridunit per region**,
> so its canvas *is* the body's region grid — variable per body (~19K gridunits
> on an Earth-class body; the D-243 elastic seam made visible, since region
> count floats per `body_radius_km`). **Region (rung 1) is the largest
> *fixed*-size rung** — viewport-sized like every fixed rung below it, *not*
> the always-kept tier (that is Global). Tile/voxel (1 m)
> is **dropped from the Atlas ladder** (Jeroen, interview 2: *"the actual tile
> level rung seems unusable, maybe replace with 64"* — a 10-px-per-tile
> full-screen view is ~192×108 m of ground, which is in-world viewport content,
> Phase-5's scope, not an Atlas map); voxel is reserved for Phase-5 in-world
> rendering. Every fixed rung's **gridunit spacing is one of D-243's
> absolute-metre rungs** — never a viewport-derived spacing (see the D-243
> gridunit amendment, §(a.4)). **Deepest bottom-out rule: 1 screen px per 64 m gridunit, no
> magnification margin** — the old "10 px per tile" margin does *not* carry
> over (it existed because a 1 m unit is sub-readable at 1×1; a 64 m gridunit
> is already a legible map feature — a city block / short street — so chunk
> uses the plain fixed-canvas budget with no display-ratio-sized exception,
> §(c)).
> - **Canvas policy:** **rung 0 (Global) is the sole canonical, always-keep
> tier** — the body-surface region-grid canvas, one gridunit per region,
> variable-extent per body. D-226(d)-legal by construction: at one gridunit per
> region it is *coarser* than the region grid, never a sub-region
> metre-resolution whole-body derivation. **Every fixed rung — Region (rung 1)
> through Chunk (rung 5) — is viewport-sized and evictable** (Region included:
> it is the largest fixed rung, not the canonical tier), bounded to a fixed
> canvas-pixel budget (not a per-monitor echo), which keeps the deep
> ladder legal under D-226(d) **per request** by construction (a viewport-sized
> chunk-spacing canvas is never a whole-body metre-resolution derivation on any
> single request). **The D-226(d) prohibition is stated as a per-request /
> per-derivation constraint, not an aggregate-storage constraint** (§(a.8),
> Troblum S3) — the letter and the purpose are both held server-side; the
> client-cache *accumulation* path is closed by the deep-rung retention cap in
> the Cache bullet, not left to practical improbability.
> - **Wire:** the per-step canvas rides a **tagged-envelope** carrier (§(a.5),
> §(b)) — the ceiling-forcing payload D-225 deferred, now built. Dense
> classification fields ship PNG-per-field (measurement ④'s winner on size and
> speed); sparse feature lists (`courses`, `cliffs`) stay MessagePack-native.
> - **Cache:** three tiers, cheapest-first — client in-memory LRU → client
> disk-backed `FileAccess` store (self-cleaning TTL for sim-state entries) →
> server (D-203-shaped resident global tier + TTL(detail, time, distance) for
> finer rungs). Determinism (D-227) makes every geometry tier a pure cache,
> never a source of truth. **Staleness and storage are distinct eviction
> axes** (§(a.6)). **The server global (rung-0) tier is the body-surface
> region-grid canvas — one gridunit per region, ~19K gridunits on an
> Earth-class body — and costs ~8.85 MB PNG-encoded (27.61 MB raw) across the
> entire real ~267-body population** (Dudley, measured: 4,825,615 total cells,
> avg 18,073/body; largest GJ325Ac at 25,200 cells/47.4 KB, smallest a moon at
> 253 cells; Earth-class reference 195×97 = 18,915, matching the ~19K framing).
> This supersedes the earlier ~174 MB figure (which mis-priced a fixed 4K-class
> Region-*spacing* canvas per body, ~440× too many cells). At ~8.85 MB the
> keep-always tier is **trivially process-resident, not merely disk-safe** —
> the "always keep global" policy gets *easier*, not harder, under the
> rung-identity correction. Convention stated: **SI decimal MB**, so the figure
> isn't "corrected" against a `du`-reported MiB number later (Troblum N1).
> **Derive cost — the "snappy after first calc" number (Dudley, measured on
> real per-body extents, not extrapolated):** ~16–21 ms single-thread per body
> (~830–910 ns/cell over ~18K cells) — trivially interactive on a fresh
> Atlas-open. The tier **populates lazily, once per body, on first Atlas-open**
> via the D-206 background queue (the same pattern every other cached layer
> uses), so the ~4.0 s all-267-summed single-thread figure is a sanity ceiling
> only, never paid synchronously in one batch (same shape as T-1177's
> per-body-vs-summed hydrology framing). **Two client-cache hardening requirements, both
> stated in the record, both implemented in Stig's `FileAccess` spec:** (i) a
> **per-body deep-rung retention cap** (a maximum resident chunk/block-spacing
> tile count or disk quota per body, independent of the rung-0 retention floor)
> — this is the structural ceiling that closes the D-226(d) accumulation gap
> (§(a.8)); (ii)
> every persistent cache entry carries a **schema/version tag** (the game's
> `project.yaml` version string or a `generator_sha`-style stamp), checked at
> read time — a mismatch is treated as a cache miss and re-fetched, never
> decoded (§(a.9), Troblum S5).
> - **Rationale:** the T-1143 error class (client compensating for a
> zoom-scaled canvas — `_canvas.scale`, `_zs`/`_zs_stroke`/`_zs_ring_radius`,
> the line-rasterizer floor) is dissolved at the root: there is no
> zoom-scaled canvas anymore, so per-call compensation cannot occur. Every
> pre-workshop cost gate (①–⑤) came back GO with no extrapolation.
> - **Cross-reference:** D-166 (cascade + the amended zoom-ladder corollary),
> D-226 (the ceiling this migrates past; D-226(d) the deep-step boundary),
> D-227 (derive-don't-store — the cache's discipline), D-243 (the rung ladder
> gridunit snaps to), D-225 (the tagged-envelope migration this executes),
> D-010 (determinism — server-owns-derivation, client-art-function).
> - **Dissent:** None.
### (a.1) D-166 corollary — repoint (owns between-step magnification)
Paste as an amendment on D-166, after the 2026-07-21 amendment:
> **Amendment (2026-07-25, body-map-viewer workshop — stepped ladder
> supersedes continuous).** The 2026-07-21 corollary sentence — *"Display at
> every rung samples the derivation at canvas resolution (the ladder is a
> continuous field, not a stack of fixed display rasters)"* — is **repointed,
> not deleted.** The zoom mechanism is now **stepped**: the server generates
> one data canvas per discrete zoom step, sampled at that step's native D-243
> gridunit spacing. The corollary's *guarantee* survives, re-expressed
> per-step: **a gridunit is never derived coarser than its step's own rung
> floor, and never displayed finer than the display-ratio tunable** (1×1 ideal,
> ≥5×5 px/gridunit acceptable — measurement ④). The "continuous field" framing
> is retired as literally false (it never was continuous once server-side
> per-step canvases replaced the client `_canvas.scale` model). The corollary
> now reads: *"each zoom step's data canvas is a derivation sampled at that
> step's native gridunit spacing; display within a step holds that canvas at a
> fixed, texel-exact ratio; between-step magnification of the held canvas is
> bounded to one step interval and is the ladder's only sanctioned display-time
> scaling."* **This amendment owns the one honest new gap it creates:** in the
> interval just before a step-cross, the held (coarser or adjacent) canvas is
> magnified to fill the new step's viewport for the fetch duration — the exact
> operation the original corollary was written against, now a **named, bounded
> exception** rather than a silent violation. The bound is joint in two knobs:
> step count (more steps → smaller per-step magnification factor) and the
> display ratio. The fetch interval is short — measurement ⑤ shows step-canvas
> texture upload at 0.03–4.6 ms across every size, never a frame-budget risk,
> and measurement ④ shows an uncached 330K-gridunit step derives + encodes
> server-side in ~80 ms — so the magnified hold is a double-digit-millisecond
> transient, not a resting display state. **Ladder extent (interview 2,
> §(a.8b)):** the 2026-07-21 amendment's *"down to tile scale"* phrasing is
> narrowed — the Atlas ladder bottoms out at **chunk (64 m)**, not tile (1 m);
> tile/voxel is Phase-5 in-world content, not an Atlas rung. The per-step
> derivation guarantee above applies to every rung from region down to chunk.
### (a.2) T-1143 ruling 3 — superseded for zoom transport
Paste as an amendment on the D-226 record's T-1143 ruling block (or on D-166,
wherever the T-1143 rulings are anchored — they live in the D-226 amendment
chain):
> **Superseded 2026-07-25 (body-map-viewer workshop).** T-1143 ruling 3
> ("continuous cursor-anchored zoom... wheel-zoom carries the view from the
> orbital frame down through regional granularities continuously") is
> superseded for the **transport mechanism**: zoom is **stepped** — scroll
> clicks through discrete D-243-rung gridunit-spacing levels, one server-canvas
> fetch per crossed step boundary. **Survives unchanged:** (a) cursor-anchored
> centering, (b) edge-scroll pan, (c) the [HARD] condition that a full
> zoom-out resets to the canonical Global (rung-0) body-surface frame and
> location. A
> client-side morph/tween between held step canvases is an **investigation
> item** (cosmetic interpolation of already-arrived textures while the next
> step's canvas is in flight — never re-derivation, never a second positional
> truth; see the determinism-boundary ruling, §(a.7)). **T-1143 ruling 2**
> ("progressive capped-density tiling riding the generalized `district_window`
> carrier — no forced tagged-envelope migration") is **also superseded**: the
> tagged-envelope migration is now triggered (§(a.5)) — measurement ④ shows the
> step-canvas payload at 21×–563× the windowed ceiling across the three
> measured sizes, off by two to three orders of magnitude before encoding is
> even considered, which no additive-param reuse of `district_window` can
> carry.
### (a.3) `select_rung` / rung-model — replaced by the step index
Paste as an amendment note (on the same T-1143-rulings block):
> **Superseded 2026-07-25.** The shipped rung selector — `select_rung()`'s
> unified per-rung coverage-ceiling walk keyed on `MAX_COVERAGE_M` (Quarter →
> District → Region → tile-mode mosaic; `atlas-zoom-ladder-t1143.md` §6 "Final
> model, round 3") — is **replaced by a discrete step index** into the stepped
> gridunit ladder. Rungs-as-derivation-granularity survive conceptually (every
> step still names an absolute-metre D-243 rung), but the *selector* is no
> longer a coverage walk over viewport extent — it is "which step the viewport
> is currently on," advanced/retreated one notch per scroll. `select_rung`,
> `MAX_COVERAGE_M`, the coverage-ceiling walk, and the `compute_tile_grid()`
> whole-body Region-tile mosaic that composed the beyond-Region extent are
> retired. The whitelist-validation *discipline* from `resolve_window_granularity`
> (T-1150: finer-than-district integer multiples only, unknown values fall back,
> never trusted from the wire) is the correct shape for the **step index's own
> validation** and should be reused, not reinvented.
### (a.4) D-243 — gridunit vocabulary entry (additive)
Paste into D-243's item (5) vocabulary block, or as a labelled amendment:
> **Amendment (2026-07-25, body-map-viewer workshop) — `gridunit` added to the
> locked vocabulary (additive; no rung changes).** **gridunit (at zoom step):**
> the per-step data-canvas cell. **Not a new spatial rung** — a *role* name for
> whichever D-243 absolute-metre rung the Atlas ladder pins a given zoom step
> to. **At every *fixed* rung, gridunit spacing equals one of the ladder's fixed
> metre values and never floats with viewport size or display resolution** — a
> fixed step's gridunit is a D-243 metre rung, full stop. **The Atlas ladder's
> fixed rungs run from Region (~205 km, rung 1) down to chunk (64 m, rung 5, the
> deepest Atlas rung); tile/voxel (1 m) is NOT an Atlas gridunit** — it is
> dropped from the ladder (§(a.8b), Jeroen interview 2) and reserved for Phase-5
> in-world rendering. **The one exception to fixed-metre snapping is rung 0
> (Global)**: it is the *variable* map-opener, one gridunit per region, so its
> gridunit is a whole region (~205 km) but its *canvas extent* floats with the
> body's region count (the D-243 elastic seam). Global's gridunit is still not
> viewport-derived — it is the region grid, a fixed property of the body — so the
> "never floats with viewport/display" discipline holds; only the body's own
> region count varies. So
> the outline's *"10 px per tile"* bottom-out is **superseded** by **"1 screen
> px per 64 m gridunit, no magnification margin"** (Dudley's chunk bench): the
> Atlas bottoms out at the chunk rung at the workshop's own 1×1 ideal ratio,
> with *no* extra magnification, because a 64 m gridunit is already a legible
> map feature (city block / short street) — unlike a 1 m unit, which needed the
> 10× margin only to be readable at all (which is exactly the in-world-viewport
> problem chunk is defined to sit above). The **display ratio** (screen-px per
> gridunit — 1×1 ideal, ≥5×5 acceptable) is the free,
> client-side, viewport-dependent parameter, kept terminologically and
> architecturally separate from gridunit spacing: two monitors at different
> resolutions requesting the same step receive the **same** absolute-metre
> canvas (same rung, same derived content, same cache entry — shareable) and
> merely display it at different px-per-gridunit ratios (a GPU
> texture-to-viewport resize, never a re-derivation). This is what keeps the
> derivation cache key free of any presentation parameter (D-227: "a canvas for
> a fixed seed never changes" stays true). **The display ratio is
> step-dependent, not flat across the ladder** (Stig, measurement ⑥ —
> `Image.set_pixel` 77.5 ns/cell flat, so coloring cost is driven by cell count,
> never display density): **1×1 at the deepest steps** (Block/chunk — full
> fidelity where the player is closest to visible detail; chunk-64 m is now the
> deepest Atlas rung, §(a.8b)), **1×1 preferred at the mid steps**
> (Quarter/District) while the realistic canvas stays under ~2M cells, and
> **~5×5 reserved for the shallow rungs** (Region/rung 1 — and the Global/rung-0
> opener, whose region-grid canvas is tiny in cell count regardless of ratio)
> where the metre *extent* grows large. There is no measured reason to sacrifice
> fidelity where the player is nearest the detail; the ~5×5 fallback is a
> cost-relief valve for the coarse steps only. This changes *nothing* about the
> snap rule or the cache key — the ratio still governs only presentation (canvas
> *extent* sizing + on-screen scale), and per-step ratio selection is a
> client-side policy, never a spacing or derivation input.
### (a.5) D-226 T-1124 §2 windowed-family ceiling — tagged-envelope migration triggered
Paste as an amendment on D-226 (extends the amendment chain that already holds
the T-1156/T-1170 carrier notes):
> **Amended 2026-07-25 (body-map-viewer workshop — tagged-envelope migration
> triggered, superseding T-1143 ruling 2's "no forced migration").** The
> windowed-family ceiling's **purpose survives untouched**: prevent
> uncorrelated concurrent windowed *queries* needing per-field request
> correlation. Its **current mechanism** — "exactly one windowed-query field on
> `AtlasLayerResponse` (`district_window`), sized for a ≤4,096-cell served
> window" — **cannot carry a step canvas**: measurement ④ (T-1179) shows the
> best available encoding (PNG-per-field) at 21×–563× the ~30 KB reference
> across the three measured canvas sizes (330K/2.07M/8.3M gridunits), with no
> encoding in the measured set closing that gap (it is a cell-count gap of two
> to three orders of magnitude, not a codec gap). The **tagged-envelope
> migration D-225 deferred** (2026-06-12 constraint: *"the next inbound message
> type must introduce a tagged envelope"*) is hereby the sanctioned path and is
> executed: the step-canvas payload becomes its own **tagged message type on
> the existing IPC stream** (not a second socket, not a `district_window`
> growth) — the exact shape §(b) fixes. **The ceiling rule is re-scoped, not
> lifted:** "exactly one windowed-query field" now governs the **legacy
> `district_window` carrier only**; the tagged step-canvas envelope is a
> **separate carrier the ceiling rule does not apply to by construction** — it
> is not a second windowed field on the old shape, it is the new shape. The
> ceiling's original job (no uncorrelated concurrent windowed queries) is
> preserved *within* the envelope by the same echo-key/staleness discipline
> `district_window` uses today (`(body, step, center, granularity)` echoed on
> every response, latest-wins, stale-discardable). Framed as **expected scope,
> executing a planned deferral** — tier: challenging but doable, not a risk.
> **Concrete shape (Dudley, §(b)):** inbound is a discriminator-field
> `StepCanvasRequest { step_canvas: bool, body_id, step_index, center, extent,
> min_wl_m }` — a sixth `Inbound` variant extending the existing
> `star_map`/`city_names`/`browse` `ShapeProbe` pattern (the required-marker
> form D-225's 2026-06-12 amendment named); outbound is a **dedicated
> `StepCanvasResponse` message** (not a field on `AtlasLayerResponse`) +
> `SimBridge::send_step_canvas_response`. The legacy `district_window` carrier
> survives **byte-unchanged** — the envelope carries only new step-canvas
> traffic; `district_window` goes cold when its only client (the retired
> `AtlasWindowViewer`) is deleted, then is removed in a follow-up cleanup
> ticket (D-005/D-192 co-ship — no old-client/new-server window). **The ceiling
> rule is therefore RE-SCOPED, not retired:** it keeps governing the live legacy
> carrier verbatim.
### (a.6) Map-time axis + storage/staleness eviction split (Jeroen's interview-1 amendment)
New — records interview-1 ruling 2 verbatim-grounded. Paste as an amendment on
D-227 (the derive-don't-store record whose cache discipline this instantiates)
and cross-reference from the new render-architecture record:
> **Amendment (2026-07-25, body-map-viewer workshop — map time axis; and
> staleness vs storage as distinct cache axes).** The Atlas map shows **current
> state via a TTL-split**: **static geometry** (morphology/elevation/moisture/
> vegetation/glaciation/height — everything derive-don't-store covers) is
> **cached indefinitely-fresh** (determinism: re-derivation is byte-identical,
> so "stale" is not a concept that applies to geometry); **sim-state planes**
> (frozen/flooded, and any future climate-sim-driven field) are carried as
> **separately-cached short-TTL planes**, re-requested as sim time advances.
> **Two distinct eviction axes (Jeroen, interview 1, verbatim: *"we still may
> also want to evict non global level geometry based on time to save storage
> for planets the player visits but never goes back to"*):** **(1)
> staleness-eviction** applies only to sim-state planes (they genuinely go
> stale as sim time moves); geometry never goes stale. **(2)
> storage-eviction** applies to *all* sub-global cache entries including
> geometry — **every fixed rung, Region (rung 1) through Chunk (rung 5),
> included** — evicted on time-since-last-visit purely as a **storage-budget
> policy**, not because the data is wrong (a re-derive on next visit is
> byte-identical and cheap — measurement ②/③, ~190–220 ns/cell parallel flat at
> every rung). **The global tier — rung 0 (Global, the body-surface region-grid
> canvas), NOT Region — alone is keep-always** — exempt from both axes, the
> resident tier that makes "atlas navigation snappy after first calc" true.
> (Region is rung 1, the largest *fixed* rung; it is viewport-sized and
> storage-evictable like every rung below it — the always-kept tier is the
> variable Global opener above it.) This makes Dudley's `TTL(detail, time, distance)`
> formula precise: the `time` term is *storage-eviction* for geometry (evict a
> long-unvisited body's fine-rung canvases to reclaim disk) and *staleness*
> for sim-state planes (re-fetch a flooded/frozen plane whose sim-time TTL
> expired) — two different reasons an entry leaves the cache, never conflated.
### (a.7) river-courses-t1170.md carrier rule + cliff sparse-list — terminology repoint + cliff addition
Paste as a note at the top of `docs/architecture/river-courses-t1170.md` and as
a one-line D-227 cross-reference update:
> **Note (2026-07-25, body-map-viewer workshop).** The carrier three-way rule
> (Ruling 1c) survives unchanged in substance; "the windowed payload" is
> repointed terminologically to "the per-step data-canvas payload — formerly
> the `district_window` field, now the tagged step-canvas envelope (D-226
> 2026-07-25 amendment, §(a.5))." Rule (iii) — *rung-indexed invented detail
> rides the windowed payload regardless of geometric kind* — now has a **second
> vector member alongside `courses`: `cliffs: Vec<CliffSegment>`.** Carved-gorge
> geometry (dominant `elevation` unchanged + per-segment `channel_depth` +
> `cliff_edge`, T-1177's direct solver-output carry) is **rung-indexed invented
> detail** exactly as course geometry is (hydrology settles once per body, but
> the *carved representation drawn at a step* is a windowed-rung concern the
> same way course geometry is), so it rides the step-canvas envelope as a
> **sparse feature list — zero-length when nothing is carved** (measurement ①:
> gorge carving is structurally rare, zero carved cells across all three
> production-scale benches), `#[serde(default)]`, parallel to `courses`. It is
> **not** a dense per-gridunit array and **not** a stolen bit of an existing
> classification byte. **Scope: Phase-4 Atlas** (Jeroen, interview 1, ruling 3
> — a map showing a smooth shoreline where the settled solver computed a carved
> channel would misrepresent the "settled hydrology" the workshop premised;
> including it is nearly free given the direct-carry representation). **The
> sparse-list encoding's "near-zero occupancy" cost is SURVEY-CONFIRMED
> population-wide (Troblum B1, resolved strong):** the pre-filing population
> survey ran all **267 committed real heightmaps** independently (each body's
> own PNG-embedded sea level, real 512×256 working grid, ~0.86 s total,
> byte-exact determinism across two runs) and found **zero carved-outlet
> basins, zero `cliff_edge` cells, 0/267 bodies with any carving** — across
> genuinely varied terrain (31–187 basins/body), not a flat population. The
> `cliffs` field ships **empty on every currently-committed body**; the
> "near-free" claim is now population-backed, not one-body-backed. **Framing
> held deliberately at "not observed yet ≠ cannot occur":** the carving
> arithmetic (`channel_depth = original − spill_level`, the `cliff_edge` flag)
> stays unit-proven in isolation for any future body whose terrain does produce
> the narrow two-independently-sealed-basins-plus-single-cell-corridor geometry
> carving requires — the field-set and Phase-4 scope stand because the model
> must represent what the solver *can* produce, and the sparse-list is the
> right shape at zero occupancy (empty vec, `#[serde(default)]`) and remains a
> correct shape at any future nonzero occupancy. **Survey scope limit to carry
> into the filed record:** the survey used uniform `moisture_q = 55` (per-body
> climate wiring is out of scope) — this affects only the endorheic/overflow
> *split*, never carving (carving is elevation-gated, never moisture-gated), so
> the zero-carving finding is independent of the climate simplification. Full
> addendum: `measurements/t1177-hydrology.md` "POPULATION SURVEY" section.
### (a.8) D-226(d) whole-body prohibition — per-request framing + client-cache accumulation cap (Troblum S3)
Paste as an amendment on D-226 (the item-(d) prohibition), alongside the ceiling
amendment §(a.5):
> **Amended 2026-07-25 (body-map-viewer workshop — accumulation gap closed,
> Troblum adversarial pass S3).** The item-(d) whole-body prohibition ("chunk/
> tile/voxel output never appears as a whole-body planetary map layer") is
> **stated explicitly as a per-request / per-derivation constraint, not an
> aggregate-storage constraint.** The stepped ladder's viewport-sizing policy
> holds it **by construction server-side**: no single `StepCanvasRequest` ever
> derives whole-body coverage at sub-Region spacing (the deepest step is a
> chunk-64 m viewport canvas — see §(a.8b) for the tile/voxel-drop narrowing).
> But the client-side persistent cache (Stig's `FileAccess` store) introduces an
> *accumulation* path the letter of the rule does not cover: a systematic
> exhaustive pan at the deepest (chunk) spacing — most plausibly the **D-226
> item-(4) `AtlasAgentInterface` automated QA sweep**, not normal play — could
> assemble a near-whole-body chunk-resolution artifact as N discrete cached
> files over wall-clock time (the tile-spacing case that produced the ~985 GB /
> 6.6M-tile figure is now moot at the Atlas ladder — tile is dropped, §(a.8b) —
> but the *accumulation principle* still applies at chunk/block spacing, just at
> a coarser resolution and a proportionally smaller aggregate; impossible by
> accident, but not *structurally* prevented). The rule's purpose is about
> information content, not file count, so packaging it as N files does not
> exempt it. **Closed structurally, not by improbability:** a **per-body
> deep-rung client-cache retention cap** (a maximum resident chunk/block-spacing
> tile count or disk quota per body, independent of the rung-0/Global retention
> floor) bounds sub-global accumulation so the aggregate can never approach whole-body
> metre-resolution coverage. The automated QA channel inherits the same cap —
> an agent sweep's client cache is bounded identically, so a QA run cannot
> silently deposit the forbidden artifact in `user://atlas_cache/`. The exact
> cap value is a tuning constant sized in Stig's cache spec at implementation;
> the *requirement that a cap exist* is record-level.
### (a.8b) D-226(d) floor — partial restore at chunk (Jeroen's interview-2 narrowing of his own interview-1 opening)
Paste as an amendment on D-226, immediately after the T-1143 ruling 1 block
(which opened item-(d)'s floor for the ladder) — this narrows that opening:
> **Amended 2026-07-25 (body-map-viewer workshop, interview 2 — the opened
> floor lands at chunk; tile/voxel re-close for the Atlas).** T-1143 ruling 1
> (2026-07-21) opened item-(d)'s floor "toward block/tile granularity" for the
> Atlas ladder, on the BHAG that *"we set a new BHAG so old restrictions are up
> for debate."* Interview 2 **scopes that opening — it does not reverse it.**
> Jeroen (verbatim): *"the actual tile level rung seems unusable. maybe replace
> with 64?"* Reasoning (matching the workshop's own seam logic): a 10-px-per-tile
> full-screen Atlas view is ~192×108 m of ground — that is **in-world viewport
> content (Phase-5 scope)**, not a map. The Atlas zoom ladder therefore bottoms
> out at **chunk (64 m)**, and **tile/voxel (1 m) output returns to
> never-Atlas-mapped** — exactly item-(d)'s original prohibition, now restored
> for the tile/voxel tier specifically. **Precise post-interview-2 state of the
> floor:** the opening is *used* down to chunk (64 m) — block (128 m) and chunk
> (64 m) are legal Atlas rungs, riding the viewport-sized windowed carve-out
> (§(a.8) per-request framing) — and *re-closed* below chunk: tile (1 m) and
> voxel/subvoxel output are Atlas-invisible, verified by the
> believability/derivation harnesses and shown **in-world in Phase 5**, exactly
> as item (d) originally required. This is a **deliberate narrowing of the
> BHAG's scope, not a walk-back of the BHAG** ("a Reach a character can travel
> through" is unchanged; what changed is that the *Atlas map* stops one rung
> above the in-world viewport, because that's where a map stops being a map).
> The harness-verification path for tile/voxel L5 fill remains primary, as it
> was before the opening — the opening's practical effect is now limited to the
> block and chunk rungs, which is the range where a whole-viewport canvas still
> reads as a *map* rather than a *scene*.
### (a.9) Client-cache schema/version tag — the one D-192 gap (Troblum S5)
Paste as an amendment on D-227 (the cache-discipline record) and cross-reference
from D-192:
> **Amendment (2026-07-25, body-map-viewer workshop — persistent-cache version
> discipline, Troblum adversarial pass S5).** D-192 drops the client/server
> protocol version handshake on the subprocess-co-ship rationale ("the Godot
> client launches the Rust server it was built with — always in sync at
> runtime"). That holds for the **live** wire, and for the tagged-envelope
> migration itself (no live network deployment where an old client meets a new
> server). It does **not** hold for a **disk-backed persistent cache** (Stig's
> `user://atlas_cache/` store, premise 9): a cache file written by game version
> N and read back by version N+1 **survives a game update by construction** —
> exactly the version boundary D-192 assumes away everywhere else. A patch that
> changes the step-canvas payload shape (adds a field — e.g. this workshop's own
> `cliffs` list — or shifts an enum discriminant range per T-1150's "unknown
> values fall back, never trusted from the wire") would have an N-schema cache
> decoded as N+1-schema: a hard decode error at best, a silent
> misinterpretation of old bytes as new fields at worst (the exact class D-225's
> 2026-06-12 tagged-envelope constraint prevents for the *live* wire, now
> extended to the *disk* format). **Requirement (record-level):** every
> persistent client-cache entry carries a **schema/version tag** at write time
> (the game's `project.yaml` version string, or a `generator_sha`-style stamp),
> and a **read-time check**: a tag mismatch is treated as a **cache miss —
> re-fetch, never decode.** This extends the existing cache-index shape
> (`{written_at, last_read_at, kind, size_bytes}` + `schema_version`) by one
> field; it lands in Stig's `FileAccess` spec at implementation, but the
> *requirement* is stated here so it is not discovered as a stale-cache decode
> bug in the wild. Consistent with D-227's "cache never truth, evict → recompute
> always valid" — a version-mismatched entry is just another eviction case.
### (a.10) Seed-chaining — the "cache-accelerated pure function" model (Jeroen interview-2: he meant consuming coarser output)
Paste as an amendment on D-227 (this is the one place derive-don't-store meets
"a finer step reads a coarser step's output") — **for Jeroen's final
ratification against his phrase "serves as seed information for the deeper
cascade."** Governance framing is mine; the cost/impl side is
**Dudley-confirmed** (`dudley-interview2-response.md` §2) — his A/B mechanism
distinction is folded in, and the `[DUDLEY]` bracket is resolved.
> **Amendment (2026-07-25, body-map-viewer workshop, interview 2 — seed-chaining
> resolved as a cache-accelerated pure function).** Jeroen's outline —
> *"[the step canvas] at the same time serves as seed information for the deeper
> cascade"* — is ruled at interview 2 to mean the finer step **consumes the
> coarser step's resolved output** (not merely that the coarser rung is
> independently re-derivable). This is reconciled with D-227 derive-don't-store
> **without weakening it**, via the **cache-accelerated pure function** model:
>
> - **What "consuming coarser output" means precisely — mechanism B, not
> mechanism A** (Dudley §2, load-bearing to prevent a subtle violation).
> **(A) Reading the coarser step's RESOLVED CLASSIFICATION** (e.g. taking a
> District-spacing `morphology_zone` as a shortcut for a Chunk cell's zone)
> is **ruled OUT** — it violates Araminta's categorical-field re-derivation
> rule (§(a.6)/round-1: coarser steps re-derive morphology/vegetation/
> glaciation as a fresh dominant-mode pick at their own spacing, never inherit
> a different spacing's classification — the same-vocabulary/different-meaning-
> per-scale contradiction). **(B) Reading the coarser step's underlying
> CONTINUOUS PRIMITIVE** (the region/district *baseline blend* at a world
> position) as an input to the finer step's OWN fresh classification is what
> "seed information for the deeper cascade" means — and it is **already the
> shipped pattern** (`invent_primitives`'s district call reads a region
> baseline today). The cache-accelerated model applies to (B): cache the
> continuous baseline blend, not the resolved classification.
> - **The DEFINITION stays pure.** Every gridunit value remains
> `derive(seed, position)` — a pure function of fixed inputs, byte-identical
> on every evaluation (D-010/D-227 unchanged). "Consuming the coarser output"
> does not make the finer step a function of *mutable state*; the coarser
> baseline it consumes is itself `derive(seed, position)` at the coarser rung —
> a pure value, not a stored fact.
> - **The IMPLEMENTATION may read a resident coarser canvas as an
> ACCELERATION,** with a **derive-fresh fallback** when that canvas isn't
> resident. Reading the cached coarser canvas is faster than recomputing it;
> not having it costs a fresh derive of exactly the shape Dudley already
> benched.
> - **This is an OPTIMIZATION, not a SEMANTIC DEPENDENCY — the load-bearing
> governance distinction.** The coarser canvas being read is *itself evictable
> derived data* (a pure function of the same seed). So correctness never
> depends on the cache being warm: if the coarser entry is evicted, the finer
> step derives it fresh and gets **byte-identical** input, producing the same
> output. That is precisely D-227's own standing test — *"eviction →
> recompute, always valid."* A semantic dependency would mean "the finer step
> is *wrong* without the coarser cache" — never true here, because the coarser
> value is re-derivable to the same bytes. The cache is still **never truth,
> only an accelerator**: derive-don't-store holds intact.
> - **Mandatory determinism test (the proof that makes this airtight):** the
> cache-hit path (read resident coarser canvas) and the cache-miss path
> (derive the coarser input fresh) **must produce byte-identical finer-step
> output** — both are the same pure function of the same seed. This is a
> required test, the same shape as T-1170's window-independence invariant. If
> it passes, the two paths are indistinguishable except in speed, which is the
> definition of "optimization, not dependency." If it ever failed, that would
> be a determinism bug (a non-pure derivation), caught here rather than
> corrupting a save (D-227's determinism-is-save-critical clause).
> - **Benched numbers survive as worst-case ceilings (Dudley-confirmed).** Every
> ①②③ cost number measured the *independent re-derivation* path — which is
> exactly the cache-miss fallback. So the measured costs are the **cache-cold
> ceiling**: the shipped system is never slower than the appendix, and is
> faster whenever the coarser baseline is resident. This closes Troblum B2's
> "every cost number prices the wrong architecture" worry — the numbers price
> the *fallback*, and the fallback is the worst case, so they remain
> authoritative as an upper bound.
> - **No chain-reaction on eviction (Dudley-confirmed).** A deep-step (chunk)
> cache miss does **not** walk backward through evicted District/Region cache
> state — each rung's fresh-derive fallback is fully self-contained
> (`derive(seed, position)` takes nothing but those two inputs, by
> construction), so a miss costs *one* fresh derive at that rung's own inputs,
> never a cascade through every evicted ancestor. Troblum B2's chain-reaction
> worry **dissolves** — there is no dependency to chain, because the fallback
> was never built to depend on any other rung's cache state.
> - **Acceleration magnitude is unquantified but not architecture-gating
> (Dudley-confirmed honest caveat).** The saving from reading a cached
> continuous baseline vs. recomputing it (mechanism B) is *very likely real
> but modest* — no bench isolates the region-baseline-blend sub-cost from the
> full `derive_at_metres` call (most of the per-cell cost is the octave-sum
> detail-scatter work mechanism B does *not* propose caching). This is a
> **future implementation-time measurement**, not a gate on the architecture
> ruling: the ①②③ ceiling numbers already answer the load-bearing yes/no, and
> the "expected" (as opposed to worst-case) latency is sized later.
> - **Cross-reference:** supersedes Dudley's round-2 §(b) "independent
> re-derivation, never reads a cached response" *as the exclusive rule* — that
> ruling survives as the **fallback path's** description (function-composition
> on a cache miss); interview 2 adds the cache-read **fast path** (mechanism B)
> on top, governed by the byte-identical-paths test above.
### (a.11) Lakes — settled-hydrology sourcing of the existing `MorphologyZone::Lake`; endorheic cue via outflow-course presence (FINAL, converged)
**FINAL — fully converged on both sides.** The endorheic-cue residual is closed:
Dudley picked Araminta's **outflow-course presence** (his doc updated with proper
supersession — the stale 4-state-`water` sections, a crossed-in-flight reply to
Araminta's *original* questions, are marked SUPERSEDED-BY-CROSSING and traceable,
not deleted; his corrected section carries her 8-dense + 2-sparse struct with a
no-dissent confirm). No open clauses remain in this amendment.
Paste as an amendment on D-227 (the derive-pipeline record — the fill is a
data-source fix, not a wire change) with a D-239 §6 cross-reference. Converged
sources: Araminta's encoding ruling (`araminta-round2.md` §(e), where she verified
Dudley's code-read at source — `district_profile.rs:556-565`,
`generator.rs:1198-1223`, `features.rs:104-105` — and withdrew her earlier
new-field proposal) + Dudley's pipeline ruling + endorheic-cue pick
(`dudley-interview2-response.md`, "LAKES CONVERGENCE", corrected section).
> **Amendment (2026-07-23, body-map-viewer workshop — lakes sourced from settled
> hydrology; endorheic cue via outflow-course presence).** The outline's lake
> behaviour (settled fill, overflow, endorheic sinks — measurement ①/T-1177)
> reaches the Atlas map with **no new wire field and no vocabulary change**:
>
> - **Lakes ride the EXISTING `MorphologyZone::Lake`** (discriminant 1, already
> in the frozen 17-zone D-239 §6 vocabulary — *not* a new zone; **no new wire
> field for the fill itself**). The gap was never a missing vocabulary entry; it
> was a missing **data source**. Today `derive_morphology_zone` emits `Lake`
> from a crude heightmap threshold (`ocean_fraction_q >= 60`, a bilinear sample
> of `elev < sea_level`), with **zero connection to `HydrologyResult`'s
> settled-equilibrium basins** (the code's own comment already flags this as a
> known gap). **The fix:** `derive_morphology_zone` sources the `Lake` emission
> from settled hydrology instead of the heightmap heuristic, **falling through
> to today's `ocean_fraction_q` heuristic where no basin exists.**
> - **Pipeline sourcing — sample the continuous `filled_scaled` field, NOT
> projected basin-cell membership (Dudley's ruling, adopted).** Water derives
> from `HydrologyResult`'s continuous `filled_scaled` field **sampled bilinearly
> at each gridunit's world position** (the same mechanism `ocean_fraction_q`/
> `sea_level` already use) — *not* from basin-cell membership projected as a
> discrete 512×256 lookup, which would produce a **blocky, non-refining lake
> edge — exactly the magnified-coarser-composite artifact D-166's corollary
> forbids** (§(a.1)). This is **seed-chaining mechanism B (§(a.10)) applied to
> hydrology exactly as it already applies to `sea_level`**: sample a coarser
> rung's continuous primitive fresh per rung, never re-solve — so the lake edge
> *refines* with zoom (a genuine finer curve at each rung), not a coarse grid
> magnified. Sea vs. Lake is unchanged (both already-existing discriminants; the
> `OpenOcean` tier is untouched).
> - **Static, distinct from the sim-state flooded plane.** Lake basin geometry is
> a settled equilibrium — a pure function of seed + terrain (D-227), cached
> indefinitely-fresh like all geometry. It is **not** the short-TTL
> frozen/flooded sim-state plane (§(a.6)); a lake's *existence and extent* are
> static, while the flooded plane is the sim-time-varying water-height overlay.
> The two do not conflate.
> - **Endorheic-vs-overflow cue = OUTFLOW-COURSE PRESENCE. No wire bit, no new
> zone (FINAL).** The distinction (a lake that drains vs. a closed sink) ships
> as a **client-side read of whether a lake has a river course exiting it**: an
> overflow basin's outlet edge appears in `courses` (T-1170); an endorheic
> basin's does not. **Three reasons this is the right carrier, not a dense
> marker:**
> - **Proportionality.** Endorheic is **1,030 of 22,270 basins = 4.63%** in the
> real population. A per-cell bit under morphology-fold would force either an
> 18th `MorphologyZone` arm — a **D-239 frozen-vocabulary change every consumer
> inherits forever** (including the one-colorizer-family guarantee) — or a new
> wire field, for a **fewer-than-1-in-20 distinction**, when a zero-marginal-
> cost alternative exists.
> - **The work is required regardless.** Overflow lakes **must** show their exit
> rivers to be hydrologically honest (a lake that drains but shows no outlet
> river is simply wrong on the map) — so the basin-outlet→course wiring is
> built no matter what. The endorheic cue is a **side effect of that required
> work, not a feature it pays for.**
> - **Definitionally sound, no misfire case.** Every `Overflow` basin has a
> guaranteed non-empty `outlet_path` (tested:
> `overflowing_basin_has_nonempty_outlet_path`); every `Endorheic` basin has
> none. The presence/absence read is exact — there is no basin class that
> drains-but-shows-no-course or is-closed-but-shows-a-course.
> The **cliffs list is NOT the cue** (T-1177's survey: zero carved cells across
> all 267 real bodies — a cliff-wired cue would fire nowhere).
> - **Honest scope — until the outlet-wiring ticket ships, there is NO visible
> endorheic cue.** `HydrologyResult`'s basin `outlet_path`/`BasinOutcome` is
> **not currently threaded into `RiverNetwork`/`courses`** (D8-network-derived, a
> different computation; T-1177's own scope note confirms `HydrologyResult` is
> prototype-only, unwired to any payload). So the two deliverables are distinct:
> the `Lake` **sourcing fix** (self-contained — the map shows lakes as water) and
> the **outlet-wiring** (the exit-river distinction). **Between them, the map
> shows lakes but not the drains-vs-closed distinction** — state this plainly;
> it is not a gap in the design, it is the honest sequencing. The outlet-wiring
> is a **real, small, additive follow-up ticket** (§(d)), **pre-cleared by
> T-1170 Ruling 7b** (the reserved `TERMINAL` downstream sentinel: *"an interior
> sink becomes an additive drainage-extraction change, not a wire migration...
> the work is in D8 sink retention and lake morphology, and every piece this
> batch builds consumes them without modification"*), and
> `river_course::build_edges` is already **no-op-safe on `TERMINAL`** (Dudley's
> confirm) — additive, not a migration.
> - **Visual treatment:** lakes draw as water at every rung, same colorizer family
> as sea/ocean (one-colorizer-family guarantee, Araminta round-1 §3 — a `Lake`
> discriminant colours identically whether sourced by heuristic or basin);
> endorheic-vs-overflow reads via **outlet-course presence** at the lake shore
> (no distinct fill/texture on the lake body, no new styling rule for Stig — the
> present-or-absent exit river *is* the visual difference).
---
## (b) TAGGED-ENVELOPE MECHANICS — governance disposition (with Dudley on wire/serving)
**Division of labor:** I own the record framing + the D-225/D-226 §2 disposition
(what happens to the ceiling rule). Dudley owns the wire/serving design (message
shape, demux, legacy coexistence). **Dudley has ruled** (`dudley-round2.md` §(a),
grounded in a direct read of `server/src/bridge/mod.rs`); his three answers are
folded in below and §(a.5)'s bracketed clause is now resolved.
### The ceiling-rule disposition (mine, final)
The windowed-family ceiling (D-226 T-1124 §2, [HARD]) has two separable parts,
and only one moves:
- **Its purpose — "no uncorrelated concurrent windowed queries needing
per-field request correlation" — is preserved in full.** This is why the rule
existed: two independently-in-flight windowed queries would each need their
own request→response correlation, and the hand-rolled shape-demux couldn't
carry that. The tagged envelope *strengthens* this: a tagged message has an
explicit correlation handle by construction (the tag + echo key), which is
strictly better than the shape-demux the ceiling was protecting.
- **Its mechanism — "exactly one windowed field on `AtlasLayerResponse`" — is
re-scoped to the legacy carrier only.** The rule was a *fence around a
hand-rolled demux*, not a first-principles limit on windowed data. Once a
proper tagged envelope exists, the fence is obsolete for anything inside the
envelope: the envelope can carry as many correlated fields as its schema
declares, because the tag makes correlation explicit rather than structural.
**So the disposition is: the ceiling rule keeps governing `district_window`
(the legacy shape-demuxed carrier) verbatim, and does not extend to the tagged
step-canvas envelope, which is a new carrier with its own explicit correlation
discipline.** This is the same move T-1156 and T-1170 made in miniature (both
kept traffic *off* the ceiling by choosing the right carrier) — the difference
is those two stayed inside the one-field shape because they *could* (skeleton is
whole-body; courses are content of the existing field), whereas the step canvas
*cannot* (cell count alone forces a new carrier), so this one builds the
envelope the others didn't need.
**D-225's constraint is landed on its intended target.** D-225's 2026-06-12
amendment said the *next* inbound message type must introduce a tagged envelope
rather than extend shape-based detection. Every carrier since (T-1124's window
params, T-1131's BrowseRequest — "the fifth and last map-shape probe") deferred
it by riding existing shapes. The step canvas is the one that can't ride an
existing shape, so it is correctly the one that pays the deferred cost. This is
a *planned* migration executing on schedule, not an emergency.
### Three slots — Dudley's rulings, folded in
Dudley read the actual `decode_inbound`/`Inbound` code and found the
"tagged-marker" pattern D-225 asked for is **already implemented five times**
(each newer inbound shape — `StarMapRequest`/`CityNamesRequest`/`BrowseRequest`
— carries a mandatory boolean discriminator, and `ShapeProbe` already enforces
at-most-one-discriminator). So the migration extends a proven pattern rather
than inventing a new envelope format — which sharpens my governance framing: at
the *code* level this is "doable," and the "challenging" part lives in the
client rebuild (Stig) and the cache/ladder work, not the demux.
1. **Message shape → discriminator field on the existing stream.** Inbound: a
new `StepCanvasRequest { step_canvas: bool, body_id, step_index, center,
extent, min_wl_m }` as a sixth `Inbound` variant, extending the existing
`ShapeProbe` mutual-exclusivity chain. Outbound: a **dedicated
`StepCanvasResponse` message** (not a field on `AtlasLayerResponse` — D-226
T-1124 §2 names this exact "dedicated response message by rule" case) +
`SimBridge::send_step_canvas_response`. **My §(a.5) text stands verbatim** —
"its own tagged message type on the existing IPC stream" is exactly a
discriminated inbound variant + dedicated response message; D-225's language
was "a tagged envelope *(or a required marker field)*", and the required
marker field is what ships. The ceiling disposition is identical to what I
drafted.
2. **Legacy coexistence → `district_window` survives unchanged; the envelope
carries only the new step-canvas traffic.** Dudley's call matches my lean:
no retirement message, no dual-write, no migration of existing windowed
traffic. The legacy carrier keeps working byte-for-byte until Stig's stepped
viewer replaces its only client, at which point it goes cold and gets deleted
in a follow-up cleanup ticket (D-005/D-192 co-ship guarantee means no
old-client/new-server window). **Therefore the ceiling rule is RE-SCOPED (my
§(a.5) primary text), not fully retired** — it keeps governing the live
legacy `district_window` carrier verbatim; the envelope is the new carrier
outside it. The §(d) deprecation table's parenthetical "(if Dudley rules
one-carrier-subsumes-all → SUPERSEDE)" branch does **not** fire — re-scope is
final.
3. **Rung-0 (Global) and rung-1 (Region) both ride the NEW envelope** — at
`step_index: 0` (Global, the variable body-surface region-grid canvas) and
`step_index: 1` (Region, 204.8 km fixed), **not** the legacy whole-body
`region_grid`/`district_grid` `Option` fields. Dudley's reasoning is the one
I'd have given: putting the opener on the legacy family would force the new
stepped client to speak two response protocols (envelope for the deeper rungs,
old family for the opener) for zero architectural gain — one request shape,
one response shape across the whole ladder is simpler on both sides and is
what premise 8 ("step boundaries = compute-chunk boundaries") implies
uniformly. The only per-rung difference is that rung 0's canvas *extent* is
variable (the region grid) rather than a fixed pixel budget — a field value on
`StepCanvasRequest`, not a different message shape. **Render-architecture
record "Wire" bullet updated accordingly** (§(a.0): the envelope carries every
rung including the Global opener; the legacy whole-body layers keep serving
whatever non-stepped consumers survive the cutover, if any).
**Response framing (Araminta's relayed dense/sparse question, Dudley answered):**
one flat tagged `StepCanvasResponse` carrying all fields together (six dense
PNG-per-field + `settlement_id` dense + `courses`/`cliffs` sparse
MessagePack-native), **not** a dense/sparse split into separately-tagged
sub-messages. The progressive-paint UX case (terrain first, annotations after)
is real but belongs client-side (paint the RTT layer on decode, defer the
annotation draw a frame) — no wire-protocol complexity for a cost split the
measurements don't show (courses +0.09–0.21 ms, negligible). This matches the
whole-payload-together precedent D-225 set for `Layer1Output`.
*Filing note: §(a.5) and §(a.0) are now final — no bracketed clauses remain.*
---
## (c) STEP-LADDER TABLES — snap-rule constraints (mine) + costs (Dudley's)
**INTERVIEW-2 UPDATE — tile/voxel dropped, chunk-64 m is the new deepest rung,
and Dudley's Option D is DELIVERED (every row measured).** Jeroen ruled the tile
rung "unusable" (§(a.8b)); chunk (64 m) is the deepest Atlas rung. Dudley
benched the never-measured chunk rung (`dudley-interview2-response.md` §1) and
built Option D — **this is now the live ladder table** (below). Options A/B/C
are superseded and retained only for the carried-forward reasoning.
### Option D — the adopted ladder (six levels: Global opener + 5 fixed rungs; chunk-64 m deepest, every fixed rung measured)
**Six levels after Jeroen's interview-2 rung-identity correction: rung 0 =
Global (the variable body-surface region-grid map opener, always-kept), then five
fixed metre rungs Region→Chunk.**
| Rung | Level | Spacing | Factor (from prev) | Canvas | Cells | World extent | Derive cost (parallel) |
|---:|---|---:|---:|---|---:|---|---:|
| 0 | **Global** (opener) | 1 gridunit / region (~205 km) | — | **variable — the body's region grid** | ~18K (avg/body; ~19K Earth-class) | whole body | **~16–21 ms/body single-thread** (Earth-class 18,915 cells = 15.9–16.2 ms; largest 25,200 = 20.9–21.3 ms; ~830–910 ns/cell); **~8.85 MB PNG all 267 bodies** |
| 1 | Region | 204.8 km | — (variable→fixed seam) | 3840×2160 | 8.3M | whole body (capped mosaic) | 1,827 ms |
| 2 | District | 2,048 m | 100× | 3840×2160 | 8.3M | 7,864 × 4,424 km | 1,827 ms |
| 3 | Quarter | 512 m | 4× | 3840×2160 | 8.3M | 1,966 × 1,106 km | ~1,827 ms (same-band) |
| 4 | Block | 128 m | 4× | 3840×2160 | 8.3M | 491 × 276 km | ~1,827 ms (measured) |
| 5 (deepest) | **Chunk** | **64 m** | **2×** | 3840×2160 | 8.3M | **245.8 × 138.2 km** | **1,724–1,733 ms (measured)** |
**Rung 0 (Global) is categorically different from the fixed rungs:** its canvas
is the body's region grid (one gridunit per region), so its *extent* is variable
per body (the D-243 elastic seam — region count floats per `body_radius_km`,
~19K gridunits Earth-class, far smaller than a fixed 4K canvas) and it is the
sole always-kept tier. Rung 1 (Region) is the largest *fixed* rung — 204.8 km
spacing, a fixed 3840×2160 viewport canvas, evictable like every rung below. The
**Global→Region transition (rung 0→1) is the variable→fixed seam**, not a metre
factor; the first metre factor is Region→District (rung 1→2, ÷100).
**Every fixed row (1–5) is directly measured or in a directly-measured band —
the "zero asterisks" property the old Option B earned by *skipping* chunk, now
earned by *measuring* it.** (Rung 0 is Dudley-measured too: ~16–21 ms/body
single-thread over ~18K region-grid gridunits, ~8.85 MB PNG across all 267
bodies — trivially cheap to derive and store.) Two things Option D settles that
the old tables couldn't:
- **The deepest-step canvas-sizing special case is GONE.** The old voxel/tile
deepest step needed a display-ratio-sized canvas (216×384 m at 10 px/tile,
*not* the fixed-budget convention every other step used, because the two were
incompatible at 1 m spacing). **Chunk has no such incompatibility** — at 1×1
px/gridunit the fixed 3840×2160 budget and the "no magnification margin"
bottom-out rule are the *same* rule, so chunk uses the plain fixed-canvas
convention like every other rung. Dropping the special case is a genuine
simplification, a free consequence of Jeroen's own ruling.
- **Deepest-step derive cost rose two orders of magnitude in absolute terms
(17 ms → ~1.7 s) — and that is correct, not a regression.** Chunk's canvas
covers a vastly larger world area (216×384 m → 245.8×138.2 km) at the *same
cell count*, so it costs the same as every other rung's full canvas
(1.7–1.8 s parallel band) — which is exactly what "no special case" means.
1.7 s parallel is comfortably inside the step-cross tolerance every number in
this workshop uses. Cost still does not gate this rung.
**Bottom-out rule (record-level, §(a.0)/§(a.4)):** *1 screen px per 64 m
gridunit, no additional magnification margin.* The old 10×-per-tile rule was a
legibility margin for a sub-readable 1 m unit; a 64 m gridunit is already a
legible map feature, so it does not carry over — applying it would needlessly
shrink coverage 10× for no legibility gain.
**My snap constraints (hard, from the snap rule + interviews 1 & 2, corrected for
the Global/Region rung-identity fix):**
- **Every *fixed* rung's gridunit spacing = one of D-243's metre rungs.** The
Atlas ladder's fixed rungs are region ~205 km (rung 1), district 2,048 m,
quarter 512 m, block 128 m, chunk 64 m (rung 5) — tile/voxel (1 m) is NOT an
Atlas rung (§(a.8b), Phase-5 in-world content).
- **Rung 0 = Global** — the *variable* map opener (one gridunit per region, the
body's region grid; D-243 elastic seam), the whole-body canonical / always-keep
tier. This is the ONE variable-extent level; it is not viewport-derived (its
gridunit is a whole region, a fixed property of the body).
- **Rung 1 = Region** (~205 km) — the largest *fixed* rung, viewport-sized and
evictable like every rung below it; **not** the always-kept tier (that is
Global).
- **Deepest rung (5) bottoms at chunk (64 m)**, at 1×1 px/gridunit, no margin —
fixed 3840×2160 canvas like every other fixed rung (no display-ratio-sized
exception; that oddity died with the tile rung).
- No fixed rung invents a spacing D-243 doesn't name.
- Display ratio (px/gridunit, 1×1 → 5×5) is the free client knob, decoupled
from spacing — it sizes the canvas *extent* and the on-screen scale, never
the spacing.
---
**The material below (Options A/B/C + convention notes) is pre-interview-2
(voxel-deepest) reasoning, superseded by Option D above.** Retained for the
carried-forward logic (factor-unevenness analysis, the zero-asterisk argument)
— every "voxel 1 m / 216×384 m / 10 px/tile" row is void.
> **The tables and convention notes below are pre-interview-2 (voxel-deepest)
> reasoning, retained for the carried-forward logic (canvas convention, factor
> analysis, zero-asterisk argument). Options A/B/C and every "voxel 1 m / 216×384
> m / 10 px/tile" row are SUPERSEDED by Dudley's Option D (chunk-64 m deepest).
> The display-ratio-split *logic* below still holds — it just now lands the
> "display-ratio-sized deepest step" at the chunk rung, with Dudley's chunk
> bench supplying the deepest-row numbers that replace 216×384 m / 17 ms.**
**Canvas convention (Dudley's correction, adopted — reads at chunk now, not
voxel):** the fixed 3840×2160 canvas-pixel budget holds at every step **except
the deepest** — at the deepest rung the fixed-px-budget convention and the
display-ratio contract are mutually incompatible (a fixed 3840×2160 canvas at
metre-scale spacing covers little ground yet holds many cells, oversized for the
bottom-out display density), so the deepest step is **display-ratio-sized
instead**. *(Pre-interview-2 this was voxel 1 m → 216 × 384 m / 82,944 cells,
measurement ③; post-interview-2 the deepest rung is chunk 64 m and the extent is
Dudley's Option-D chunk display band.)* This is a convention-level split
(fixed-budget shallow, display-ratio deep), not a skeleton-specific one.
**Per-step display ratio (Stig, measurement ⑥):** the px-per-gridunit ratio is
step-dependent, not flat — **1×1 at Block/chunk** (full fidelity where the
player is nearest visible detail; ⑥ confirms `set_pixel` at 77.5 ns/cell flat,
so cost is cell-count-bound, not display-density-bound), **1×1 preferred at
Quarter/District** while the realistic canvas stays under ~2M cells, **~5×5 at
the shallow rungs (Region and the Global opener)** where extent grows. This
tightens the extent column above: at
the shallow steps the ~5×5 ratio means a coarser realistic canvas than the
fixed 3840×2160 worst-case (fewer cells, cheaper), so the 8.3M/1,827 ms figures
in the tables are the *fidelity-ceiling* cost, not the typical one — the actual
shallow-step canvas at ~5×5 is proportionally smaller. The deep steps stay at
1×1 (the 17 ms/83K deep-step number is already at full 1×1 fidelity). Cost only
*improves* against the table where the ratio relaxes; nowhere does it worsen.
**Three candidate ladders — Dudley's cost fills folded in** (all costs
MEASURED or same-band-confirmed against ①–④; the one honest gap, chunk at 64 m,
is flagged not guessed):
### Option A — one-rung-per-step (6 steps, clean 1:1 to D-243)
| Step | Gridunit rung | Spacing | Factor | Viewport extent | Cells | Derive cost (parallel) |
|---|---|---:|---:|---|---:|---|
| 0 | region | 204.8 km | — (canonical) | whole body (capped-tile mosaic) | 8.3M | 1,827 ms MEASURED |
| 1 | district | 2,048 m | ÷100 | 7,864 × 4,424 km | 8.3M | 1,827 ms MEASURED |
| 2 | quarter | 512 m | ÷4 | 1,966 × 1,106 km | 8.3M | ~1,827 ms same-band (T-1154) |
| 3 | block | 128 m | ÷4 | 491 × 276 km | 8.3M | ~1,827 ms MEASURED (T-1154) |
| 4 | chunk | 64 m | ÷2 | 246 × 138 km | 8.3M | **UNMEASURED** — never benched; cutoff-mechanism reasoning strongly implies same-band, but Dudley refuses to report a number he didn't run |
| 5 | voxel | 1 m | ÷64 | 216 × 384 m (display-ratio) | 83K | 17 ms MEASURED (③) |
*My concern with A, now confirmed by cost:* chunk (64 m) is the one rung nobody
measured, and it's D-243's *stream/derive unit*, not a natural display rung —
its ÷2 factor is a barely-perceptible zoom notch that adds a fetch and an
asterisk for no visual gain.
### Option B — skip-chunk (5 steps) — the joint recommendation
| Step | Gridunit rung | Spacing | Factor | Viewport extent | Cells | Derive cost (parallel) |
|---|---|---:|---:|---|---:|---|
| 0 | region | 204.8 km | — (canonical) | whole body | 8.3M | 1,827 ms MEASURED |
| 1 | district | 2,048 m | ÷100 | 7,864 × 4,424 km | 8.3M | 1,827 ms MEASURED |
| 2 | quarter | 512 m | ÷4 | 1,966 × 1,106 km | 8.3M | ~1,827 ms same-band |
| 3 | block | 128 m | ÷4 | 491 × 276 km | 8.3M | ~1,827 ms MEASURED |
| 4 (deepest) | voxel | 1 m | ÷128 | 216 × 384 m (display-ratio) | 83K | **17 ms MEASURED (③)** |
*Why B wins, and it's an evidence argument, not a taste one (Dudley + me
converge here independently):* **every row is directly measured or same-band-
confirmed — zero asterisks.** That's a direct consequence of chunk never having
been benched: B is the only one of the three skeletons that doesn't lean on the
unmeasured rung. The one genuinely large factor (block→voxel ÷128) lands at the
*deepest* step where between-step magnification matters *least* (max detail
already; no finer step to under-serve). The ÷100 Region→District factor at
0→1 is the one that maps onto a real seam — see the factor note below.
### Option C — coarse-doubled (4 steps, skip District too)
| Step | Gridunit rung | Spacing | Factor | Viewport extent | Cells | Derive cost |
|---|---|---:|---:|---|---:|---|
| 0 | region | 204.8 km | — (canonical) | whole body | 8.3M | 1,827 ms MEASURED |
| 1 | quarter | 512 m | ÷400 | 1,966 × 1,106 km | 8.3M | ~1,827 ms same-band |
| 2 | block | 128 m | ÷4 | 491 × 276 km | 8.3M | ~1,827 ms MEASURED |
| 3 (deepest) | voxel | 1 m | ÷128 | 216 × 384 m (display-ratio) | 83K | 17 ms MEASURED |
*Why I list but don't recommend C:* **it costs identically to B row-for-row**
(same rungs, same measured rates — District's absence is not a cost saving,
it's purely a pacing choice, Dudley confirms). Its distinguishing feature is the
÷400 Region→Quarter jump at 0→1 — a brutal between-step magnification interval
(the held Region canvas magnified ~400× to approximate a Quarter view before the
fetch lands is the D-166-corollary artifact at its worst). Only viable if fetch
latency makes that magnified hold imperceptible — and since costs don't separate
B from C at all, this is a pure UX-pacing call for Jeroen, not a cost tradeoff.
### The factor-unevenness note (Dudley's serving answer, folded in)
**The rung-identity correction makes the serving-seam story *cleaner*, not
messier** — the architectural boundary now lines up with a genuine rung boundary
instead of being buried inside a metre factor. The two seams that matter are now
*distinct*:
- **The variable→fixed / global→sub-global seam is rung 0→1 (Global→Region).**
This is the one real serving-side boundary: Global (rung 0) is variable-extent
and always-kept; Region (rung 1) is the first fixed, viewport-sized, evictable
rung. The cache-tier split (keep-always vs. storage-evictable, Dudley's §(d))
falls exactly on this seam — cleaner than the old framing, which had the cache
boundary and the ÷100 metre factor collapsed onto one "step 0→1" edge.
- **The metre factors (÷100, ÷4, ÷4, ÷2) are all *within* the sub-global fixed
ladder** (Region→District ÷100, then ÷4/÷4/÷2). None crosses a serving seam —
Region, District, Quarter, Block, chunk are all sub-global geometry, same
eviction policy, same derive path, same flat per-cell cost. The ÷100
Region→District jump is the largest metre factor but it is *pure UX pacing*
now that it no longer coincides with the cache boundary (which moved up to
rung 0→1). Option D's gentle bottom factors (largest sub-global jump ÷4, deepest
transition a barely-perceptible ÷2) are *strictly better* for the between-step
magnification red flag than any prior option.
### Recommendation — Option D (adopted)
The Option D table at the top of §(c) is the adopted ladder: **six levels**
(Global opener + 5 fixed rungs: Region→District→Quarter→Block→Chunk), every fixed
rung measured, no unmeasured rung, no deepest-step canvas-sizing special case,
and the gentlest factor profile of any candidate. It inherits everything the old
Option B earned (measured-not-inferred rows) and removes Option B's one remaining
awkwardness (the voxel deepest-step special case) for free, as a consequence of
Jeroen's own
tile-drop ruling. *(The old A/B/C ranking below is superseded — Option D is what
the chunk-deepest ruling produces, and it dominates all three.)* **The one call
left for Jeroen is confirming the six-level shape** (Global opener + 5 fixed
rungs) — Option D is Dudley's and my joint recommendation, no B-vs-C fork
survives the tile drop.
---
## (d) DEPRECATION-SWEEP DRAFT (Expected Output 2)
Every DQR record and ticket that conflicts with the decided setup, each with a
disposition one-liner — **ready for the Clerk consistency audit at wrap-up.**
The audit's job is to catch conflicts this list *didn't* name; this list is the
known-conflicts starting set (the governance delta's named items + the round-1
ticket reconciliations the brief called out).
### DQR records — amend / supersede
| Record | Disposition |
|---|---|
| **D-166 corollary (2026-07-21)** | AMEND — repoint the "continuous field, not fixed display rasters" sentence to per-step (§(a.1)); it now owns between-step magnification as a bounded exception. Not deleted. |
| **D-226 T-1124 §2 windowed-family ceiling** | AMEND — re-scope "exactly one windowed field" to the legacy `district_window` carrier only; the tagged step-canvas envelope is a new carrier outside the rule (§(a.5)). Purpose preserved, mechanism narrowed. (Re-scope is **final** — Dudley ruled `district_window` survives, envelope carries only new traffic, §(b); the SUPERSEDE branch does not fire.) |
| **D-226 T-1143 ruling 2** ("no forced tagged-envelope migration") | SUPERSEDE — migration now triggered (§(a.2), §(a.5)). |
| **D-226 T-1143 ruling 3** (continuous cursor-anchored zoom) | SUPERSEDE for transport (stepped); cursor-centering/edge-scroll/full-reset survive (§(a.2)). |
| **D-226 T-1143 §6 `select_rung` / `MAX_COVERAGE_M` coverage-walk model** | SUPERSEDE — replaced by the discrete step index (§(a.3)). |
| **D-226 T-1143 ruling 1** (item-(d) floor opened "toward block/tile") | AMEND / NARROW (interview 2, §(a.8b)) — the opening now lands at **chunk (64 m)**; tile/voxel (1 m) re-closes for the Atlas (Phase-5 in-world content). A deliberate scoping of the BHAG, not a reversal. |
| **D-243 item (5) vocabulary** | AMEND (additive) — add `gridunit` as a role name for a rung; add the display-ratio-vs-spacing distinction; **the Atlas ladder bottoms at chunk (64 m), tile/voxel not an Atlas rung** (§(a.4), interview 2). No D-243 rung *definitions* change — the metre ladder is intact; only which rungs the *Atlas* uses is narrowed. |
| **D-225 (2026-06-12 tagged-envelope-deferral constraint)** | SATISFY (note, not supersede) — the deferred constraint is now executed by this workshop's envelope; annotate D-225 that its "next inbound message type must be a tagged envelope" constraint is discharged here (§(a.5)). |
| **D-226 item (d) whole-body prohibition** | AMEND (Troblum S3 + interview 2) — state the prohibition as a **per-request / per-derivation** constraint, not aggregate-storage; add the per-body deep-rung client-cache retention cap (§(a.8)); and record the floor's partial-restore at chunk (§(a.8b) — the accumulation case is now chunk/block spacing, tile/voxel Atlas-invisible again). The prohibition's *substance* survives; this hardens it and re-narrows its floor. |
| **D-227 (derive-don't-store)** | AMEND (additive) — (1) the map-time TTL-split + staleness-vs-storage eviction-axes distinction (§(a.6)); (2) the persistent-cache schema/version-tag requirement (§(a.9), Troblum S5); (3) **the seed-chaining "cache-accelerated pure function" model** (§(a.10), interview 2) — a finer step may read a resident coarser canvas as an *optimization* with derive-fresh fallback, staying inside derive-don't-store because the coarser value is itself re-derivable to identical bytes (optimization, not semantic dependency); (4) **the lake sourcing fix** (§(a.11)) — `MorphologyZone::Lake` sourced from `HydrologyResult`'s continuous `filled_scaled` (mechanism B), a derive-pipeline data-source change, **not** a D-239 vocabulary change (the frozen 17-zone set is reused verbatim — see the D-239 note below). All four are additive; derive-don't-store's core is unchanged and, for (3), *reinforced* (the byte-identical-paths determinism test is D-227's own eviction-validity property applied to a fast path). |
| **D-192 (no client/server version handshake)** | CROSS-REF note (Troblum S5) — annotate that the co-ship "always in sync" guarantee does not extend to the persistent disk cache; the schema/version tag (§(a.9)) is where that boundary is handled. Not a change to D-192's live-wire reasoning, which stands. |
| **river-courses-t1170.md carrier rule (Ruling 1c)** | AMEND (note) — terminology repoint ("windowed payload" → step-canvas envelope) + add `cliffs` as rule (iii)'s second vector member (§(a.7)). Substance unchanged. |
**Records confirmed SURVIVING UNTOUCHED** (state explicitly so the Clerk audit
doesn't over-rewrite): D-010 (four principles — the server relocation
*reinforces* principle 4); D-166 cascade + phase gating (still Phase-4 Atlas);
D-169/D-170 (implant UI — the map component still lives in the implant Atlas
app, occludes gameplay via HudGroups); D-223 (`atlas_city_names` — Araminta's
settlement-name lookup rides it unchanged); D-239 (the 17-zone morphology +
vegetation/glaciation vocabulary — frozen across every step per Araminta's
one-colorizer-family ruling; **the §(a.11) lake fix explicitly does NOT touch it
— it reuses the existing `MorphologyZone::Lake` discriminant, changing only its
data source, and the endorheic cue rides `courses` precisely to avoid an 18th
zone**). *(D-226 item (d) moved out of this list — it is
now AMENDED per Troblum S3, above.)*
### Tickets — cancel / re-scope / repurpose
| Ticket | Current status | Disposition |
|---|---|---|
| **T-1176** (design discussion: revise the Atlas map render mechanism) | in_progress | CLOSE as delivered — this workshop *is* its output; the render-architecture D-record + amendments are the deliverable. Mark done at filing, referencing the new D-record. |
| **T-1158** (decompose `atlas_window_viewer.gd` — canonical-frame + rung-reselection cluster) | backlog | CANCEL with supersession note (Stig's round-1 call, confirmed): the canonical-frame state machine T-1158 wanted to extract is exactly what changes shape under stepped zoom (no continuous zoom floor, no `_canonical_fit_zoom()`); extracting code about to be deleted is waste. The new component's three-piece decomposition (input/pan, step-cross orchestration, two draw layers) is decided fresh in the implementation ticket, not by reviving T-1158. Note: "superseded by body-map-viewer render architecture (D-NNN); decomposition folded into the new component's implementation." |
| **T-1175** (Atlas map-reading polish — river/nature presentation, single large pass) | backlog | RE-SCOPE + note the styling-engine dependency: T-1175's per-vertex river tapering/width-grammar work lands on the **screen-space annotation layer** (§ Stig round-1) via a Polygon2D-strip mechanism (`draw_polyline` can't taper). **The c1 blocker is now cleared** — measurement ⑥ landed (Stig: `Image.set_pixel` 77.5 ns/cell flat) and the c1 call resolved **CPU-first** (terrain raster colorized CPU-side; shaders deferred, not a ladder precondition). T-1175's tapering is a screen-space-annotation-layer concern independent of the terrain c1 call, so it depends only on **(a) the new annotation layer landing** — the ⑥-gated dependency is discharged. Not cancelled; re-homed onto the new component, unblocked. |
| **T-1157** (redesign atlas visual-capture goldens for the zoom ladder) | backlog | RE-SCOPE as the new mechanism's verification story: the dead-goldens problem T-1157 targets is a direct consequence of the `AtlasViewer`/orbital-mosaic split that the stepped model *collapses* (global zoom = step 0, not a separate code path — Stig round-1). Re-scope to "capture harness for the stepped render architecture" — goldens key on (body, step) canvases, not the retired continuous-zoom frames. Keep, don't cancel: the capture harness is more needed than ever (it's the eyeball-check substrate for every step canvas). |
| **T-1174** (batch vs window derive paths sample different world positions for the same DistrictPos) | backlog | KEEP, unchanged in intent — this is a *derivation-correctness* bug (two derive paths disagreeing on the same position) that is **orthogonal to the render pivot** and survives it: the tagged envelope still needs batch and window derive to agree cell-for-cell (it's the same `derive_at_metres` correctness both carriers depend on). If anything the stepped model *raises* its priority (more derive paths sharing the same position math). No supersession; note only that it now blocks the step-canvas serving path too. |
| **T-1153** (continuous cursor-anchored zoom ladder in the client) | done | Its shipped CODE is superseded (the continuous zoom transport, `_view_zoom` float, progressive cross-rung refinement machinery) — the ticket stays `done` (it delivered what it was scoped for), but its *code* is on the `_canvas.scale` retirement path (below). No ticket action; flag its shipped surfaces in the retirement-path note so the implementation ticket knows what it's replacing. |
| **T-1152** (derived planetary rung — progressive capped-density tiling; AtlasViewer heightmap texture retires) | done | Its `compute_tile_grid()` whole-body Region-tile mosaic is superseded by the **rung-0 Global canvas** (the body-surface region grid, one gridunit per region — the map opener; there is no beyond-Region mosaic, and Region itself is now rung 1, a viewport-sized fixed rung, not the top). Ticket stays `done`; its mosaic code is on the retirement path. The heightmap-texture retirement it *did* land (the `reliefmap.png` display path) stays retired — that survives the pivot (heightmaps were already demoted to derivation input by the D-166 corollary). |
| **T-1153/T-1152 test suites** | — | The zoom-ladder goldens and rung-selection tests those two stories shipped are **retired with their code** (they test continuous zoom + coverage-walk selection + the Region-tile mosaic — all superseded). T-1157's re-scoped capture harness (above) is their replacement. Concretely: the `select_rung`/coverage-walk unit tests, the continuous-zoom seam tests, and the `compute_tile_grid` mosaic tests are deleted in the same change that deletes the code they cover; new step-index + step-canvas tests land under the re-scoped T-1157. Do NOT leave the old goldens asserting against retired frames (the T-1157 dead-goldens problem, repeated). |
| **NEW — lake morphology sourcing** (§(a.11) converged core) | new | CREATE — source the existing `MorphologyZone::Lake` emission from `HydrologyResult`'s continuous `filled_scaled` field (bilinear per-rung sampling, seed-chaining mechanism B), replacing the `ocean_fraction_q >= 60` heightmap heuristic as the authoritative `Lake` trigger with heuristic fallback. Self-contained; no wire change, no vocabulary change. Blocks nothing; consumes T-1177's `HydrologyResult` (currently prototype-only). |
| **NEW — hydrology basin-outlet → D8 wiring** (§(a.11) endorheic follow-up) | new | CREATE — thread a `HydrologyResult` overflow basin's resolved `outlet_path`/spill point into `RiverNetwork`/`courses` as a D8 downstream continuation (so an overflow lake's outlet edge appears in `courses`, an endorheic basin's absence reads as "no course"). **Pre-cleared additive by T-1170 Ruling 7b** (reserved `TERMINAL` sentinel; `river_course::build_edges` already no-op-safe on `TERMINAL`) — not a wire migration. This is the deliverable the endorheic-vs-overflow distinction ships on: the cue is **outflow-course presence** (§(a.11) FINAL — no bit, no zone), so until this ticket lands the map shows lakes but not the drains-vs-closed distinction. Required regardless (overflow lakes must show exit rivers to be hydrologically honest); the endorheic cue is a side effect of that work. |
### The `_canvas.scale` retirement path (explicit, for the implementation ticket)
Retired **in the same change** that lands the new two-layer component (Stig
round-1 §1; sequenced so close inspection is never stranded, the T-1138
discipline):
- `_canvas.scale` / `_canvas.position` transform model + `_apply_transform()`
(continuous scale) — replaced by the step-canvas anchor (a fixed per-step
texture draw, not a continuous scale node).
- `_zs()` / `_zs_stroke()` / `_zs_ring_radius()` compensation family in
`atlas_window_geometry_nature.gd` — deleted wholesale; the unscaled
screen-space annotation layer makes them structurally impossible to need.
- `select_rung()` + `MAX_COVERAGE_M` + `compute_tile_grid()` (T-1152/T-1153
code) — replaced by the step index.
- The `AtlasViewer`/orbital-mosaic-vs-window two-code-path split — collapses to
"the Global opener is rung 0" (one viewer, one path; global zoom is the rung-0
body-surface canvas, not a separate code path).
- `_view_zoom` float + `_canonical_fit_zoom()` continuous fit — replaced by the
discrete rung index + the rung-0 (Global) canonical frame.
**Surviving client surfaces** (Stig round-1, not on the retirement path):
`_filter_for_granularity_v2()` (NEAREST/LINEAR per rung still a real RTT
draw-time question), `atlas_window_tile_set.gd`'s LRU *shape* (right skeleton
for the new client cache, plus the two-tier eviction), and the
compositing/legend/overlay-bar chrome (call-site updates only, no structural
rewrite).
---
## Summary — 100% COMPLETE, Clerk-audit-ready
All governance calls are decided and Jeroen-ratifiable, and every cross-agent
input is folded in — Dudley's interview-2 response (chunk-64 m display band +
Option D + seed-chaining confirm + S2/S4) and his corrected global-tier byte
number (**~8.85 MB PNG all-bodies**), Jeroen's **Global/Region rung-identity
correction** (below), on top of round 2's Dudley/Araminta/Stig/Troblum inputs.
**No `[DUDLEY]` brackets and no held clauses remain.** **§(a.11) (lakes) is
FINAL** — both parts converged on both sides: (1) the **core** — lakes reuse the
existing `MorphologyZone::Lake` (no new field/zone, a data-source fix) sourced
from `HydrologyResult`'s continuous `filled_scaled` field sampled bilinearly per
rung (lake edges *refine* with zoom, not blocky-magnified — seed-chaining
mechanism B); and (2) the **endorheic-vs-overflow cue = outflow-course
presence** (Dudley picked Araminta's (i); no wire bit, no 18th zone). Rationale
recorded: proportionality (4.63% endorheic doesn't justify permanently widening
the D-239-frozen vocabulary), the outlet-wiring is required regardless (overflow
lakes must show exit rivers to be honest — the cue is a side effect), and the
inference is definitionally sound with no misfire (every Overflow basin has a
non-empty `outlet_path`, every Endorheic none). Honest sequencing stated: until
the outlet-wiring ticket ships, the map shows lakes but not the drains-vs-closed
distinction. The stale "4-state water field" crossing is documented (superseded
by Araminta's withdrawal, Dudley's doc marked SUPERSEDED-BY-CROSSING) — never
adopted.
**Jeroen's rung-identity correction (post-briefing-back) — folded in
throughout.** Round 2 conflated the map opener's *spacing* with its *rung
identity*: it called Region "step 0 / the canonical tier." Corrected to **six
levels**: **rung 0 = Global** (the body-surface map opener — *variable* extent,
one gridunit per region, the D-243 elastic seam made visible; **this** is the
canonical always-kept tier), **rung 1 = Region** (the largest *fixed*-size rung,
viewport-sized and evictable like every rung below it), then District (2),
Quarter (3), Block (4), Chunk (5). Corrected everywhere: §(a.0) ladder/canvas/
cache bullets, §(a.1)/§(a.2) full-zoom-out reset (→ Global frame), §(a.4)
gridunit entry (Global is the one variable-extent level, fixed rungs snap to
metres), §(a.6) keep-always tier (→ rung 0, not Region), §(a.8) retention floor
(→ rung 0), §(b) envelope carriers (both Global and Region ride it), §(c) Option
D table + snap constraints + factor note, and the T-1152 disposition. **The
correction makes the serving-seam story cleaner:** the global/sub-global cache
boundary now falls on a real rung edge (rung 0→1, the variable→fixed seam)
instead of being buried in the ÷100 Region→District metre factor. **Byte-math
consequence (Dudley, measured):** the always-kept global tier is the tiny
variable region-grid canvas — **~8.85 MB PNG-encoded across all ~267 bodies**
(27.61 MB raw; avg ~18K cells/body), not a fixed 4K-class canvas. This supersedes
the ~174 MB figure (~440× too many cells) and makes the tier **trivially
process-resident, not merely disk-safe** — "always keep global" gets *easier*
under the correction. Sub-global (Tier 2) is unchanged: disk + storage-eviction.
**Interview-2 reworks are folded in (two changes, two ratifications):**
- **RATIFIED as-is:** the ceiling re-scope (§(a.5)/§(b), files as drafted) and
everything in the package not touched by the two reworks below.
- **REWORK 1 — ladder bottom → chunk (64 m), Option D delivered:** tile/voxel
dropped (Jeroen: "the actual tile level rung seems unusable, maybe replace
with 64"). New amendment §(a.8b) records this as a **deliberate interview-2
narrowing** of his interview-1 floor opening — used to chunk, tile/voxel
*re-closes* for the Atlas (Phase-5 in-world content). Updated consistently
across §(a.0), §(a.4) (the "10 px per tile" bottom-out superseded by **"1 px
per 64 m gridunit, no magnification margin"** — Dudley's chunk bench), §(a.1),
§(a.8), and §(c). **Option D is the adopted ladder** (six levels: Global opener
+ 5 fixed rungs Region→District→Quarter→Block→Chunk; every fixed rung measured
— chunk deepest = 245.8×138.2 km / ~1.7 s parallel). Its bonus: the
deepest-step canvas-sizing *special case is gone* — chunk uses the plain
fixed-3840×2160 budget like every fixed rung (a free simplification), and its
factor profile (÷100/÷4/÷4/÷2, all within the sub-global fixed ladder) is the
gentlest of any candidate, strictly best for the between-step magnification
flag.
- **REWORK 2 — seed-chaining → "cache-accelerated pure function":** Jeroen
ruled he meant *consuming* the coarser output, not independent re-derivation.
New amendment §(a.10) reconciles this with D-227 **without weakening it**: the
definition stays pure (`derive(seed, position)`), the implementation may read
a resident coarser canvas as an **optimization** with derive-fresh fallback,
and it stays inside derive-don't-store because the coarser value is itself
re-derivable to identical bytes (optimization, not semantic dependency —
proven by a mandatory byte-identical-paths determinism test). Dudley's §(b)
independent-re-derivation ruling survives as the *fallback path*; the benched
numbers survive as the **cache-cold worst-case ceiling**, which closes Troblum
B2. **Dudley-confirmed** (`dudley-interview2-response.md` §2): the model is
D-227-sound, no chain-reaction on eviction (each rung's fallback is
self-contained), and his **A/B mechanism distinction** is folded into §(a.10)
— "consuming coarser output" means reading the coarser **continuous primitive
baseline** (mechanism B, the shipped district-reads-region pattern) as input
to a *fresh* classification, **not** reading the coarser *resolved
classification* (mechanism A, ruled out — it would violate Araminta's
categorical re-derivation rule). Acceleration magnitude is unquantified but
not architecture-gating (a future impl-time measurement).
**Final and record-ready (§a, §b, §c, §d) — 1 new-record draft (§(a.0)) + twelve
amendment texts (§(a.1)–§(a.11), including §(a.8b)):** §(a.1) D-166 corollary;
§(a.2) T-1143 rulings 2/3 supersession; §(a.3) select_rung→step-index; §(a.4)
D-243 gridunit; §(a.5) D-226 §2 ceiling→envelope; §(a.6) D-227 TTL-split; §(a.7)
T-1170 carrier + cliff; §(a.8) D-226(d) per-request+cap; §(a.8b) D-226(d)
floor→chunk (interview 2); §(a.9) D-227/D-192 cache schema-tag; §(a.10)
seed-chaining (interview 2); §(a.11) lakes — FINAL (morphology-fold +
`filled_scaled` sourcing + endorheic cue via outflow-course presence, interview
2). §(a.7) cliff cost is survey-confirmed (0/267 carve).
§(c) carries the adopted **Option D** ladder (six levels: Global opener + 5 fixed
rungs, chunk-deepest, every fixed rung measured). Deprecation sweep: **12 DQR
dispositions** (adds D-226 T-1143-ruling-1 narrow + the D-227 seed-chaining line)
+ 10 ticket dispositions (adds the two lake tickets — morphology-sourcing +
basin-outlet D8 wiring) + the `_canvas.scale` retirement path. **No `[DUDLEY]`
brackets, no held clauses, no open items remain** — the global-tier byte total
is in (~8.85 MB PNG all-bodies, Dudley measured; supersedes the ~174 MB figure,
tier now trivially process-resident) and §(a.11)'s endorheic cue is finalized
(outflow-course presence). **The package is 100% complete and Clerk-audit-ready.**
**For Jeroen's final ratification** (governance calls, decided text ready):
(1) **§(a.10) seed-chaining** against his phrase "serves as seed information for
the deeper cascade" — the cache-accelerated-pure-function model (mechanism B) is
the reconciliation of his "consuming coarser output" intent with D-227;
(2) **Option D's six-level shape** (Global opener + Region→District→Quarter→Block
→Chunk) — the tile-drop removed the old B-vs-C fork, so this is a single confirm,
not a choice.
The migration is the one real chunk of new work; Dudley's code read puts it at
"doable" (one `Inbound` variant + one `SimBridge` method on a pattern proven five
times), with the "challenging" part in Stig's client rebuild.
*Troblum's adversarial pass is now **fully dispositioned**: B1 (cliff survey)
resolved 0/267 → §(a.7) survey-confirmed; B2 (seed-chaining) resolved by §(a.10);
**S2 (deep-step course density) run by Dudley** — a real finding (+38%–87% cost
at chunk/block vs. District's <5%, traced to course point-count scaling inversely
with rung spacing; still affordable in absolute terms, flagged as an
implementation consideration — a possible rung-independent station-spacing cap —
for the ticket plan; the measurement is filed as the "S2 ADDENDUM" in
`measurements/t1178-t1154-derive-bench.md`, alongside the chunk-64 m
"INTERVIEW-2 ADDENDUM" — both already written by Dudley); **S4 (sim-state phase
cadence) resolved** by a Dudley+Araminta joint formula (`SIM_STATE_TTL` bound to
1× the field's own driving sim bucket — tidal-or-seasonal for flooded, seasonal
for glaciation; 1× confirmed cost-affordable). S1 (evict-then-revisit costing)
remains a Dudley cache-spec tuning number, not a governance-text change — my
§(a.6) TTL amendment already separates the two eviction axes it probes. S2's
finding may warrant a one-line note in the §(a.7)/render-record wire discussion
if course rendering at chunk needs the station-cap; flagging for the Clerk audit.
Full adversarial-pass disposition is visible for the audit.*