A rung used to fix the gridunit SPACING, with the canvas extent falling out
of spacing x cell count. That is why the top of the ladder was unusable: at
REGION_M spacing a viewport-sized canvas spanned ~251,658 km — six times
around a rocky body — so the Region rung capped to the body and redrew the
Global picture pixel-for-pixel. "Global and region look the same" was not a
rendering bug; it was this relation, stated in metres.
Inverted: a rung fixes the EXTENT and the spacing falls out of the canvas
size. The shorter viewport axis spans exactly one cell of the rung's level,
so a widescreen window shows more ground on the long axis rather than less
on the short one. Every rung now shows the ground its name promises —
Region 262x466 km, District 4.1x7.3 km — and the canvas cell count is
viewport-driven and identical at every rung, so derive cost no longer varies
with depth and resize is free.
Consequences that fell out of the inversion rather than being chosen:
- Region leaves the orbital derive set. It was envelope-only because at
251,658 km nothing finer made sense; at 262 km it is a genuine provincial
map and takes the full courses-aware derive. Region having no rivers at
all was much of why the top of the ladder read flat. It also joins the
deep display ratio for the same reason.
- The S2 station-spacing floor is deleted, not retuned. It guarded an
O(1/spacing) blowup that the inversion makes structurally impossible (the
canvas cell count is now constant across rungs, so stations-per-course is
bounded however deep you scroll). Kept, it would do active harm in the
opposite direction: a 2,048 m pitch across a 3.6 km District canvas places
two stations and draws every river as a straight line. Station placement
gets its own generator pass.
- cap_extent_to_body is superseded and now a documented no-op. A canvas can
no longer over-request a body by construction. The residual question —
whether a rung's cell exceeds the whole body — is liveness, not capping,
and is_rung_live_on_body() answers it by omitting the rung. Empirically it
never fires on inhabited content: all six rungs are live on all 271
populated bodies with a radius.
- snap_to_gridunit no longer truncates its multiplier to int. Post-inversion
the deep rungs run sub-metre (Chunk ~0.12 m at a 1080 px short axis), where
int(spacing) floors to zero and would collapse every request centre onto
the origin.
Both sides derive spacing from the same three inputs (rung, echoed cell
extent, body radius) rather than one telling the other, so there is nothing
to keep in sync beyond the constant table itself. Body radius already
reaches the viewer via enter(); no wire change.
Pair session with Jeroen, 2026-07-26. D-243/D-255 amendments to be backfiled.
Co-Authored-By: Claude <noreply@anthropic.com>
Finding 2 became a real code fix: interstitial_fill_into now enforces
the footprint-wins conflict rule (column_has_voxel range probe) — the
FilledChunk absence contract was previously a documented promise the
code didn't keep against conflicting inputs; pinned by a fully-
overlapping-leaf test asserting per-tile resolution. The tautological
overlap test replaced with a real rects_overlap() geometric helper
(itself sanity-tested) applied pairwise. The degenerate-setback fix is
now a standalone pure fn shrink_lot_or_interstitial with four boundary
tests — honestly documented as unreachable from live traffic today
(every min_lot exceeds every setback), a robustness guard for future
recalibration. Both sub-chunk clip tests now reconstruct the full
32-tile union across the seam (disjoint + complete), including the
pre-existing footprint clip test (leave-cleaner). Brief's ChunkLayout
claim tightened to the verified no-production-consumer statement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The BSP leaves that lose the D-233 coverage roll in
subdivide_block_footprints were computed and discarded; they are now
surfaced as the interstitial rect set (BlockSubdivision), making the
ground-plane classification exhaustive by construction: footprint /
interstitial / street-margin-or-reserved. FillChunk carries the leaves
plus a minimal BlockFillContext (interstitial_character + setback_tier,
re-derived at block level via the existing pure fn); FilledChunk gains
a sparse interstitial map whose absence contract is stated on the
struct (missing key = footprint/street/reserved, never unknown). The
pure resolution maps OperationsSurface (D-233) first, else setback_tier
onto five of D-235's seven interstitial values — dock_slip/market_pad
have no specified trigger in the record and point at T-1209 rather
than an invented mapping. Design brief with the geometry model at
docs/architecture/interstitial-fill-t1098.md (lead-approved
checkpoint). Bonus fix: a degenerate setback shrink previously vanished
from BOTH lists silently; it now falls through to interstitial. 14 new
tests; full cargo test green incl. all golden harnesses; purity per
T-987 (plan-time compute, pre-resolved work items, no cache reads).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The surcharge is now COASTAL_ACCESS_SURCHARGE_HOPS_PER_RING=1 added
directly to length_cells (a pure hop count) — the old cost-unit
constant div_ceil'd through MIN_CELL_COST silently produced 4 hops per
ring, worst-case +24 (double the waypoint threshold) for physically
short edges; worst case is now 6. A formula-pinning test asserts both
the arithmetic and the constant. GJ251c's repro tightened to the
documented 2 edges. The always-land citation now points at the real
guarantee (features.rs::extract_attractors, D-209) — and checking the
D-211 Phase-4 synthetic-overflow path exposed a real gap: it has no
ocean-mask guard at all (T-1206 filed); documented, not papered over.
D-210 gains a dated amendment recording the surrogate-anchor-at-cost
carve-out and the relaxation-over-nudge adjudication. The bare 100
dependency dissolved with the unit fix. Edge counts on both repro
bodies verified unchanged (reachability was never affected).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A routing cell folds up to 64 native pixels, so a coastal settlement's
own land pixel (placement always filters !ocean_mask) can sit inside a
water-majority cell that RouteGrid marks IMPASSABLE — and astar()
hard-returned None for every pair touching it, zeroing whole road
graphs (GJ251c: all 3 placements; GJ380c: Sethvale). The fix relaxes
only the start/goal anchor lookup: nearest_passable_cell (ring BFS,
deterministic row-major tie-break, bounded at COASTAL_ANCHOR_MAX_RING=3)
finds a surrogate anchor and prices it via COASTAL_ACCESS_COST_PER_RING
— a short, honestly-costed access road, never a free water crossing.
IMPASSABLE semantics untouched everywhere else (D-210 transit costs,
open-ocean). The placement-nudge alternative was rejected: it would
move Layer-3 state D-211 promises is seed-derived, for no gain.
Boundary semantics pinned by test: exactly-half-water cells stay
passable (strict-majority rule); a settlement with no passable cell
within the search ring degrades to an isolated 0-edge node, never a
panic or fabricated route. Failing-first repro on real bodies
(GJ251c 0->2 edges, GJ380c 0->1) via the real cascade entry point,
plus same-seed determinism. Full cargo test green; believability and
cascade goldens verified unaffected (Layer-2-only change).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guard becomes land_districts <= 1 (both reviewers converged — a lone
island definitionally cannot show two distinct directions; same
nothing-to-vary condition one value short), with a lone-island vacuous-
pass fixture; golden confirmed untouched. Oasis scaling adjudicated as
LIVE, not future — GRID_W is already 1024 on main, so ring iterations
change 2/4 -> 4/8 today: extracted a pure oasis_ring_iterations()
helper pinned by tests at both 512 and 1024, and traced exactly why the
determinism hash stayed green (it reads only elevation; the rings touch
only biome — a genuinely different array, not a coincidence). The
drainage merge-logic question answered byte-precisely: zero logic
changed vs main (comment-only diff) — and the deeper dig PROVED the
'isolated basin with another basin to escape to' branch is
mathematically unreachable for any connected grid (contracting vertex
groups of a connected graph cannot disconnect it), so the comment now
states that instead of narrating a divergence that never fires; two
direct merge-target tests added regardless. Wrap test renamed to what
it actually pins (non-wrap-awareness). D-010 docstring softened to
same-process purity, naming the cascade golden as the cross-run layer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A real D-245 gate-shape strengthening, not just a test: the D-256/T-1174
finding proved the believability golden byte-identical under a total
all-North basin_direction collapse — every scalar contrast field is
structurally blind to the one field that regressed. ContrastMetrics
gains land_districts and basin_directions_distinct (both over ALL
districts, no new derive calls — the evidence-backed pick over the
voxel-transect proxy, which washes out at production sample density),
and evaluate_criteria gains 'basin direction variety': pass when
land_districts == 0 (the drained-body guard — an all-ocean body has no
cells that can cast a D8 vote per the aggregator's own exclusion rule,
so a uniform default is legitimate, mirroring the file's existing
nothing-to-vary idiom) or distinct >= 2. Negative test proves the
criterion catches the land-bearing all-North regression; vacuous-pass
test proves the waterworld guard. Golden regenerated and rerun-stable;
both validation bodies (Arbour, Edict) pass at distinct=2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The verified-still-open coverage list: per-type attractor reachability
fixtures (LakeShore via enclosed depression, PassEntrance via crafted
saddle, PlainCenter via flat terrain, RiverCrossing via confluence) plus
thin_by_spacing behavior (collision, strict-< boundary, equirectangular
column wrap); heightmap 8-bit decode, sea_level passthrough, downsample
identity and zero-target early-return; drainage area_pct bit-for-bit
determinism plus the isolated-basin-fallback divergence comment (Tyre
N1, citing the pre-#953 behavior it deliberately departs from); the
layer1 mountain-branch pairing test (investigated first — the cascade
test supplies a mountain pool but only ever asserted river counts, a
genuine gap); an importer idempotency test covering atlas_city_names
AND atlas_feature_names plus the Sol exemption, wired into
make test-tooling; and the oasis_water dilation radius scaled by
GRID_W/512 (Tyre N2, hash-stable). One stale item dropped per the
refinement trim (test_sim_determinism wiring — already done).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe found the concrete residual: the hand-copied driver block called
derive_temperature_c unconditionally where invent_primitives prefers the
lapse-adjusted region baseline on non-airless bodies — driver_temp feeds
glaciation/moisture into the warp magnitude, so orbital displacement
could still differ from District at the same position (the ticket's
defect class, one step upstream); the copy structurally couldn't branch
right because region_baseline_at_district was computed after it. Tyre
demanded the structural cure: steps 1-3 now live in ONE shared helper
(invent_coastal_position -> warped position + CoastCharacter);
invent_primitives composes helper + detail-scatter; the orbital path
hoists the baseline and makes a single helper call. The audit test's own
'District-style' side turned out to be a THIRD copy carrying the same
bug — rewired to the real helper (raw pre-fix side untouched as the
historical baseline; refreshed: 5.01% disagreement, mean 5,957.9 m,
748.9 ns/cell). Golden verified byte-identical under forced regen, with
the reason traced: the wire temperature never flowed through the buggy
internal branch, and the fixture's probe positions cross no discrete
boundary — the 267-body audit is the instrument that sees the drift.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One commit for two tickets whose changes share the bridge/plugin
plumbing files. T-1169 connects the three dormant feature-name pieces:
atlas_feature_names populated at regen (17,891 rows — 15,190 mountain,
2,701 river — via populate_atlas_feature_names mirroring the city-names
importer; systems.db regenerated, stamp fresh), attach_feature_names
wired into the cascade's Topography block with name pools threaded
DB-free through AnalyzeBody (D-225 pattern) and assignments stored on
Layer1Output/BodyWorldState for future consumers, and a
FeatureNamesRequest/Response read proxy as the bridge's 7th tagged
envelope (D-236 pattern, both SimBridge impls). Client label DRAW is
deliberately NOT here — implementation proved both river and mountain
labels need a wire-carried position (the pool is position-free; course
polylines aren't correlated with the named attractors by construction) —
deferred to T-1195's single design pass. cascade_layer1 golden re-pinned
(additive feature_names field).
T-1159 retires the legacy u32 granularity field fully shadowed by
window_granularity_v2: AtlasLayerRequest.window_granularity,
DistrictWindowLayer.granularity echo, the u32::MAX sentinel, and
resolve_window_granularity are gone server-side; client encode paths and
the caller-less atlas_window_cache legacy key component dropped;
msgpack fixtures regenerated; the T-1150 aliasing regression test now
drives through the surviving enum field. The district_window carrier
itself survives byte-compatible per D-255(c).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Global/Region rungs sampled the heightmap ocean mask raw while every
finer rung samples it at the coast-warped position — a structurally
different coastline at the orbital-to-district seam. Audited on all 267
real body heightmaps at region spacing, coastal band only: 5.01%
land/ocean classification disagreement (2,911 of 58,073 cells), mean
displacement ~6.0 km, max ~18.9 km. Fork taken: APPLY the warp —
derive_orbital_at_metres now runs invent_primitives' steps 1-3 (driver
climate -> coast character -> coast_warp_px) before sampling, still
skipping detail-scatter (its octave ceiling, 32.8 km, is below region
spacing; the WARP's octaves reach 262 km, which is why skipping it was
wrong). Cost measured: ~784 ns/cell added (~15 ms on an Earth-class
Global canvas; low hundreds of ms parallel at the 8.3M-cell ceiling).
The ignored audit test preserves the pre-fix baseline for the record.
Golden re-pinned — region rows only, district/quarter byte-identical.
project.yaml 0.4.2 -> 0.4.3: orbital canvas bytes changed, client disk
caches must miss. Awaiting Araminta's review-seat sign-off per ticket.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The PR #206 eyeball caught what every numeric gate passed: the depth
signal was visually flat on both test lakes (GJ1c lmq=0 across the whole
basin; GJ338Bd 0-13 of 100). Two compounding causes: a fixed absolute
ceiling (one body's p90 cell depth) compressing skewed depth
distributions into single digits, and heightmap-pitch depth variation
being sub-texel-tiny within most basins. lake_margin_q is now
depth / the basin's own maximum settled depth: HydrologyResult grows
basin_max_depth_scaled (computed in solve() from existing basin_cells
membership, broadcast per basin), threaded through
HydrologySample.basin_max_depth, normalized in lake_from_hydrology_at
with a degenerate-basin epsilon guard (a genuinely uniform pond shades
flat — honest, not forced). Lake EXISTENCE (filled > original) is
untouched — only tone changes. Measured at district spacing:
GJ1c min=0 p50=33 max=84; GJ338Bd min=8 p50=38 max=70 — full-range
shore-to-deep ramps on both. No perceptual curve added: the linear
per-basin ramp is already well-quartiled. project.yaml 0.4.1 -> 0.4.2
(0.4.1-tagged canvases carrying flat-lmq semantics reached real disk
caches during eyeball runs and must miss). Acceptance gates green;
zero golden churn (lake_margin_q not captured by either golden shape).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe's review finding: the build_step_canvas row-space convention block
still described region_centre_latitude_deg as pole-anchored/bugged and
said 'T-1186 stays open' — both false after this PR's own first commit.
Rewritten to reflect the fixed state; the deliberate signed-convention
rationale and the layer_proxy deferral (still true, still T-1181's scope)
are preserved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lake edges rendered as hard step-edges while ocean coasts got multi-tone
transition bands: every coastal-transition morphology gate keys on
ocean_fraction_q, definitionally 0 inside a lake basin (hypothesis (b)
of the ticket; (a) disproven first — a shoreline-crossing sweep at
2048/512/128m plus a 10m fine sweep all land on the same continuous
crossing, so positional refinement was never broken). New
DistrictProfile.lake_margin_q (0-100 settled-hydrology depth band, from
the same bilinear filled/elevation pair the lake test already samples;
ceiling calibrated just above the observed p90 depth on GJ338Bd's 5,043
flooded cells), threaded through both derive paths onto
EncodedStepCanvas (serde-default for shape tolerance) and down the
client: protocol decode, terrain-layer plane, colorize shades Lake cells
by depth band instead of elev_q (bedrock-under-water, the wrong signal).
project.yaml 0.4.0 -> 0.4.1: the new wire field must invalidate the
client disk cache via its version tag (T-1183's D-192 mechanism).
Acceptance gates green with the new field (lossless round-trip,
cache-hit==cache-miss, every rung).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
region_centre_latitude_deg mapped row 0 to the north pole with lat_frac
clamped [0,1] while the derive core floor-divides equator-anchored signed
world metres — every northern-hemisphere region clamped to +90 (polar
everywhere) and the southern hemisphere read as compressed northern
tropics. Now mirrors the derive core's exact inverse mapping (lat_frac =
centre_y/meridian clamped [-0.5,0.5], lat = -frac*180). The ticket's
in-the-wild datapoint flips as predicted: GJ338Bd region (136,43), old
baseline +13.94N/26.8C -> new -76.06S/10.3C; the 76S district cell now
derives Lake/2.07C/Light glaciation. Goldens regenerated (believability +
window derivation, stable on rerun); two region_profile unit tests
repositioned — their probe coordinates meant different places under the
old convention. step_canvas rung-0 already used the correct signed
convention; layer_proxy's pole-anchored pseudo-grid stays deferred to
T-1181's rung-0 rebuild per D-256(f).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe finding 1 (live-firing on GJ1c: 2 spill collisions + 1 i==1
collision among 51 Overflow basins): adjacency adjudicated
INSUFFICIENT for the cue — a course's visible anchor is its upstream
cell, so nothing pre-existing belongs to the lake unless wired. The
spill cell (outlet_path[0]) now always gets a real entry: appended
when new, OVERWRITTEN IN PLACE when it collided with an existing river
cell (append would duplicate edge_id; the hydrology solve is the more
authoritative downstream answer for that cell than flat D8
extraction). Interior stop-on-collision stays, now provably safe.
Internal lookup is a dense Vec<Option<usize>>, never iterated (D-010).
Two non-vacuous regression tests prove the cue through build_edges
output; end-to-end on GJ1c all 51 Overflow basins now build a readable
edge (was: one silently missing).
Hoshe finding 2: both doc sites now state the fallback-vs-production
split explicitly (fallback moisture 55: 51/2; production GJ1c moisture
80: 53/53 all-Overflow) — the golden's Endorheic pair is a
fallback-constant artifact, not a fact about GJ1c.
Golden re-regenerated: river_cells 143->192, position-identity diff
purely additive (zero removed, one legitimate in-place overwrite at
the spill-collision cell); attractors/basins/mouths/confluences
byte-identical. Suites: hydrology 26/26, full lib 1943, cascade_golden
1/1, window goldens + believability untouched green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
extend_river_network_with_basin_outlets threads each Overflow basin's
resolved outlet_path into RiverNetwork as real D8 downstream
continuations (spill cell skipped as lake interior; Sea target ends at
RIVER_DOWNSTREAM_MOUTH with a seaward entry, Basin/OpenSpillway at
RIVER_DOWNSTREAM_EDGE_DRAIN); Endorheic basins contribute nothing —
the ruled zero-bit cue is outflow-course PRESENCE (D-227 amendment
(4)): a lake with no outgoing course reads as closed. Additive per
T-1170 Ruling 7b; no wire migration, no new zone, no endorheic bit.
Wired in run_layer1_with_moisture strictly AFTER attractor extraction
so outlet cells never perturb settlement placement (tested). Edge
identity is the existing pack_cell_id (row,col) convention; wiring
determinism proven by three independent double-solve byte-identity
tests. 13 new tests across hydrology_equilibrium and layer1.
Goldens: cascade_layer1.json re-pinned (third deliberate re-pin,
documented in cascade_golden.rs) — river_cells 93->143 on GJ1c,
append-only prefix byte-identical, 51/53 real basins Overflow with
short OpenSpillway stubs (the dominant honest pattern: most basins
spill onto adjacent open ground, not a long channel to sea).
window_derivation_golden and river_course_golden verified structurally
unreachable and unchanged.
Known scope boundary (documented in-code): outlet cells carry
river_class 0 (stream) — outlet-channel classification/meander width
scaled to catchment is a named follow-up, not guessed at here.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe finding 1: StepCanvasRequest.extent is no longer wire-trusted —
clamp_step_canvas_extent enforces the D-255(b) canvas budget at the
request boundary (per-axis cap 3840 defeating u32::MAX before any
multiplication, then an aspect-preserving total-cell ceiling at the
measured 3840x2160 = 8,294,400-cell workshop budget), mirroring the
legacy carrier's clamp_window_n_v2 discipline; the Global rung ignores
the wire extent entirely. StepCanvasResponse gains the extent echo
field so a client can detect the clamp (the DistrictWindowLayer.n
precedent — a pre-existing gap closed in passing, recorded on the
ticket for T-1182). Seven new tests including an end-to-end u32::MAX
request proving actual allocation respects the cap.
Hoshe finding 2: submit_step_canvas coalescing now has the same two
regression tests its submit_window sibling always had (same-key
collapses to one pending item, different-key does not), exercising
step_canvas_supersede_key.
Targeted suites green: 1928 lib, acceptance gate 5/5, bridge_tcp 22/22,
window_derivation_golden 6/6 byte-green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two needless mutable borrows in the serve system, one spurious
markdown list marker from a doc-comment line wrap (a leading '- '
turned the whole T-1186 convention note into a lazy continuation), and
a named row/col pair replacing the identity-op grid index in the
settlement-id tie test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The T-1184 merge seam: TerrainAnalysisCache::get_or_derive gained a
body_params parameter (real moisture ceiling for the hydrology solve);
the DeriveStepCanvas arm now passes its own body_params so step-canvas
requests get the same hydrology-carrying TerrainAnalysis the legacy
DeriveWindow path does. Acceptance gate + window goldens green
post-merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The D-225-discharging wire migration per D-255(c): a new
server/src/atlas/step_canvas.rs subsystem (six-rung StepCanvasRung
ladder — Global variable-extent rung 0 through Chunk 64m — the
StepCanvasRequest/StepCanvasResponse tagged envelope extending the
proven ShapeProbe discriminated-shape pattern, PNG-per-field dense +
MessagePack-native sparse encoding per the T-1179 measured table, and
both server cache tiers: the structurally keep-always GlobalTierCache
and the dual-axis StepCanvasCache with storage TTLs per rung and
SIM_STATE_TTL clock-bucket staleness per the D-227 amendment (1)
formula). Wired through bridge/{mod,tcp,local}.rs (sixth demux shape,
send_step_canvas_response mirroring the five existing senders),
gen_queue.rs (DeriveStepCanvas work item with per-connection-per-rung
coalescing, reusing the shared TerrainAnalysisCache), and plugin.rs
(serve/complete systems, lazy D-206 rung-0 population).
Acceptance gate (mandatory per D-227 amendment (3)):
tests/step_canvas_acceptance_gate.rs — cache-hit == cache-miss
byte-identical for every rung, lossless encode round-trips, cache
round-trips vs fresh derive, distinct-center sanity. 5/5 pass.
Station-spacing cap ADOPTED: course stations floored to District
spacing (2,048 m) at finer rungs — the S2-measured +38-87% chunk/block
course cost had zero display benefit at the same station density
(COURSE_STATION_SPACING_FLOOR_M).
Documented honest gaps, not shortcuts: settlement_id is a proximity
approximation (no footprint polygons exist yet); glaciation/flooded_q
sim-state planes are wire-shape-ready D-253 stubs; rung-0 uses signed
equator-anchored rows (the canonical D-256 core convention — T-1186's
wrong-latitude behavior applies unchanged and unfixed here, by
instruction).
Legacy district_window carrier byte-unchanged: window_derivation_golden
6/6 byte-identical, all district_window suites pass unmodified. Full
suite at implementation time: 2127 passed across 45 binaries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe finding 1: dropping HydrologySample.elevation was PROVEN unsafe
(TerrainAnalysis.elev_pct is a rank percentile, not raw elevation;
HydrologyResult carries no elevation) — the copy stays, and the truth
moves into the docs instead: gen_queue's TerrainAnalysisCache sizing
comment corrected to real 512x256 working-grid numbers (~1.57 ->
~2.62 MB/entry, capacity-8 worst case ~21 MB), clone-on-hit cost
documented, byte-safety proof recorded on HydrologySample itself.
Arc<TerrainAnalysis> follow-up filed as T-1187.
Hoshe finding 2: the endorheic-split test now asserts the bowl basin's
BasinOutcome actually diverges (Endorheic at moisture 0, Overflow at
100, straddling ENDORHEIC_MOISTURE_CEILING=60) plus basin-count sanity
— mutation-verified by stubbing is_endorheic and watching it fail.
Doc/test-only round: goldens byte-unchanged, full suite 2114 green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Productionizes the T-1177 equilibrium solver: run_layer1 now solves
hydrology once per body (~24ms, mirrors drainage::analyze) and carries
it as TerrainAnalysis.hydrology; run_layer1_with_moisture threads the
real body moisture ceiling (extracted derive_moisture_ceiling_q), with
the T-1177 population-survey default as fallback. The resident rung-0
global tier does not exist yet (T-1181's scope) — hydrology rides
TerrainAnalysis and lands in that tier for free when it is built
(deviation recorded on the ticket).
MorphologyZone::Lake is now sourced from the settled solver at derive
time: a gridunit is Lake when bilinear-sampled filled surface exceeds
bilinear-sampled original elevation at the sample's own (px, py) — the
continuous comparison, so lake edges refine with rung like coastlines;
never a discrete basin-cell projection. The gate sits strictly between
OpenOcean (>= 80) and the old ocean_fraction heuristic (>= 60), which
survives as the derive-fresh fallback when no solve is attached —
byte-identical to pre-T-1184 output in that case. Static
classification, distinct from the sim-state flooded plane; no
endorheic bit (the drains-vs-closed cue is T-1185's outlet-course
presence, per the D-227 amendment (4) sequencing). Zero new wire
bytes.
Acceptance: lake_classification_cache_hit_equals_cache_miss (solve
twice independently, byte-identical zones, non-vacuous Lake hit) plus
hydrology determinism tests. Golden fidelity: the window golden
fixture now builds TerrainAnalysis through the production entry point
(run_layer1_with_moisture, per-body), and a dedicated lake_bowl golden
body pins the hydrology-sourced Lake path (morphology 1 at
ocean_fraction_q 0 — provably not the heuristic); the 108 pre-existing
golden rows are byte-identical (pure append). believability.json moved
by one lake-shaped line (GJ338Bd voxel_relief_m 27->28, a correctly
reclassified lake district leaving the dry-relief sample set).
river_course and derivation-harness goldens unchanged. Full suite:
2114 passed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe finding 1: derive_all_districts passed a permanently-empty
region_cache, so every survey cell paid four on-the-fly baseline
derivations — and derive_district_profile's docstring (build the cache
before calling in a loop) was contradicted by its sole production
caller. The cache is now pre-built on the TRUE region keys the shared
core looks up (survey-cell centre metres -> containing district ->
region ±1 ring, BTreeSet-deduped), restoring the per-body build-once
cost model. Cache-hit == cache-miss byte-identity holds by D-227 purity
(build_region_profile and the miss branch share the same expressions) —
verified by the believability harness passing against the UNCHANGED
golden. Finding 2: the cascade.rs call-site comment and the stale
module header now state the pre-build truthfully.
Found in passing, filed as T-1186 (pre-existing, out of scope here):
region_centre_latitude_deg is pole-anchored while the derive core keys
equator-anchored signed regions — northern-hemisphere baselines clamp
to +90.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
derive_district_profile is now a thin wrapper over the shared
derive_at_metres_with_riparian core at survey-cell-centre world metres
— one derive core, two position sets. The batch pseudo-grid and the
true D-243 district grid were two coordinate systems sharing one bare
(i32,i32) type; the new SurveyCellPos newtype re-keys every batch
product (BodyWorldState.districts, Layer1Output.survey_basin_dirs) so
the compiler rejects cross-namespace passing.
Fixes two latent same-position divergences the T-1174 investigation
surfaced: three inconsistent latitude conventions collapse into the
core's single inverse mapping, and the region-climate baseline now
floor-divides true world metres instead of collapsing the whole body
onto region (0,0)'s baseline — batch climate becomes latitude/region
graded (D-245 direction: every changed believability metric increased).
Binding preservations per D-256(c): basin_direction rides a post-call
override with the true L1 D8 survey-cell aggregate (layer1's map
re-keyed to SurveyCellPos, identity lookup — a floor-divide lookup
against the pseudo-keyed map would have silently defaulted every cell
North); the riparian verdict comes from near_perennial_water_at, never
the empty-slice default (which would have flipped riverside
vegetation_class).
Quarter-skeleton morphology_zone now resolves at the settlement's
exact world position via derive_at_metres at work-item execution
(where TerrainAnalysisCache lives), replacing the survey-cell-centre
map lookup (D-256(d)); settlement_district_pos fixed to true-district
floor-division in passing (same doc/impl mismatch class). Second
pixel-vs-metre conflation fixed in aliveness_probe's anchor-walk math.
Window path byte-unchanged (window_derivation_golden 6/6 byte-
identical); derivation_harness golden untouched; believability golden
regenerated. Full lib + integration suites green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tyre C2: DownstreamTarget::OpenSpillway added — success (open low ground,
complete carved path) split from EdgeUnreachable (strict search exhaustion);
both doc comments exact; test updated. Tyre C3: determinism docstring
corrected (no BTreeMap; the endorheic f64 gate stated as deterministic-by-
derivation). Hoshe H1: vacuous cliff_edge test replaced by
single_basin_bowl_never_carves_a_gorge asserting the known-empty outcome.
Hoshe H2/H3: courses-force-empty disclosure at rect_window_replica and in
the results doc; courses.len() print added to the square production-fn
bench — measured 3/6/10 courses in window (NOT courses-empty, verified
twice); 'faithful stand-in' claim retracted for a precise scope statement;
GJ1c 18-course run identified as the sole production-density rate. Tyre C1:
appendix (4) headline rephrased — the tagged-envelope migration cannot be
dodged by payload optimization (byte math), field-count-rule trigger is a
workshop synthesis call; appendix (2) scope split per-shape.
Hydrology unit suite 15/15; atlas lib suite 679 green; benches compile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cargo fmt across the four new files; needless_range_loop x2 (enumerate /
iter_mut) and identity_op in hydrology_equilibrium.rs. cargo test was green
on the bounced push — lint-only fixes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bmv_gridunit_bench: production build_district_window_layer par_iter path at
330K/2.07M/8.3M cells (throughput holds, ~190-220 ns/cell parallel), block
(128m) and tile (1m/4m) spacing costs, the 83K-cell deepest-step viewport
shape, and the verified zero-savings octave-cutoff finding below District
spacing. wire_encoding_bench: real GJ338Bd derived canvases through
derive_at_metres, five encodings (raw rmp / bit-packed / RLE / PNG-per-field
/ PNG-of-packed) with measured bytes + encode/decode round-trips; corrected
raw density 6.00 B/cell. All #[ignore]d release tests.
Workshop gate measurements (2)(3)(4) for body-map-viewer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Priority-flood fill with basin grouping, topographic-saddle spill points,
moisture-governed endorheic classification (reuses the reserved
RIVER_DOWNSTREAM_TERMINAL sentinel), and Dijkstra overflow/carving. Pure
function of (elevation, sea_level, climate) — deterministic per D-010, no
stateful simulation. 15 unit tests incl. determinism proofs and direct
carving-mechanism verification; #[ignore]d release benches at 512x256 /
768x432 / 3840x2160 plus the 273-bodies-parallel production shape.
Workshop gate measurement (1) for body-map-viewer: settled hydrology is
VIABLE per body-open (~24 ms at 512x256; ~0.7-0.8 s all 273 bodies).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The apparent course-over-drawn-water contradiction resolved to two
reconciliation errors, neither in production: (1) the probe's first
reconstruction bypassed the request layer's wire clamp (Quarter n=32
serves as n=16 — build_district_window_layer trusts n verbatim by
doc); (2) the lead's capture read overestimated — the window frames
only the estuarine tail of a 213-station course (178 land stations
out of frame upstream; the visible water run is genuine Lake/
OpenOcean paint, RGB-verified against MORPHOLOGY_RGB_OPAQUE x
elevation-lightness) and the terminus sits ~240m from the painted
land->water crossing, not tens of km. Walk and paint agree at every
instrumented position (zero flip-flops across the monotonic 178-land/
35-water station sequence). New permanent test reproduces the exact
live window (real GJ380c + systems.db params, district (13195,-2383),
Quarter, clamped n=16; asserts 44 pts + Mouth as identity) and pins
the terminus-lands-in-painted-water invariant mechanism-agnostically;
revert-verified with an injected 500km positional slip. Probe
deleted. cargo test --lib 1878/1878.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The live acceptance question (Mouth flag apparently not surviving
crop) resolved to hypothesis (b): the terminus genuinely lay outside
both test windows — the Lendel mouth chord is ~108.5km, not the ~38km
estimated, so a District n=32 window centered on the cell OR the
midpoint misses the bisected waterline; a seaward-cell-centered
window provably ships Mouth. Hypothesis (a) — upstream-only crops
collapsing the flag — was FALSIFIED directly with a constructed
window (upstream anchor cut, true terminus in range -> Mouth ships;
crop reads only last_in). This test converts that probe into a
permanent pin with construction-sanity asserts, closing the blind
spot where the existing GJ1c acceptance test's bbox-derived window
always contains the whole course. Revert-verified: reintroducing a
first_in==0 requirement fails this test by name while the whole-
course test blindly passes — exactly the gap. Probe file deleted.
cargo test --lib 1877/1877.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The batch's real bug: extract_river_network computed the seaward
neighbor (nr,nc) to decide the MOUTH sentinel then discarded it, and
build_edges placeholder-pointed mouth edges at themselves — zero
chord, invent_course's degenerate 1-point return, resolve_mouth_
terminus dead code on real data, ALL real mouths resolving None, and
(because Ruling 3g retired the D/Q clip on the promise of real
termini) mouths vanishing at District/Quarter. The second instance of
the threw-away-the-answer anti-pattern Ruling 2b fixed for interior
pointers. Fix: river_seaward: Vec<(u16,u16)> on RiverNetwork
(additive, serde-default, parallel array; meaningful only at MOUTH
entries), captured in the same extraction pass; build_edges gives
Mouth edges the real one-D8-step chord. Permanent acceptance:
all_real_gj1c_mouths_resolve_to_mouth_terminus_not_none — 3/3, revert-
verified failing at the golden's first mouth (38,47). Mouth golden
coverage added (river_course_golden gains district_mouth/quarter_mouth
samples + non-degeneracy test; the previously Interior-only filter
gap closed). Tyre 1: the land-probe's dead dx/len*len arithmetic
replaced — station_spacing_m threaded through the crop path, probe
steps one real cell spacing, comment reconciled. Tyre 2: boxing
comment reattributed to variant-size balancing (Layer1Output retention
lives on TerrainAnalysisCache, not BodyWorldState). Hoshe #4:
cascade_golden's doc now states the attractor cascade accurately
(count-parity, not byte-identity — water_dist seeds from mouths).
Full cargo test green; goldens re-pinned deliberately; bench +3.0%.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tyre's two governance sentences on the D-226 course note: (a) the
Godot rasterizer floor makes per-class course WIDTH differentiation
inert at every shipping fit zoom — classes distinguish by opacity
alone until T-1175's per-vertex tapering; the 'trunk widest' promise
is design intent, not current pixels. (b) The pole-row edge-drain
branch is structurally unreachable; the real mechanism is interior
k<0 (revert-verification discovery). Plus the dangling cargo-fmt
reflow in the course-cost bench from the gate-bounce round.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A2 (river_course.rs): Stage A rung-independent valley-seeking control
path (chord/8 stations, k=5 bilinear-scored candidates + continuity
penalty); Stage B rung-indexed perpendicular warp on GLOBAL arc-length
(window-independence, Ruling 1e), band chord/2 down to min_wavelength_m
hard-truncate, sine taper to zero at anchors, amplitude min(8% chord,
half-cell) slope/class-scaled. SeedDomain::RiverCourse=17, distinct
salt. Wire: RiverCourse{edge_id,class,points,terminus} on
DistrictWindowLayer.courses (serde-default); bbox-culled, window-
cropped +1 station. TerrainAnalysisCache retains Layer1Output (the
gen_queue:626 discard, Ruling 4b). A3: mouth termination walks
stations sampling the window's OWN rung-consistent morphology verdict,
6-iteration bisect; land-at-anchor probes one segment then None;
EdgeDrain never probes. A5: near_perennial_water point-to-segment
predicate (D-239 §8 governed bands 1-3m class-scaled) threaded through
both batch and window paths; Region always false; never touches
moisture_q. Discipline closed: dormant zoom-ladder bench run + numbers
recorded in the design doc (District 3.009/1.785, Quarter 1.823
us/cell, Region window 0.617ms); course-cost bench CAUGHT a real
+12-36% per-cell riparian scan regression -> precomputed bbox O(1)
reject (60ns->2.2ns/call), final delta +3.4-6.9% at budget; three
determinism tests (overlapping-window byte-identity, cross-rung
amplitude bound, warp-stream cross-correlation r<0.3); goldens: window
sweep gained a verified course-bearing position (pure append), new
river_course golden at both rungs, believability verified unchanged.
Revert-verification discovered the pole-row branch is structurally
unreachable (flow_direction bounds-check) — the real edge-drain path
is k<0 flat-plateau; test fixture rewritten to exercise reality.
scale.rs stale comment fixed. Full cargo test green.
Tickets: T-1170, T-1168
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>