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:
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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/
|
||||
|
||||
Reference in New Issue
Block a user