feat(ui): the Atlas ladder bottoms out at Quarter (D-255, T-1213)

Quarter becomes the deepest navigable rung. Block and Chunk leave the ladder.

The rule, from the amendment: the deepest Atlas rung is the one at which a
screen pixel shows one subtile. At the uniform 2x2 px display ratio a
3440x1440 window gives 540 gridunits on the short axis, so Quarter's 512 m
extent draws 0.948 m per gridunit -- about one voxel per gridunit and one
0.5 m subtile per pixel. Block (0.237) and Chunk (0.119) magnify beneath the
finest datum that can exist, and measured as exactly that on Ferrath: one
morphology zone, one vegetation class, an unbroken colour field. They were not
missing a feature; there was nothing left to show them.

CHUNK ITSELF IS UNTOUCHED. It remains D-243's 64 m stream/derive unit and is
where Phase 5 derives first-person walkable content -- D-012's
load-around-the-player is expressed in chunks. Block remains the 128 m
generator planning unit. Both keep their enum variants, their extent_m answers
and their wire vocabulary. What was retired is the claim that a MAP of one is
worth looking at.

DEEP_RUNGS moved with the floor, and this is the part worth reading twice. It
is a mandatory D-255(d) hardening: a per-body retention cap bounding how much
ground an exhaustive pan can hold resident at fine spacing. Left as
[Block, Chunk] it would have guarded rungs no client can request -- dead code
-- while the accumulation gap silently re-opened under Quarter, now the finest
navigable rung at ~0.95 m per gridunit. It is now [District, Quarter]. A
control that names its targets by rung has to follow the ladder when the
ladder moves.

Six tests pinned the old floor and were updated rather than deleted, since
each was protecting a real property: the clamp tests now clamp at Quarter, and
the disk-cache tests use Region for "shallow" (District is capped now) and
Quarter for "deep". One new test pins the distinction the change turns on --
the retired rungs are absent from RUNG_LADDER but still present in
RUNG_EXTENT_M, because viewability was retired, not vocabulary.

Also removes the four capture scenarios for the retired rungs, including the
two blank goldens that had been passing against blank captures.

1838 client tests, 0 failed. Server clippy clean, step_canvas suite green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 14:38:47 +02:00
co-authored by Claude Opus 5
parent b429f633e6
commit 8eb5cb9ff7
10 changed files with 212 additions and 86 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

+18 -9
View File
@@ -254,13 +254,18 @@ func test_capacity_sweep_never_touches_an_entry_under_budget() -> void:
func test_deep_rung_cap_evicts_oldest_deep_rung_entry_when_exceeded() -> void:
var cache := StepCanvasDiskCache.new(_test_root)
# Fill past the cap: MAX_DEEP_RUNG_ENTRIES_PER_BODY + 2 distinct Chunk
# Fill past the cap: MAX_DEEP_RUNG_ENTRIES_PER_BODY + 2 distinct Quarter
# windows for one body, backdating each so eviction order is
# deterministic (oldest last_read_at goes first).
#
# Quarter, not Chunk: DEEP_RUNGS became [District, Quarter] when the ladder
# floor moved (D-255 amendment 2026-08-07). Quarter is now the finest
# navigable rung and therefore the exhaustive-pan accumulation case this
# cap exists to bound.
var cap: int = StepCanvasDiskCache.MAX_DEEP_RUNG_ENTRIES_PER_BODY
for i in range(cap + 2):
cache.put("GJ1c", "Chunk", Vector2i(i, 0), Vector2i(64, 64), _stub_canvas({"i": i}))
var key := StepCanvasDiskCache.make_key("GJ1c", "Chunk", Vector2i(i, 0), Vector2i(64, 64))
cache.put("GJ1c", "Quarter", Vector2i(i, 0), Vector2i(64, 64), _stub_canvas({"i": i}))
var key := StepCanvasDiskCache.make_key("GJ1c", "Quarter", Vector2i(i, 0), Vector2i(64, 64))
cache._debug_backdate_entry("GJ1c", key, i, i) # strictly increasing "recency"
assert_int(cache.entry_count("GJ1c")).override_failure_message(
@@ -268,20 +273,24 @@ func test_deep_rung_cap_evicts_oldest_deep_rung_entry_when_exceeded() -> void:
).is_equal(cap)
# The two OLDEST (i=0, i=1) must have been evicted; the newest must survive.
assert_bool(cache.has("GJ1c", "Chunk", Vector2i(0, 0), Vector2i(64, 64))).is_false()
assert_bool(cache.has("GJ1c", "Chunk", Vector2i(1, 0), Vector2i(64, 64))).is_false()
assert_bool(cache.has("GJ1c", "Quarter", Vector2i(0, 0), Vector2i(64, 64))).is_false()
assert_bool(cache.has("GJ1c", "Quarter", Vector2i(1, 0), Vector2i(64, 64))).is_false()
assert_bool(
cache.has("GJ1c", "Chunk", Vector2i(cap + 1, 0), Vector2i(64, 64))
cache.has("GJ1c", "Quarter", Vector2i(cap + 1, 0), Vector2i(64, 64))
).override_failure_message("the most-recently-written deep-rung entry must survive").is_true()
## Region, not District: the ladder floor moved on 2026-08-07 and DEEP_RUNGS
## moved with it, from [Block, Chunk] to [District, Quarter]. District is now a
## capped rung, so it can no longer stand for "shallow" — Region is the finest
## rung the cap does not apply to.
func test_deep_rung_cap_does_not_apply_to_shallow_rungs() -> void:
var cache := StepCanvasDiskCache.new(_test_root)
var cap: int = StepCanvasDiskCache.MAX_DEEP_RUNG_ENTRIES_PER_BODY
for i in range(cap + 5):
cache.put("GJ1c", "District", Vector2i(i, 0), Vector2i(64, 64), _stub_canvas({"i": i}))
cache.put("GJ1c", "Region", Vector2i(i, 0), Vector2i(64, 64), _stub_canvas({"i": i}))
assert_int(cache.entry_count("GJ1c")).override_failure_message(
"District is not a DEEP_RUNGS member — the deep-rung cap must not evict it"
"Region is not a DEEP_RUNGS member — the deep-rung cap must not evict it"
).is_equal(cap + 5)
@@ -290,7 +299,7 @@ func test_deep_rung_cap_never_evicts_the_global_floor_entry() -> void:
cache.put("GJ1c", "Global", Vector2i.ZERO, Vector2i.ZERO, _stub_canvas())
var cap: int = StepCanvasDiskCache.MAX_DEEP_RUNG_ENTRIES_PER_BODY
for i in range(cap + 3):
cache.put("GJ1c", "Chunk", Vector2i(i, 0), Vector2i(64, 64), _stub_canvas({"i": i}))
cache.put("GJ1c", "Quarter", Vector2i(i, 0), Vector2i(64, 64), _stub_canvas({"i": i}))
assert_bool(cache.has("GJ1c", "Global", Vector2i.ZERO, Vector2i.ZERO)).override_failure_message(
"the Global floor entry must survive deep-rung cap eviction regardless of fill order"
).is_true()
+22 -4
View File
@@ -20,13 +20,30 @@ func test_rung_at_index_zero_is_global() -> void:
assert_str(StepCanvasTransport.rung_at_index(0)).is_equal(StepCanvasTransport.RUNG_GLOBAL)
func test_rung_at_index_five_is_chunk_the_deepest() -> void:
assert_str(StepCanvasTransport.rung_at_index(5)).is_equal(StepCanvasTransport.RUNG_CHUNK)
## The ladder bottoms out at Quarter (D-255 amendment 2026-08-07): the deepest
## rung is the one where a screen pixel shows one subtile, and Quarter's 512 m
## extent draws ~0.948 m per gridunit — about one voxel per gridunit at the 2x2
## display ratio. Block and Chunk drew 0.237 and 0.119 m per gridunit, below
## the finest datum that exists, so they magnified rather than informed.
func test_rung_at_index_three_is_quarter_the_deepest() -> void:
assert_str(StepCanvasTransport.rung_at_index(3)).is_equal(StepCanvasTransport.RUNG_QUARTER)
## Block and Chunk remain legal WIRE vocabulary (RUNG_EXTENT_M still carries
## them, and the server enum still has the variants) — they are simply not
## reachable by scrolling. This pins that distinction: the ladder does not
## offer them, whatever the index.
func test_retired_rungs_are_not_reachable_on_the_ladder() -> void:
assert_int(StepCanvasTransport.RUNG_LADDER.size()).is_equal(4)
assert_bool(StepCanvasTransport.RUNG_LADDER.has(StepCanvasTransport.RUNG_BLOCK)).is_false()
assert_bool(StepCanvasTransport.RUNG_LADDER.has(StepCanvasTransport.RUNG_CHUNK)).is_false()
# Still nameable — the retirement is of viewability, not of the vocabulary.
assert_bool(StepCanvasTransport.RUNG_EXTENT_M.has(StepCanvasTransport.RUNG_CHUNK)).is_true()
func test_rung_at_index_clamps_out_of_range_indices() -> void:
assert_str(StepCanvasTransport.rung_at_index(-3)).is_equal(StepCanvasTransport.RUNG_GLOBAL)
assert_str(StepCanvasTransport.rung_at_index(99)).is_equal(StepCanvasTransport.RUNG_CHUNK)
assert_str(StepCanvasTransport.rung_at_index(99)).is_equal(StepCanvasTransport.RUNG_QUARTER)
func test_index_for_rung_round_trips_every_ladder_entry() -> void:
@@ -49,7 +66,8 @@ func test_scroll_step_ascends_one_notch_at_a_time() -> void:
func test_scroll_step_clamps_at_the_deepest_rung() -> void:
assert_int(StepCanvasTransport.scroll_step(5, 1)).is_equal(5)
# Index 3 = Quarter, the floor since the 2026-08-07 ladder amendment.
assert_int(StepCanvasTransport.scroll_step(3, 1)).is_equal(3)
func test_scroll_step_clamps_at_the_global_opener() -> void:
+3 -1
View File
@@ -66,7 +66,9 @@ func test_scroll_rung_clamps_at_the_deepest_rung() -> void:
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
for _i in range(10):
v._scroll_rung(1, Vector2(400.0, 300.0))
assert_str(v.get_held_rung()).is_equal(StepCanvasTransport.RUNG_CHUNK)
# Quarter is the floor (D-255 amendment 2026-08-07) — scrolling past it
# would magnify below one subtile per pixel rather than show more.
assert_str(v.get_held_rung()).is_equal(StepCanvasTransport.RUNG_QUARTER)
func test_reset_to_global_returns_from_a_deep_rung() -> void:
-36
View File
@@ -179,20 +179,6 @@ func apply_setup(scenario_name: String, tree_root: Node) -> bool:
["gen_dw_moisture"]
)
"atlas_GJ338Bd_Block":
_setup_atlas_golden_shot(
tree_root,
{
"body_id": "GJ338Bd",
"body_radius_km": 6711.0,
"proper_name": "Arbour",
"terrain_reference": "wiki/star-systems/GJ-338B/bodies/GJ338Bd/heightmap.png",
},
{"system_id": "GJ 338B", "proper_name": "Arbour"},
"Block",
[]
)
"atlas_GJ820Bc_Global":
_setup_atlas_golden_shot(
tree_root,
@@ -247,15 +233,6 @@ func apply_setup(scenario_name: String, tree_root: Node) -> bool:
tree_root, _FERRATH, _CYGNI_B, "Quarter", [], _FERRATH_LAND
)
"atlas_GJ820Bc_land_Block":
_setup_atlas_golden_shot(
tree_root, _FERRATH, _CYGNI_B, "Block", [], _FERRATH_LAND
)
"atlas_GJ820Bc_land_Chunk":
_setup_atlas_golden_shot(
tree_root, _FERRATH, _CYGNI_B, "Chunk", [], _FERRATH_LAND
)
"atlas_GJ251c_Global":
_setup_atlas_golden_shot(
@@ -296,19 +273,6 @@ func apply_setup(scenario_name: String, tree_root: Node) -> bool:
["gen_dw_temp"]
)
"atlas_GJ445c-m1_Chunk":
_setup_atlas_golden_shot(
tree_root,
{
"body_id": "GJ445c-m1",
"proper_name": "Jinghu",
"terrain_reference": "wiki/star-systems/GJ-445/bodies/GJ445c-m1/heightmap.png",
},
{"system_id": "GJ 445", "proper_name": "Jinghu"},
"Chunk",
[]
)
_:
push_warning("VisualScenarios: unknown scenario '%s'" % scenario_name)
return false
@@ -58,8 +58,9 @@ extends RefCounted
## existing `put(sim_ttl_sec)` parameter — tracked in T-1190.
##
## **HARDENING (D-255(d), both mandatory):**
## (i) Per-body deep-rung retention cap — DEEP_RUNGS (Block, Chunk; the
## two finest, matching troblum-round2.md S3's specific pan-assembly
## (i) Per-body deep-rung retention cap — DEEP_RUNGS (District, Quarter as
## of the 2026-08-07 ladder-floor amendment; the two finest, matching
## troblum-round2.md S3's specific pan-assembly
## concern) are capped at MAX_DEEP_RUNG_ENTRIES_PER_BODY resident
## entries, independent of the Global floor and independent of the
## general Tier-2 byte budget — closes the D-226(d) accumulation gap
@@ -120,7 +121,15 @@ const SUB_GLOBAL_BYTE_BUDGET: int = 256 * 1024 * 1024
## bytes — capping entry count directly bounds how much ground a client can
## have simultaneously resident at fine spacing, which is the D-226(d)
## purpose (information content), not a byte-budget proxy for it.
const DEEP_RUNGS: Array = ["Block", "Chunk"]
## Re-pointed 2026-08-07 (D-255 ladder-floor amendment): was ["Block",
## "Chunk"]. Those two left the navigable ladder — a screen pixel there shows
## less than one subtile, so they magnify rather than inform — which would have
## left this cap guarding rungs no client can request, i.e. dead code, while the
## accumulation gap it exists to close re-opened silently under the new finest
## rungs. The control follows the ladder: District and Quarter are now the two
## finest, and Quarter at ~0.95 m per gridunit is exactly the fine-spacing
## exhaustive-pan case S3 raised.
const DEEP_RUNGS: Array = ["District", "Quarter"]
const MAX_DEEP_RUNG_ENTRIES_PER_BODY: int = 512
## (ii) Schema/version tag source: the game's `project.yaml` `version:`
@@ -20,14 +20,27 @@ const RUNG_QUARTER: String = "Quarter"
const RUNG_BLOCK: String = "Block"
const RUNG_CHUNK: String = "Chunk"
## Coarsest (0) to deepest (5) — the SIX levels D-255(a) names, index IS the
## rung. scroll_step()/rung_at_index() below are the only places this table
## is walked; every other consumer works in terms of the rung NAME (matching
## the wire's own bare-string vocabulary, never a raw integer sent over the
## bridge).
const RUNG_LADDER: Array = [
RUNG_GLOBAL, RUNG_REGION, RUNG_DISTRICT, RUNG_QUARTER, RUNG_BLOCK, RUNG_CHUNK
]
## Coarsest (0) to deepest (3) — the navigable ladder, index IS the rung.
## scroll_step()/rung_at_index() below are the only places this table is
## walked; every other consumer works in terms of the rung NAME (matching the
## wire's own bare-string vocabulary, never a raw integer sent over the bridge).
##
## BLOCK AND CHUNK ARE DELIBERATELY ABSENT (D-255 amendment 2026-08-07). The
## deepest Atlas rung is the one at which a screen pixel shows one subtile;
## below that a rung magnifies rather than informs. At the uniform 2x2 px
## display ratio a 3440x1440 window gives 540 gridunits on the short axis, so
## Quarter's 512 m extent is 0.948 m per gridunit — about one voxel per
## gridunit and one 0.5 m subtile per pixel. Block (0.237 m/gridunit) and Chunk
## (0.119) magnify beneath the finest datum that can exist, and measured as
## exactly that: one morphology zone and one vegetation class, an unbroken
## colour field.
##
## They remain valid WIRE vocabulary (RUNG_EXTENT_M and the server enum still
## carry them) — this is the list the user can scroll through, not the set of
## rungs that can be named. And the 64 m chunk itself is untouched as the
## generator's stream/derive unit: it is what Phase 5 builds first-person
## walkable content on. Only the map stops there.
const RUNG_LADDER: Array = [RUNG_GLOBAL, RUNG_REGION, RUNG_DISTRICT, RUNG_QUARTER]
## D-243 world-metre CELL SIZE for every FIXED rung — mirrors
## server/src/atlas/scale.rs's own constants exactly (REGION_M/DISTRICT_M/
+136 -5
View File
@@ -80,13 +80,31 @@ pub enum StepCanvasRung {
Region,
/// Rung 2 — 2,048 m spacing (D-243 `DISTRICT_M`).
District,
/// Rung 3 — 512 m spacing (D-243 `QUARTER_M`).
/// Rung 3 — 512 m extent (D-243 `QUARTER_M`). **The deepest NAVIGABLE
/// rung** (D-255 amendment 2026-08-07): at the uniform 2×2 px display
/// ratio it draws ~0.948 m per gridunit, about one voxel per gridunit and
/// one 0.5 m subtile per screen pixel — the floor at which a further step
/// would magnify rather than inform.
Quarter,
/// Rung 4 — 128 m spacing (D-243 `BLOCK_M`).
/// Rung 4 — 128 m extent (D-243 `BLOCK_M`). **Retired from the navigable
/// ladder** (D-255 amendment 2026-08-07): 0.237 m per gridunit is four
/// gridunits per voxel, so it can only draw one voxel larger. Measured on
/// Ferrath as a single morphology zone and a single vegetation class.
///
/// The variant is KEPT: it is still legal wire vocabulary, `extent_m`
/// still answers for it, and `BLOCK_M` remains the generator's 128 m
/// planning unit. Only the client's `RUNG_LADDER` stopped offering it.
Block,
/// Rung 5 — 64 m spacing (D-243 `CHUNK_M`), the deepest Atlas rung
/// (D-255(a): tile/voxel is Phase-5 in-world content, never an Atlas
/// rung).
/// Rung 5 — 64 m extent (D-243 `CHUNK_M`). **Retired from the navigable
/// ladder** (D-255 amendment 2026-08-07): 0.119 m per gridunit, eight
/// gridunits per voxel.
///
/// Retired from the MAP only. The 64 m chunk remains D-243's stream/derive
/// unit and is where Phase 5 derives first-person walkable content
/// ([D-012](#d-012)'s load/unload-around-the-player is expressed in
/// chunks) — it is arguably the most load-bearing unit in the cascade, and
/// nothing about it changes here. What was removed is the claim that a
/// *map* of one is worth looking at.
Chunk,
}
@@ -2469,4 +2487,117 @@ mod tests {
);
assert!(raw.settlement_id.iter().all(|&v| v == 0));
}
// -----------------------------------------------------------------
// T-1213 diagnostic — where the ladder goes flat
// -----------------------------------------------------------------
/// Per-field variance down the rung ladder on a REAL body, at the real
/// spacing, through the production canvas builder.
///
/// The descent ladder captured on 2026-08-06 showed every rung below
/// Global rendering a uniform colour field. This reports WHICH field is
/// flat and by how much, so the un-summarisation work targets a measured
/// gap rather than an inferred one — the captures show the symptom, this
/// shows which of the six derived fields produces it.
///
/// Reference numbers it exists to explain: Ferrath's heightmap is 1024x512
/// over a 38,089 km circumference = 37.2 km per source pixel. Global draws
/// at ~35.3 km/gridunit (about 1:1 with the source, and reads as a world);
/// Region at 0.379 km/gridunit is ~98x finer than any stored datum, and
/// District at ~0.0038 km/gridunit is ~9,800x finer. Everything below
/// Global is therefore invented, not sampled.
///
/// `#[ignore]`: runs the full cascade for a body. Diagnostic, not a gate.
///
/// ```text
/// cargo test --lib t1213_ladder_field_variance -- --ignored --nocapture
/// ```
#[test]
#[ignore = "diagnostic — full cascade for one body; run explicitly"]
fn t1213_ladder_field_variance() {
let body = "GJ820Bc";
let Ok((snapshot, params)) =
crate::atlas::believability::cascade_snapshot_for_body(42, body)
else {
eprintln!("skip: {body} not loadable");
return;
};
// `terrain_analysis` is transient and always None on a returned
// snapshot — re-derive through the same call the cascade used. See
// `believability::cascade_snapshot_for_body`'s doc.
let (_l1, ta) = crate::atlas::layer1::run_layer1_with_moisture(
&snapshot.heightmap,
crate::atlas::district_profile::derive_moisture_ceiling_q(&params),
);
let river_network = snapshot
.layer1
.as_ref()
.map(|l1| l1.river_network.clone())
.unwrap_or_default();
let placements = snapshot
.layer3
.as_ref()
.map(|l3| l3.placements.clone())
.unwrap_or_default();
let climate = ClimateConstants::default();
let seed = SeedChain::root(42).derive(crate::seed::SeedDomain::Body, 1);
// The land point the descent-ladder captures are anchored on (the
// cascade's highest-scoring placement on this body).
let center = (29_422_009_i64, -5_675_959_i64);
// A 1290x540 canvas is what a 3440x1440 window produces at the 2x2
// display ratio — the shape the captures were taken at.
let extent = (1290u32, 540u32);
for rung in [
StepCanvasRung::Global,
StepCanvasRung::Region,
StepCanvasRung::District,
StepCanvasRung::Quarter,
StepCanvasRung::Block,
StepCanvasRung::Chunk,
] {
let raw = build_step_canvas(
seed,
body,
&params,
&ta,
&river_network,
&placements,
rung,
center,
extent,
&climate,
0,
);
let spacing =
rung.spacing_m(raw.width, raw.height, params.body_radius_km.unwrap_or(0.0));
let uniq_u8 = |v: &Vec<u8>| {
v.iter().collect::<std::collections::BTreeSet<_>>().len()
};
let span_u8 = |v: &Vec<u8>| match (v.iter().min(), v.iter().max()) {
(Some(a), Some(b)) => *b as i32 - *a as i32,
_ => 0,
};
println!(
"{:8?} {:>9.1} m/gu {}x{} elev[uniq {:3} span {:3}] moist[uniq {:3} span {:3}] \
morph[uniq {:2}] veg[uniq {:2}] glac[uniq {:2}] courses {}",
rung,
spacing,
raw.width,
raw.height,
uniq_u8(&raw.elev_q),
span_u8(&raw.elev_q),
uniq_u8(&raw.moisture_q),
span_u8(&raw.moisture_q),
uniq_u8(&raw.morphology),
uniq_u8(&raw.vegetation),
uniq_u8(&raw.glaciation),
raw.courses.len(),
);
}
}
}
-20
View File
@@ -58,11 +58,6 @@
"live": true,
"description": "T-1157 curated Atlas golden (D-255): Arbour (GJ338Bd), Global rung, moisture overlay"
},
"atlas_GJ338Bd_Block": {
"ticks": 240,
"live": true,
"description": "T-1157 curated Atlas golden (D-255): Arbour (GJ338Bd), Block rung, no overlay"
},
"atlas_GJ820Bc_Global": {
"ticks": 240,
"live": true,
@@ -89,16 +84,6 @@
"live": true,
"description": "Descent ladder: Ferrath (GJ820Bc), Quarter rung, land-anchored, no overlay"
},
"atlas_GJ820Bc_land_Block": {
"ticks": 240,
"live": true,
"description": "Descent ladder: Ferrath (GJ820Bc), Block rung, land-anchored, no overlay"
},
"atlas_GJ820Bc_land_Chunk": {
"ticks": 240,
"live": true,
"description": "Descent ladder: Ferrath (GJ820Bc), Chunk rung (deepest), land-anchored, no overlay"
},
"atlas_GJ251c_Global": {
"ticks": 240,
"live": true,
@@ -113,11 +98,6 @@
"ticks": 240,
"live": true,
"description": "T-1157 curated Atlas golden (D-255): Edict (GJ244Ad), Global rung, temperature overlay — frozen planet_class pick"
},
"atlas_GJ445c-m1_Chunk": {
"ticks": 240,
"live": true,
"description": "T-1157 curated Atlas golden (D-255): Jinghu (GJ445c-m1), Chunk rung (deepest), no overlay — inhabited moon pick"
}
},
"flows": {