step_canvas_disk_cache.gd: the Tier 2/3 store per D-255(d) and the
round-2 three-tier spec. Payload is the WIRE, pre-decode — store_var/
get_var round-trips the PNG-encoded PackedByteArrays natively, and
Image.load_png_from_buffer never runs in this file. One composite key
shared verbatim with Tier 1 (hash filenames for filesystem safety);
per-body index.json with malformed-index recovery (rebuild-or-discard,
never crash).
Three independent eviction mechanisms, exactly as ruled: rung-0 Global
carries a retention floor no sweep touches (now also threaded into
Tier 1 per the T-1182 handoff); Tier 2 geometry is byte-valid forever
and evicts only by time-since-last-visit (14d starting tunable, on
body-open) and LRU byte budget (256 MiB/body, 5-min coarse timer) —
two separate sweeps; Tier 3 sim-state TTL is wired and tested but has
no production caller yet (no sim-state field exists on
EncodedStepCanvas — the D-253 stub inheritance, documented).
Hardening per D-255(d), both mandatory: per-body deep-rung cap
(512 Block+Chunk entries, enforced synchronously in put(), floor- and
budget-independent — the ticket sanctions count-or-quota; count chosen
as the direct D-226(d) information-content proxy) and a schema/version
tag on every entry (project.yaml version via the existing
loading_screen line-scan idiom — D-192 co-ship makes the client
version the wire-schema version; exact-inequality mismatch = miss +
drop, NEVER decode, checked in both has() and get_canvas()).
Integration: request_now() checks Tier 2 on a Tier-1 miss (synchronous
promote), Ready responses write through to both tiers, Pending never
writes; viewer runs the visit sweep on body-open + the background
sweep on a 5-min timer.
30 new disk-cache tests + 7 request-integration + 3 sweep-wiring tests
(restart persistence, sweep independence both directions, cap
semantics, version-mismatch never-decode, corrupt-index recovery).
Full client suite 3,440/3,440, 0 orphans; cold-parse clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two-layer client rebuild per D-255(a)(b)(e), replacing the
_canvas.scale continuous-zoom model with one viewer, one path, all six
rungs:
- step_canvas_protocol.gd: StepCanvasRequest/Response codec against
the T-1181 wire contract — incl. the discovered png_bytes subtlety
(rmp_serde without serde_bytes emits a msgpack int-array, not bin;
decode repacks via PackedByteArray before load_png_from_buffer) and
the extent-echo rule (read the server-clamped extent, never assume
the requested one).
- step_canvas/ component: transport (six-rung ladder, cursor-anchored
scroll steps, edge-scroll/WASD pan with re-request on edge crossing,
hard reset-to-Global), RTT terrain layer (Image.set_pixel colorize
per the c1 measured ruling, texture.update reuse on step-cross,
NEAREST coarse / LINEAR fine per rung), unscaled screen-space
annotation sibling (courses + settlement markers at literal px),
in-memory LRU cache (Tier 1; T-1183 layers the disk tiers beneath),
request lifecycle (pending retry, staleness gate, extent echo).
- Full _canvas.scale retirement in the same change: the zoom-scaled
canvas model, the _zs compensation family, select_rung /
MAX_COVERAGE_M / compute_tile_grid, the orbital-mosaic-vs-window
two-path split, _view_zoom/_canonical_fit_zoom — 10 source files
deleted; their 14 test suites deleted with them (T-1157 dead-goldens
rule; replacement visual-capture coverage is re-scoped T-1157).
- Surviving surfaces kept per the ticket: atlas_window_cache.gd's LRU
shape (the ticket's named file atlas_window_tile_set.gd was the
retiring orchestrator; the real LRU shape lives in
atlas_window_cache.gd — cited in step_canvas_cache.gd), overlay
colors, legend/overlay-bar chrome, AtlasViewer descend geometry.
Determinism boundary per D-255(e): the client interpolates only within
the closed server-supplied input set. 7 new gdUnit suites (164 cases)
incl. a real extent-echo bug caught by its own test during
implementation. Full client suite green (exit 0) with the live-gated
suites running against a worktree server build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>