Files
settled-reach/docs/workshops/body-map-viewer/troblum-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

60 KiB
Raw Blame History

title, description, type, status, workshop, round, owner, decision_refs
title description type status workshop round owner decision_refs
Body Map Viewer — Troblum Round 2 (Adversarial Pass) Stress-test of the round-1 measured claims and cache/governance proposals against real play patterns — findings ranked by severity workshop active body-map-viewer 2 Troblum
D-166
D-226
D-227
D-243
D-203
D-225
D-192
D-005

Troblum — Round 2 Adversarial Pass

Read the brief (incl. the MEASURED appendix), lead-interview-1.md, round-1-notes.md, all four round-1 position files, and all four measurements/*.md docs in full before writing this. No {agent}-round2.md files existed at the time this was written — I flag anywhere that matters below.

I went into this expecting to find sloppy extrapolation. I didn't find much of that — the four measurement docs are unusually disciplined about the MEASURED/ARITHMETIC distinction, and every round-1 agent cites correctly. What I found instead is more interesting: two population-scale claims that were never actually tested at population scale (they were tested for throughput, not for the property being claimed), one arithmetic units slip, one real governance-purpose gap in the D-226(d) boundary that survives the letter of the rule, and a cache/eviction design that has never been pressure-tested against a play session shaped differently than "look at one step canvas once." Findings below, most severe first.


BLOCKING

B1. The "273 bodies" hydrology parallel-throughput bench is the SAME body solved 273 times — it proves nothing about cliff rarity across the population, and the workshop is about to file a Phase-4 ruling that leans on "rare" as a population-level property

The claim as stated: T-1177's headline: "all 273 bodies Rayon-parallel ~0.70.8s", cited by Dudley (Q1, Q4), and Tyre's cliff ruling explicitly says "real planetary heightmaps beyond the one body sampled may differ" but still files "Phase-4 Atlas scope, not deferred" on the strength of a rarity finding whose own caveat says it's measured on "one real body (GJ1c) plus two synthetic gradients."

What actually breaks it: I read the bench source directly (server/tests/hydrology_equilibrium_bench.rs:233-260, bench_parallel_273_bodies_at_512x256). It calls gj1c_512x256() once, then solves that identical elevation array 273 times in a par_iter:

let (elev, sea_level) = gj1c_512x256();
let body_count = 273usize;
let total_basins: usize = (0..body_count)
    .into_par_iter()
    .map(|_| {
        let result = solve(&elev, 512, 256, sea_level, default_climate());
        result.basins.len()
    })
    .sum();

This is a throughput measurement (can the Rayon pool solve 273 independent jobs of this size in under a second — yes) wearing a population-diversity measurement's headline ("all 273 bodies"). It answers "is per-body-open hydrology affordable" (yes, cleanly) — it does not answer, and was never designed to answer, "does cliff carving stay rare across 273 distinct real terrains." The 273-basin-count sum reported (total_basins) is 273× GJ1c's own 68 basins — a tell, if anyone checks it, that this is one terrain replayed, not 273 terrains sampled.

Nobody in round 1 mis-states this — Dudley's text says "512×256 (real GJ1c working grid)" for the single-body cost row and correctly separates it from the parallel-throughput row, and Tyre's caveat is honest about the sampling limitation. But the round-1 notes and the lead interview both let "all 273 bodies" stand unqualified as a headline in the appendix table, and the cliff ruling (already ratified by silence for the representation, and provisionally ruled Phase-4 by Tyre) is exactly the kind of decision where "how often does this fire across the real population" is load-bearing for the wire-cost argument that makes Phase-4-not-Phase-5 attractive in the first place ("nearly free... because it's a mostly-zero field").

The mandate's own hint turns out to be directly actionable, and cheaper than advertised. The brief's Troblum question asks "is there a cheap pre-filing check (run the solver across the real 273-body population — the bench exists and takes ~0.8s)?" — the bench that exists does NOT do this, but a real one is nearly free to build from what's already checked in:

$ find wiki/star-systems -iname heightmap.png | wc -l
267

267 real heightmap PNGs are already committed (close enough to "273 inhabited bodies" that the six missing are very unlikely to change the finding qualitatively). gj1c_512x256() already shows exactly the load/downsample pattern needed (load_heightmap_png + .downsample(512,256)); a population survey is: loop the 267 paths, load+downsample+solve each, count cliff_edge.iter().filter(|&c| c).count() per body, report the distribution (bodies-with-zero-carves vs bodies-with-nonzero, and the max carved-cell count seen). At ~24ms/body single-threaded (the GJ1c number) or parallelized across the Rayon pool the same way the throughput bench already demonstrates, 267 REAL bodies solve in well under the same ~0.70.8s ballpark. This is not a "more research needed, defer the decision" finding — it's "run this one already-half-built script before filing the cliff D-record," and it costs minutes, not a round-3.

Severity and why BLOCKING, not SERIOUS: the cliff ruling is about to be filed as a D-record amendment with "rare, cheap to carry" as its central argument for Phase-4 inclusion. If the real population survey finds even a double-digit percentage of bodies with genuine carving (plausible — GJ1c's tectonic_class/hydrosphere params are one point in a parameter space that explicitly includes Volcanic/high-relief bodies per D-239 §5's morphology gates, and D-239 §1 explicitly flags RIVER_THRESHOLD as eventually per-body-class-derived, not the fixed 200 this prototype borrowed), the wire- cost argument doesn't just weaken, the field stops being "mostly zero" and Araminta's sparse-list encoding choice (near-free at near-zero occupancy) needs re-costing at whatever the real occupancy rate turns out to be. Filing now on the untested assumption risks a silent contradiction the moment someone actually looks at a mountainous body's Atlas map and it should show gorges that a "rare" assumption undersized the format for.

What resolves it: run the population survey (concrete, minutes of work, harness 90% exists) before the D-record for the cliff ruling is filed. Report back: (a) fraction of the 267 real bodies with ≥1 carved cell, (b) max carved- cell count on any single body, (c) whether any body's carve count is large enough to threaten the "mostly zero" premise Araminta's sparse-list sizing assumes. If the finding holds (most bodies still zero, a minority nonzero but bounded), the existing ruling stands and gets a stronger evidence base for free. If it doesn't hold, better to know before filing than after.


B2. "Cost does not gate any step in the ladder" is true for derivation but silently assumes independent re-derivation at every step — the one architecture question everyone flagged as unmeasured (seed-chaining vs consuming the coarser output) is exactly the assumption every cost number in the brief depends on, and it is explicitly still open

What breaks it: Dudley states this cleanly himself (Q3, "One thing I did NOT measure..."): every ns/cell number in T-1178/T-1154 assumes each step re-derives independently from (seed, position), not that a finer step consumes a coarser step's already-computed values as input. This is flagged as "an open data-flow question for round 2," not resolved by anyone, and it does not appear in any of the four round-1 documents as a ruled item — it's listed in round-1-notes.md's OPEN-FOR-SYNTHESIS item 8, still open at the time I'm reading this.

Here's why this is BLOCKING rather than a tidy loose end: the two candidate architectures have different scaling shapes under step-thrashing (my mandate's own named stress scenario), and nobody has priced the one the outline's own text seems to prefer. Re-read Jeroen's outline verbatim: "This at the same time serves as seed information for the deeper cascade." That's not "the coarser step happens to also be re-derivable independently" — it reads as an intentional data-flow claim that the coarser canvas functions as seed input for the next tier. If that's the intended architecture (not just Dudley's simplifying assumption for benching purposes), then:

  • Every measured cost number in ①②③ prices the WRONG architecture. A step that consumes the coarser tier's output as an input needs that coarser tier resident/computed first — which either means step-N+1 has a hard dependency edge on step-N (serializing what's currently modeled as independent parallel derivation), or it means the "seed information" framing is loose language for "informs the RNG stream," which is a different and much smaller claim than "consumes the values."
  • Step-thrashing (my mandate's named scenario) has opposite costs under the two models. Independent re-derivation: zooming out then back in re-derives step-N from scratch (measured: 63.7ms1.8s depending on canvas size) — cache-miss cost is a flat per-step number. Consuming-coarser-output: zooming out then back in either (a) re-uses the still-cached coarser output as an input, making a re-zoom-in CHEAPER than the numbers in this brief suggest, or (b) if the coarser tier itself got evicted (see B3/S1 below, storage-eviction is now a real design axis), the finer tier's re-derivation chain-reacts backward through however many rungs got evicted, which is a cost story NOBODY has measured and that gets worse, not better, exactly in the play pattern the mandate asks me to stress (a player who steps in-out-in-out while exploring, potentially re-triggering upstream rungs).

This is not a hypothetical distinction — it changes what "measured, not extrapolated" means for the whole appendix. If round 2 or round 3 lands on the consuming-coarser-output model (which reads as closer to what the outline actually asked for), literally every ①②③④ cost number needs a footnote at minimum, and possibly a re-bench, because none of them price a dependency chain.

Severity: BLOCKING because it's not a refinement of an accepted architecture — it's a fork in the architecture itself that the brief's own Expected Output 3 ("the step ladder... deepest step's canvas policy") cannot honestly be finalized without resolving. Filing the D-record for "cost does not gate any step" without this resolved risks the record citing numbers that don't describe the shipped system.

What resolves it: explicit round-2/round-3 ruling: does a finer step's derivation (a) call derive_at_metres(seed, position, finer_spacing) independently, treating the coarser canvas as display-continuity input only (Dudley's assumption, what's actually been measured), or (b) literally sample/ consume the coarser step's resolved field values as part of its own derivation (the "seed information for the deeper cascade" reading)? If (b), a fresh cost pass is needed before the ladder table in Expected Output 3 can cite the existing numbers as authoritative for that shape.


SERIOUS

S1. Evict-then-revisit cost spikes are real and un-costed — Jeroen's storage-eviction amendment interacts with the TTL(detail,time,distance) formula in a way nobody has priced end-to-end

The scenario (my mandate's #2/#5): Jeroen's lead-interview ruling adds storage-eviction on time-since-last-visit as a second, independent axis from staleness-eviction — "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." Good instinct, but nobody has run the number on what a revisit costs once eviction actually fires.

The arithmetic (grounded in T-1179's measured derive+encode numbers): a single 330K-cell district-rung canvas costs 74.8ms derive + 5.41ms PNG-encode = 80.2ms to regenerate server-side (before wire transfer and client decode/upload, which measurement ④/⑤ show add a further ~4-10ms each). A player who explored, say, 20 distinct district-rung windows of a body during one long-ago visit, gets storage-evicted on time-since-visit, and returns: 20 × 80.2ms ≈ 1.6 seconds of pure server derive+encode cost concentrated into the moment they re-open that body's Atlas view — before wire and client costs are even added. That's not catastrophic, but it's also not "snappy re-navigation," which is the entire stated purpose of the "always keep global" instinct this eviction rule is explicitly carved out from. And this is the OPTIMISTIC case — District rung. A player who was deep-stepping (Block or Tile rung, viewport-sized canvases) across many locations before abandoning the body has a much larger number of small entries to re-derive, and nobody has counted how many viewport-sized canvases a typical "explore a city" session generates.

What nobody has specified, and needs to be specified before this ships:

  1. Does storage-eviction apply per-entry (fine-grained, LRU-by-canvas) or per-body (coarse, "haven't opened this body's Atlas in N days, drop everything but global")? Stig's FileAccess design (round 1, §4) says "two-tier eviction... geometry entries get LRU + a retention floor at step-0" — that's per-entry LRU, which means a body a player visits occasionally (not "never goes back to," but not "frequently" either) could have some of its sub-global entries evicted and others not, producing a partial-revisit cost that's neither the full 20-canvas number above nor zero — an unmeasured middle case that's actually the MOST common real pattern (players revisit systems they've been to before, irregularly, not never/always).
  2. Does the TTL formula's distance_decay term fight the storage-eviction rule? Dudley's TTL formula (ttl = BASE_TTL[rung] × time_decay(age) × distance_decay(distance_from_focus)) already decays entries far from current focus faster. If storage-eviction is a second, independently- timed sweep on top of that TTL, a body the player is CURRENTLY navigating (high focus, TTL should be long) could still hit a storage-eviction sweep timed off calendar/session time rather than in-session focus, evicting an entry the TTL formula would have kept. Nobody has specified which axis wins, or whether they're the same mechanism wearing two names (round-1 notes doesn't resolve this either — it's absent from the OPEN-FOR-SYNTHESIS list entirely, which itself is a gap: this should have been item 11).

Severity: SERIOUS, not BLOCKING, because the base numbers (80ms/canvas regen) are cheap enough that even an unoptimized worst case doesn't "catch fire" — but it's serious because the FEEL goal ("atlas navigation snappy after first calc," Dudley's own words) is explicitly what this mechanism could undermine for exactly the play pattern (occasional revisits) that's most common, and nobody has written down the eviction granularity or the two-axis interaction rule.

What resolves it: round-2/3 synthesis needs to state explicitly: (a) eviction granularity (per-canvas vs per-body), (b) which axis (TTL distance- decay vs storage-eviction time-since-visit) is authoritative when they disagree, (c) a worst-case revisit number using whatever granularity is chosen, computed the way I did above but for the actual chosen unit.


S2. The courses-inclusive rate is measured ONCE, at light density (18 courses), on ONE body — every step-thrashing/deep-pan cost projection implicitly assumes this generalizes, and the workshop's own document says not to generalize it

What the measurement actually licenses, read carefully: T-1178's own text is explicit and correct about the limits of what it found — I want to confirm this is NOT itself a finding of extrapolation (the document is honest about its own scope), but flag that the workshop's use of the number downstream risks over-generalizing what the document itself carefully scoped.

The 195.0 ns/cell "courses-inclusive, real density" number comes from one window on one body (GJ1c, district (7520, -2932), 18 courses in a 331,776-cell window) — chosen specifically because it's the densest course window anyone happened to measure. The document's own H3 finding says the headline table's square-path numbers are courses-inclusive but SPARSE (3-10 courses), and explicitly warns: "The courses-inclusive rate at REAL production course density is covered only by Cross-check 1... cite that number, not the headline table, for a courses-representative rate."

That's good discipline — but it means the entire ladder's cost story for a river-delta body, a body with many parallel drainage channels, or a densely riverine biome (all real terrain classes D-239's morphology gates explicitly support — BraidedDelta, MeanderReach, AlluvialPlain families) is projected from one 18-course sample on one body, not from a density distribution across real terrain types. 18 courses in a 331K-cell window is "nearly 2× the synthetic body's density" per the document's own framing — but "2× the driest measured case" is not the same claim as "representative of the densest real case." A body whose morphology leans heavily toward BraidedDelta/MeanderReach (the exact families D-239 §5 names as real, selectable outcomes) could plausibly carry courses at meaningfully higher density than GJ1c's 18-in-331K, especially at the deep Tile/Block rungs where a dense river network's tributaries are all individually resolvable (the deep-step 83K bench explicitly EXCLUDES courses entirely, per H2 — so the one number closest to "the actual deepest, most course-dense scenario" is the one number this whole measurement set has zero data on).

Why this matters for step-thrashing specifically (my mandate's named scenario): a player panning rapidly around a river delta at Block/Tile spacing generates many step canvases in quick succession, each paying the per-cell course cost. The <5% bound is well-established at District spacing and light-to-moderate density — it has never been checked at Block/Tile spacing (where course geometry is proportionally a larger fraction of a much smaller viewport) or at delta-class density (where course COUNT, not just per-course cost, could be several multiples of GJ1c's 18).

Severity: SERIOUS not BLOCKING. The <5% bound has enough headroom (measured against a ~5ms District-cap baseline) that even a 3-4x density multiplier at a river-dense body likely stays affordable — this isn't a "computer catches fire" risk. But it is a real, named gap in the "measured not extrapolated" claim the whole batch prides itself on, specifically at the one spot (deep-step, high-density river terrain) where the excluded cost is least likely to stay proportionally small.

What resolves it: one additional bench — deep-step (83K-cell, Block or Tile spacing) window on a body selected for high course density (a BraidedDelta or MeanderReach-dominant body, if the catalog can be queried for morphology-family distribution; failing that, a synthetic river network authored to be denser than GJ1c's). Cheap to add given the harness already exists; closes the one density/spacing combination the current appendix has literally zero coverage of.


S3. The D-226(d) whole-body prohibition survives the LETTER at every single step, but the client-side cache accumulation mechanism has no structural ceiling — the purpose of the rule (no metre-resolution whole-body derivation) is defended only by "nobody will pan that much," not by construction

The stress test my mandate asked for, run concretely: Dudley and Tyre's independently-argued convergence on viewport-sized canvases is correct and I have no finding against the SERVER-side policy — it holds cleanly, and the 17ms/83K-cell number genuinely is never a whole-body derivation on any single request. That part clears.

But the mandate specifically asks whether "pan-assembled coverage over time amounting to whole-body at fine spacing" is a real boundary risk given client-side cache accumulation (Stig's FileAccess store, premise 9). Here's the number: covering GJ1c's own 512×256-district working-grid extent (1,048.576 km × 524.288 km — the equirectangular working-grid footprint, a reasonable proxy for body coverage) with deep-step viewport tiles (216m × 384m, the measured 82,944-cell shape) requires ~6.6 million tiles, at ~156 KB PNG-encoded each (interpolated from T-1179's per-cell PNG rate), totaling ~985 GB on disk to fully assemble one body at 1m spacing via client-cached viewport canvases.

That number is obviously never going to happen by accident in normal play — which is exactly why I'm calling this SERIOUS, not BLOCKING: no player is going to pan a Tile-spacing viewport across 6.6 million distinct windows. But "obviously not by accident" is a practical-infeasibility argument, not a structural one, and the round-1 documents (Dudley's and Tyre's both) argue the viewport-sizing rule as a governance necessity, i.e., as the thing that keeps the design "legal by construction." It is legal by construction server-side (no single request ever asks for whole-body coverage) — it is legal only by practical improbability client-side, once a disk-backed, retain-forever-until-evicted cache is added on top. If a determined player (or a QA/agent harness doing exactly the kind of automated systematic sweep D-226(4)'s AtlasAgentInterface explicitly builds for) methodically panned a body at Tile spacing to completeness — slow, but not physically prevented by any mechanism in this design — the client's own disk cache would, over time, assemble the exact near-whole-body metre-resolution artifact D-226(d) exists to forbid. It would just be assembled as N discrete files rather than one canonical canvas, which is a difference of packaging, not of information content — and the rule's stated purpose (avoid a whole-body metre-resolution planetary map layer existing) is about information content, not file count.

This is exactly the gap the mandate asked me to name: does accumulation violate the rule's PURPOSE even if not its letter? My answer: yes, in principle, though the practical risk is low given the tile count required. The more concrete risk isn't "a player does this for fun" — it's the agent-navigable QA channel D-226 item (4) already built (AtlasAgentInterface, observe/act, described explicitly as turning "human-eyeball review into an agent-automatable QA sweep across the whole Reach"). An automated sweep is precisely the actor most likely to do a systematic, exhaustive pan — and if that sweep's client-side cache is retained (the "always keep global" + LRU-with-retention-floor design does NOT explicitly exclude an automated client from accumulating sub-global entries without bound), a QA run against one body at fine spacing could, over enough wall-clock time, produce the forbidden artifact as an unintended side effect of testing, sitting quietly in user://atlas_cache/.

Severity: SERIOUS. Doesn't block shipping the ladder (the server-side policy is sound and the practical risk from normal play is genuinely low), but it's a real gap between letter and purpose that a workshop explicitly concerned with "not just cost, a governance boundary" (red flag 3's own framing) should close on paper, not leave to improbability.

What resolves it: state explicitly, as part of the D-226(d) amendment text, that the prohibition is a per-request/per-derivation constraint, not an aggregate-storage constraint — and separately, add an explicit cap on client-side cache retention at deep rungs (a maximum resident tile count or disk quota per body at Tile/Block spacing, independent of the "retention floor at step-0" rule that already exists for the global tier). This turns the current "improbable in practice" defense into an actual structural ceiling, which is the same discipline the rest of this workshop already applies everywhere else (measured numbers, not vibes).


S4. Sim-state determinism for frozen/flooded is real (D-228 already answers "what serves them"), but the TTL-split's staleness boundary depends on a "phase" granularity nobody in this workshop has named, and the water-height mechanism is NOT wired to the map yet

What I verified clears (good news first, per my mandate's instruction to report clean findings explicitly): the "are frozen/flooded actually deterministic per (seed, sim-time)?" half of my mandate's question 5 checks out cleanly. D-228 (already-filed, not a round-1 invention) states the mechanism precisely: water-height is "a region property computed once per phase (not per tile, not per frame)... a pure function... recomputed on phase change" — a seasonal term phased continuously by latitude plus an optional tidal term. This is genuinely deterministic given (seed, region, phase) — no RNG, no accumulation, matches D-010. Cleared: sim-state components for water-height are deterministic per the existing, already-filed D-228 model — this is not a new risk the body-map-viewer workshop introduces.

What is NOT cleared, and is a real gap for the TTL-split ruling (Jeroen's lead-interview #2): "recomputed on phase change" begs the question this workshop needs an answer to and hasn't produced one: what triggers a phase change, and at what granularity does the client's TTL need to re-request to stay non-stale? D-228's own text names two clock terms (seasonal — a "year clock," continuous by latitude; tidal — a lunar/day clock, only present with a moon) but "computed once per phase" doesn't say how long a phase lasts in real sim-time, and nothing in the four round-1 documents or the measured appendix touches this. Jeroen's own TTL hint ("maybe shorter ttl on the climate sim state components") is a directional instinct, not a number — and it can't be turned into one without knowing the phase cadence.

The concrete failure mode this produces: if a phase is short (say, a tidal term on a body with a fast-orbiting moon — plausible given the system catalog includes many-moon systems) and the client's TTL is tuned assuming a seasonal-length phase, a player who holds a view over a coastal/tidal-flat gridunit across a phase boundary sees stale flooded/dry state rendered past its validity window — not a crash, not data corruption, just a quietly wrong map for however long the TTL overshoots the real phase cadence. The inverse failure (TTL too short) re-requests a sim-state plane that hasn't actually changed, which is wasted wire/derive cost but not a correctness bug — asymmetric risk, meaning erring toward "too short" is the safe default, but nobody has stated that as a design rule either.

Severity: SERIOUS, not BLOCKING — the determinism substrate is sound (the good-news half above), and this is a tuning-parameter gap, not an architecture gap. But it's a real gap: Jeroen explicitly ruled the TTL-split model as the working answer at lead-interview-1, and the model as ruled has an unfilled variable (phase cadence) that determines whether it actually holds in play.

What resolves it: name the phase cadence (or the range of cadences across body types — tidal-locked/fast-moon bodies vs moonless/seasonal-only bodies plausibly need different BASE_TTL values for the sim-state plane, similar to how Dudley's own BASE_TTL[rung] is already per-rung) before finalizing the TTL formula's sim-state branch. This is a Dudley/Araminta follow-up, not something I can resolve from the measured appendix — flagging it as an unanswered input the round-2 synthesis needs, not asking for a re-bench.


S5. The disk-backed client cache has no schema/version field on cached entries — the one place D-192's "client+server always co-ship, no version skew" guarantee genuinely does NOT hold

What I checked, and why this is a real gap not a nitpick: D-192 (already filed, confirmed by direct read) explicitly drops the protocol version handshake on the rationale that "our actual deployment is a subprocess: the Godot client launches the Rust server it was built with. They are always in sync at runtime." That's true for the LIVE wire protocol, and it means my mandate's item 6 ("version skew between client and server during the transition") is mostly a non-issue for the tagged-envelope migration itself — there is no live-network deployment where an old client talks to a new server.

But Stig's round-1 disk-cache design (§4, ratified by silence at lead-interview-1 as part of "cache composition... compose, don't compete") breaks exactly this guarantee, and nobody has named it. A disk-backed, self-cleaning, user://atlas_cache/-resident cache persists across game updates by construction — that's the entire point of a persistent cache (survive process restart, survive session boundaries). D-192's "always in sync at runtime" argument is about the LIVE client-server pair in one running process; it says nothing about a cache file written by version N of the game being read back by version N+1 after a patch changes the wire schema (a new dense field, a changed enum discriminant range, a bumped SCHEMA_VERSION- style change to the payload shape itself). I checked Stig's cache design text directly (stig-round1.md §4) for any versioning discipline on cached entries — there is none: the index schema he specifies is {written_at, last_read_at, kind: geometry|sim_state, size_bytes}, with no field naming which wire-schema version produced the cached bytes.

The concrete failure mode: a game update changes DistrictWindowLayer's field set (adds the cliffs sparse list this very workshop is about to introduce, or bumps an enum's discriminant range per T-1150's own precedent of "unknown values fall back, never trusted from the wire"). A player who has a warm disk cache from before the update opens the Atlas. If the client blindly deserializes the stale-schema cached bytes as if they were the new schema (the most likely naive implementation, since the cache's whole selling point is "skip the fetch, decode from disk"), this is either a hard decode error (best case — the mismatch is caught) or, worse, a silent misinterpretation of old bytes as new fields (worst case — exactly the class of bug D-225's own 2026-06-12 amendment was written to prevent for the LIVE wire, extended here to the DISK format, which nobody has extended the same discipline to).

Severity: SERIOUS, not BLOCKING — this is a real, fixable gap, not an architecture-breaking one, and the fix is cheap (one extra field). But it's exactly the kind of "invisible until it's wrong" decision Araminta's own round-1 opening line warned about, applied to a part of the design that genuinely does cross a version boundary D-192 was written to assume away everywhere else.

What resolves it: add a schema/version tag to every cached entry (a generator_sha-style stamp, cheap to compute, or simply the game's own project.yaml version string) at write time, and a check at read time — mismatch = treat as cache miss, re-fetch, don't attempt to decode. This is a small addition to Stig's already-designed index shape, not a redesign, and it should land in the same round-2/3 pass that finalizes the cache store rather than as a later patch once someone hits a stale-cache decode bug in the wild.


NOTE

N1. Dudley's "~174 MB" global-tier figure is decimal-MB (SI, 1000×1000), not MiB (1024×1024) — internally consistent but worth flagging before it gets budgeted against actual RAM/disk allocation numbers

Checked the arithmetic directly: Dudley's own measured per-body numbers (1,990,693 bytes raw / 638,382 bytes PNG-encoded at 330K cells, T-1179) times 273 bodies:

  • Using MiB (1024²): 273 × 638,382 / 1024² ≈ 166.2 MiB
  • Using decimal MB (1000²): 273 × 638,382 / 1000² ≈ 174.3 MB ← matches Dudley's stated "~174 MB" exactly

Not an error — the arithmetic is internally consistent under the decimal-MB convention, and it happens to also match red flag 2's own pre-measurement "~440 MB at 5×5" estimate reasonably (both used the same convention, so they compare correctly to each other). Flagging only because the moment this number gets used to size an actual memory allocation or du-reported disk budget, the ~8MB gap (166 vs 174) between conventions is exactly the kind of silent unit-drift that causes a "why doesn't the number match what du -h shows" ticket three months from now. Recommend the filed D-record state the convention explicitly (SI decimal MB) the first time this number appears.

No action needed beyond a one-line convention note in the filed record — this is the "clean bill" item my mandate asked me to report explicitly when a claimed number checks out. The ~174 MB figure IS what Dudley's own measured inputs produce, under a stated convention.

N2. Region-as-step-0 is still unconfirmed by Araminta (round-1-notes item 9) — I have no araminta-round2.md to check against, flagging that this is still open at time of writing

Per the mandate's instruction to note what I didn't see: no {agent}-round2.md files existed in the workshop directory at the time I read it. Tyre's §1e flagged "Region likely IS the global/step-0 rung, worth confirming" as carried into round 2. If this is still unconfirmed when synthesis closes, it directly affects two of my own findings above — S3's viewport-tile-count arithmetic assumed the deep end of the ladder starts from a District-scale "global" tier's working-grid extent as the body-coverage proxy, which is a reasonable stand-in either way, but the exact numbers would shift slightly if Region turns out not to be step-0. Not a finding against anyone's work, just noting the input I was missing.

N3. Cleared: the courses-cost bound itself (<5% at District-cap, the one number actually stress-tested at real density) holds up

Distinct from S2 above (which is about the SCOPE of what's been measured, not its accuracy) — I directly re-checked the one courses-inclusive-at-real- density number that exists (Cross-check 1, T-1178): 195.0 ns/cell vs the synthetic fixture's 192.0 ns/cell is a 1.56% delta, correctly described as "within 2%." The arithmetic is right, and the <5% District-cap bound (+0.090.21ms against ~5ms) is 1.84.2%, also correctly inside the stated threshold. No finding here — citing this as the "clean bill" companion to S2, since S2 is about what ISN'T covered, not about the one number that is.


Summary table

# Severity Finding Resolution cost
B1 BLOCKING "273 bodies" hydrology bench is 1 body × 273 — no real population-scale cliff-rarity survey exists Minutes — harness 90% exists, 267 real heightmaps on disk
B2 BLOCKING Seed-chaining data-flow (independent re-derive vs consume-coarser-output) is unresolved and changes every cost number's validity Architecture ruling, then possibly a re-bench pass
S1 SERIOUS Storage-eviction × TTL-distance-decay interaction unspecified; evict-then-revisit costs ~80ms/canvas × N canvases, uncosted at realistic N Specify eviction granularity + axis precedence
S2 SERIOUS Courses-inclusive rate measured once, light-ish density, one body — zero coverage at deep-step + high-density-river combination One more bench, harness exists
S3 SERIOUS D-226(d) holds by letter at every step but not by purpose against unbounded client-cache accumulation (~985GB to fully tile one body — improbable but not structurally prevented) Add explicit per-body deep-rung cache cap; state prohibition as per-request not aggregate
S4 SERIOUS Sim-state determinism substrate is sound (CLEARED) but phase-cadence for TTL sizing is an unfilled variable Name phase cadence(s) per body class
S5 SERIOUS Disk cache has no schema/version tag — the one place D-192's "always in sync" guarantee doesn't hold (cache persists across updates) One field addition to Stig's index schema
N1 NOTE ~174MB figure is decimal-MB not MiB — internally consistent, state convention explicitly One-line note in filed record
N2 NOTE Region-as-step-0 still unconfirmed at time of writing (no round-2 files existed yet) Informational
N3 NOTE Courses <5% bound itself is accurate where measured (cleared) None — clean bill

Top-line read for the lead interview: nothing here says "computer catches fire" or "the architecture is wrong." Everything here says "two claims are resting on measurements that don't cover what they're cited for" (B1, B2) and "three real interaction effects between mechanisms that were each individually well-designed haven't been costed together" (S1, S3, S5). All five of the serious-or-worse findings have cheap, concrete resolutions — none require walking back a round-1 position, and B1 in particular is likely to make the cliff ruling's evidence base stronger, not force a reversal, once run.


ADDENDUM (post-round-2-files)

All four round-2 files (dudley-round2.md, araminta-round2.md, stig-round2.md, tyre-round2.md) now exist and have been read in full. Short pass per the coordinator's four items. Verifying my own findings against them, not re-litigating what's already settled.

1. Finding B2 (seed-chaining fork) — RESOLVED. Dudley's ruling closes it cleanly, and I verified the mechanism against source, not just his prose.

Dudley's round-2 §(b) ruling: independent re-derivation from (seed, position); a finer step may call a coarser rung's derivation FUNCTION, never read a cached RESPONSE. I checked this against the actual code rather than taking the argument on faith, because "this closes the cost-shape concern" is exactly the kind of claim that needs verifying, not just reading.

Read server/src/atlas/district_profile.rs:1705-1732 directly — derive_at_metres (the function every T-1178/T-1154 bench calls) computes region_baseline_c via region_profile::region_baseline_at_district(...) inline, every call, with the comment stating explicitly: "No pre-built cache here — the on-demand path derives the four surrounding region baselines directly. Pure, deterministic, cheap." This is not a generalization from a different code path — it is the exact function every measured ns/cell number in the appendix already exercises. So Dudley's claim that "my own cost numbers... are validated as the correct model" isn't an assertion resting on the ruling being correct in principle — the nested-call cost is already baked into every measured number, because the benchmarked function already does this. This fully closes my concern: the ①②③ cost basis is validated, not just argued to be validated.

What remains for Jeroen, stated precisely, per the coordinator's ask: Jeroen's own outline sentence — "This at the same time serves as seed information for the deeper cascade" — is genuinely ambiguous between two readings, and interview 2 is where that ambiguity needs to close, not before. Dudley's ruling asserts his function-composition reading is what Jeroen meant, grounded in the fact that this pattern already ships (region-baseline-feeds-district). That's a strong circumstantial argument (it's the only reading consistent with existing shipped code and with D-227), but it is still an inference about authorial intent, not a confirmation from Jeroen himself. The interview-2 item, precisely stated: ask Jeroen to confirm "seed information for the deeper cascade" meant "the finer derivation calls the coarser rung's derivation function as a sub-computation, the way region-baseline-feeds-district already works today" — not "the finer step consumes the coarser step's already-computed response bytes as a literal input." If he confirms, B2 is fully closed with authorial agreement, not just Dudley's reading of the code precedent. If he meant the second reading, the cost model needs the re-bench I originally flagged — but given the mechanism is architecturally forced by D-227 (a response-consuming model would require solving the cache-dependency-chain problem Dudley's argument-point 1 lays out, which nothing in the outline text asks for), I think this is very likely to confirm cleanly. Downgrading this from BLOCKING to a one-line interview-2 confirmation item, not a re-open.

2. The envelope design — attacked directly, one real angle found, one attack that didn't pan out (reporting both, per my mandate).

Attack 1 (didn't pan out — reporting the clean bill): does StepCanvasRequest risk a structural collision with legacy AtlasLayerRequest in the ShapeProbe demux, given AtlasLayerRequest has no boolean discriminator (unlike star_map/city_names/browse)? Checked directly against server/src/bridge/mod.rs:96-152. AtlasLayerRequest's shape-identity in ShapeProbe is body_id.is_some() && up_to.is_some() (up_to is a required, non-#[serde(default)] field on the struct itself, confirmed by reading layer_proxy.rs:510-512). Dudley's StepCanvasRequest struct carries body_id but never up_to — so no minimal well-formed StepCanvasRequest payload can satisfy AtlasLayerRequest's two-field identity check, regardless of try-order placement. Dudley's text also explicitly extends ShapeProbe with a step_canvas field and folds it into the mutual-exclusivity sum — so a payload carrying both step_canvas and up_to+body_id would be correctly rejected as ambiguous by the existing H1 defensive check, not silently misrouted. Cleared: the demux design holds under a targeted collision attack, including the one case (legacy shape has no discriminator) that looked like the most likely soft spot.

Attack 2 (the "no coexistence needed" claim, stress-tested against version-skew, not just live-wire skew — this is where a real gap was, and it's already been found and closed by the workshop before I could report it): I went looking for exactly the gap I flagged in my original S5 (disk cache persists across a version boundary D-192's "always in sync" argument doesn't cover) and found tyre-round2.md §(a.9) already cites it verbatim as "Troblum S5" with a complete, record-ready fix (schema/version tag on every cache entry, tag-mismatch = cache miss, never a decode attempt). Verified the fix is structurally sound: it correctly scopes the claim (D-192's live-wire reasoning stands unchanged; only the persistent-cache boundary needs the new tag) and correctly treats a version mismatch as "just another eviction case" consistent with D-227's existing discipline, not a new failure mode. No further attack needed here — this is closed, and closed well.

Attack 3 (genuinely new, not previously flagged by me or found addressed in round 2): the migration's own sequencing claim — "the server-side sixth-shape addition and the client-side viewer rewrite don't have to land in the same PR" — is correct for the LIVE wire (D-192 co-ship holds at every commit because client+server build together), but says nothing about the disk cache accumulated during the OLD viewer's lifetime being read by the NEW viewer once the cutover lands. Concretely: a player has an existing user://atlas_cache/ populated entirely by the retiring AtlasWindowViewer (old district_window-keyed entries, old key shape make_key() without a step index per Stig's round-1 text). The new stepped viewer ships, checks the cache directory, and — assuming Stig's FileAccess index is keyed by the new composite key shape (body_id:step:center:n:granularity_v2, per his round-2 IndexEntry.key spec) — simply won't find any hits against old-format keys. This isn't a decode-corruption risk the way S5 was (different key shapes don't collide, they just miss), so it's lower severity than S5 was — but it does mean the cutover moment is a guaranteed 100% cache-cold start for every player with pre-existing Atlas usage, not a graceful degrade. Nobody has stated whether the old user://atlas_cache/ directory (if the legacy viewer even had a disk cache — checking Stig's round-1 text, the old AtlasViewer only had the in-memory atlas_window_tile_set.gd Dictionary LRU, no disk tier at all, since the disk-backed store is this workshop's own new proposal) gets cleared, ignored, or orphaned on cutover. This is a NOTE, not a SERIOUS finding, once traced fully — because the legacy viewer never had a disk cache to begin with (confirmed: Stig's round-1 §1 describes only atlas_window_tile_set.gd's in-memory Dictionary as the pre-existing cache), so there's no stale-format directory to orphan; the new disk cache starts genuinely empty on first use regardless of cutover timing. Downgrading this from a finding to a clean bill: the "no coexistence needed" framing holds for the disk cache specifically because there was no prior disk cache to migrate away from. Flagging only so the implementation ticket doesn't need to write dead migration code for a cache tier that never existed.

3. Ladder tables — Option B's zero-asterisk claim: mostly holds, one small asterisk found inside it; D-226(d) letter-compliance and the Region→District seam argument both check out.

The zero-asterisk claim, checked row by row against what was ACTUALLY measured this session vs. inherited from an earlier baseline document. Option B's five rows: Region (MEASURED, T-1178's 8.3M-cell District-spacing row reused — see caveat below), District (MEASURED, T-1178), Quarter (same-band), Block (MEASURED, T-1154's 128m sweep), voxel (MEASURED, T-1154's 83K-cell deep-step bench). Four of five are genuinely, directly measured THIS session. The Quarter row is the one soft spot: T-1154's own text frames Quarter's per-cell rate as "the existing... Quarter (1.823 µs/cell) row[s] already in atlas-zoom-ladder-t1143.md §7" — i.e., Quarter's per-cell rate is inherited from an earlier session's baseline document, not re-measured fresh this workshop. T-1154 confirms Block/Tile land in the same band as that inherited District/Quarter baseline, which is a real and valid cross-validation — but it means Quarter was never put through this session's own 8.3M-cell parallel sweep the way District and Block were (checked directly: T-1178's 8.3M square-canvas row is explicitly District spacing only, t1178-t1154-derive-bench.md:148). This is a small asterisk, not a real problem — the same-band confirmation mechanism (flat ~190-220 ns/cell parallel rate holding across every rung this session DID test at every size) makes it very likely Quarter holds too, and Dudley's own table already marks it "~1,827 ms (T-1154 same-band confirmation)" rather than a flat unqualified number, so the workshop's own documentation is honest about this — I just want it named explicitly as "one row is same-band-inferred, not this-session re-run at 8.3M," since "zero asterisks" as a literal claim slightly overstates it. Doesn't change the recommendation (Option B is still clearly the best-evidenced skeleton) — this is a precision note, not a reversal.

D-226(d) letter-compliance at each skeleton's deepest step: checked all three options (A/B/C) — every one bottoms out at voxel (1 m), display-ratio- sized (216×384 m, 82,944 cells), never the fixed 3840×2160/8.3M-cell canonical shape. This holds the D-226(d) letter identically across all three skeletons — the deepest-step canvas policy doesn't vary by which skeleton wins, only the path to reach it (how many intermediate steps) varies. No skeleton-specific compliance risk found; this was already the corrected convention Dudley applied uniformly per his own note in §(c) ("noted consistently in all three tables below, not just this one").

The Region→District seam argument: Dudley's claim that the ÷100 jump at step 0→1 "matters for serving... lines up with a real architectural boundary" (the global/sub-global cache tier split) — checked this against the actual cache-tier spec (both his §(d) and Stig's round-2 §(c)): yes, Tier 1 (global, keep-always) is Region-spaced exactly, and Tier 2 (storage- evictable) starts at District. The architectural seam and the display-factor seam are the same boundary by construction, not a coincidence — this holds up. Cleared.

4. TTL-split planes (araminta-round2.md §a) and three-tier cache spec (stig-round2.md §c) against my S1/S5 — both specs now address the concerns; S1 substantially resolved, S5 fully resolved (see item 2 above, already covered by tyre-round2.md §a.9).

S1 (evict-then-revisit cost spike, TTL-distance-decay vs storage-eviction precedence unspecified): Dudley's round-2 §(d) resolves the precedence question by eliminating the ambiguity structurally, not by picking a winner — his revised formula drops distance_decay entirely (evict_if: time_since_last_visit(entry) > STORAGE_TTL[rung]), reasoning that distance-from-focus is redundant with time-since-visit for a storage-thrift purpose. This closes the "which axis wins" half of S1 cleanly — there's only one axis left for storage-eviction, so there's nothing to arbitrate between. Stig's round-2 §(c) independently confirms the same simplification (his Tier 2 spec: "(2a) time-since-last-visit sweep" + "(2b) LRU-capacity sweep," two independent triggers, no distance term at all) — two independently-designed specs converging on dropping distance_decay is good evidence the simplification is correct, not just convenient.

What S1 asked for that's still not fully delivered: the actual revisit-cost number. I computed ~80ms/canvas (74.8ms derive + 5.41ms PNG-encode, T-1179) × N canvases for a realistic revisit scenario — neither round-2 cache spec states an expected N (how many canvases a typical explored-then- abandoned body accumulates) or runs that arithmetic explicitly. Stig's spec does now name eviction granularity precisely (per-entry, the composite key), which was the other open half of S1 — so the mechanism is fully specified, just not the end-to-end cost consequence of a real revisit. Downgrading S1 from SERIOUS to NOTE: the mechanism gaps (granularity, axis precedence) that made this SERIOUS are now closed; what's left is a tuning-pass number (expected revisit cost under realistic N), which both specs correctly treat as implementation-time tuning rather than an architecture question — Stig's own text explicitly defers the STORAGE_TTL/budget constants as "a tuning pass once real play-pattern data exists, not an architecture call I'm locking here." That's the right call; I'd only ask that the ticket plan name "measure actual revisit-pattern cost against a real play session" as a post-ship validation step, not a pre-filing blocker.

S5 (disk cache schema/version tag): already covered in item 2 above — tyre-round2.md §(a.9) resolves this completely and correctly, citing my finding by name. Checked Stig's round-2 IndexEntry schema (stig-round2.md:206-223) to confirm it does NOT yet carry the tag itself (it doesn't — the schema shown is pre-amendment) — but Tyre's text is explicit that "it lands in Stig's FileAccess spec at implementation," i.e. this is correctly sequenced as a record-level requirement now, schema addition at implementation, not a gap in Stig's document. Fully resolved.

Summary of the addendum

Original finding Status after round 2
B1 (population-scale cliff survey never run) STILL OPEN — tyre-round2.md §(a.7) correctly gates the sparse-list cost claim on it running, but it has not been run yet (checked: no new measurement doc, Dudley's round-2 text doesn't mention it). Still the one thing I'd block filing on.
B2 (seed-chaining fork) RESOLVED, verified against source (district_profile.rs:1705-1732 — the nested-call pattern is already what every measured ns/cell number exercises). One precise interview-2 confirmation item remains (does Jeroen's phrasing match this reading), stated above — downgraded to NOTE-level follow-up, not a blocker.
S1 (evict-then-revisit spike, axis precedence) Mechanism fully resolved (both specs converge on dropping distance_decay, granularity now per-entry). Downgraded to NOTE: the actual end-to-end revisit-cost number is still unstated, correctly deferred as a tuning-pass/post-ship-validation item by both specs.
S2 (courses density coverage gap) STILL OPEN — not addressed in any round-2 document. Deep-step + high-river-density combination remains unmeasured.
S3 (D-226(d) letter vs purpose, cache accumulation) RESOLVED — tyre-round2.md §(a.8), same framing, same risk actor (AtlasAgentInterface), concrete structural fix (per-body deep-rung cache cap).
S4 (sim-state phase cadence unspecified) Not directly addressed by name in round 2 — araminta-round2.md §(a) does much of the adjacent work (TTL-split field assignment, static-vs-sim-state reasoning per field) but does not name a phase-cadence number or range. Still open, lower priority than B1/S2.
S5 (disk cache version tag) RESOLVED — tyre-round2.md §(a.9), complete and correct fix.
N1 (decimal-MB units) Unchanged, still just a note-in-record-text item.
New (envelope demux collision attack) CLEARED — attacked directly, design holds.
New (cache-format cutover / stale-key orphaning) CLEARED on inspection — no prior disk cache existed to orphan; downgraded from a worry to a documented non-issue.
New (Option B's "zero asterisks" has one small inherited-baseline asterisk on the Quarter row) NEW NOTE — Quarter's rate is same-band-confirmed, not this-session-remeasured at 8.3M; doesn't change the recommendation.

Net: of the 7 original findings, 4 are now resolved (B2, S3, S5, and S1's mechanism half), 2 remain genuinely open (B1, S2) and are the two I'd still flag as blocking/serious for interview 2, and 1 (S4) is partially addressed but not closed. The round-2 team's own adversarial handling of my findings was accurate and thorough — every fix I checked against source or cross-spec actually holds, not just reads well.


FINAL UPDATE (post-B1-survey)

Verified bench_population_survey_all_committed_bodies (server/tests/hydrology_equilibrium_bench.rs:273-) directly against source, the same way I verified everything else in this pass — not taking the coordinator's summary or the measurement doc's prose at face value.

Confirmed genuine, not a repeat of the original defect: the new bench's par_iter closure calls load_heightmap_png(path, &body_id, 0.3) inside the closure, once per discovered path — a distinct file read, distinct body_id (derived from the parent directory name), and distinct small.sea_level (read from each PNG's own embedded sea_level metadata, not a shared constant) per iteration. This is structurally the opposite of bench_parallel_273_bodies_at_512x256's defect, where gj1c_512x256() was called once outside the par_iter block and every iteration reused the same elev/sea_level binding. The new bench also carries a `heightmap_paths.len()

200 assertion (guards against a silently-empty discovery walk) and a built-in determinism spot-check (assert_eq!oncliff_edge/ channel_depth_scaledfor the top-by-carving outlier, re-solved a second time) that would fire the moment any future body actually carves. Basin arithmetic checks out (22,270 total = 21,240 overflow + 1,030 endorheic, confirmed by direct addition). The measurement doc'scliff_edge/ carved_cells filter logic in the bench (BasinOutcome::Overflowwithchannel_depth_scaled[b.spill_cell] > 0, cliff_edge.iter().filter(|&&c| c)`) matches what the original T-1177 module actually computes — not a redefined or looser carving criterion invented to force a zero result.

One precision note, not a discrepancy: the coordinator's/Tyre's cited "31187 basins/body" range is the min/max within the printed top-15 table (GJ1075c=31, GJ1e-m1=187), not a separately-stated population-wide min/max — the measurement doc's own prose illustrates variety with a different example pair (GJ103c=40 to GJ1e-m1=187) rather than stating the true extremes. Both are consistent with the same data; this is not an error, just noting the "31" figure comes from reading the table rather than an explicit population min/max statement in the doc. Doesn't affect the finding.

B1 → RESOLVED. The population-scale claim now rests on real, distinct evidence (267 real bodies, each independently solved, real terrain, real sea levels) rather than one body's cost profile standing in for the population. The zero-carving result is stronger than what was being claimed before my original finding (100% zero vs. "structurally rare, one-body-verified"), and the epistemic framing in tyre-round2.md §(a.7) is honest about the remaining limit ("not observed yet ≠ cannot occur," carving arithmetic stays unit-proven for future bodies) rather than overclaiming "impossible." This is exactly the outcome I'd hoped running the survey would produce, and it did.

Scorecard, final, for interview 2

Of the seven original findings plus the addendum's follow-ups, two remain open and are the only items I'd carry into interview 2:

  1. S2 — courses-inclusive derive cost has zero measured coverage at the deep-step (Block/Tile spacing) × high-river-density combination. The one courses-inclusive-at-real-density number that exists (T-1178 Cross-check 1: 195.0 ns/cell, 18 courses, District spacing, one body) is accurate where measured (confirmed clean in my original pass), but the deepest step's own realistic-canvas bench (T-1154's 83K-cell/17ms number) explicitly excludes courses entirely by construction (H2's rect_window_replica is courses-empty), and no round-2 document adds a deep-step/river-dense bench. Not blocking (the existing <5% District-cap bound has enough headroom that a multiple-density increase likely stays affordable), but it is the one combination in the whole measured appendix with literally zero data point, at exactly the spot (deepest step, most individually-resolvable tributary geometry) where the excluded cost is least likely to stay proportionally small. Ask: run one more bench — deep-step canvas on a body selected/authored for high course density — before or shortly after filing; not a blocker for filing itself.

  2. S4 residual — sim-state TTL phase-cadence is still an unnamed variable. Araminta's round-2 §(a) fully resolved the field-assignment half of my original finding (which fields are sim-state vs static, and why, stated per-field rather than inferred from name — glaciation/ flooded sim-state, temp_dc static, with a genuinely sharp non-arbitrary argument for each). What remains unresolved is the numeric half: D-228's "computed once per phase" water-height/ glaciation clock model doesn't state a phase duration, and nothing in round 2 names one (or a per-body-class range) for sizing the sim-state plane's actual TTL. The determinism substrate is sound (confirmed clean in my original pass) — this is purely a missing tuning input, the same category Stig/Dudley both correctly deferred their own STORAGE_TTL/ budget constants as "a tuning pass once real data exists." Ask: either fold this into the same tuning-pass deferral (acceptable, and consistent with how the other unresolved constants in this workshop were handled), or if Jeroen wants a concrete number before filing, that's a Dudley/ Araminta follow-up question at interview 2, not something the measured appendix can answer as-is.

Everything else — B1 (population survey, now run and resolved), B2 (seed-chaining, resolved and verified against source), S1 (eviction mechanism, resolved; end-to-end cost number correctly deferred as tuning), S3 (D-226(d) accumulation gap, resolved with a structural fix), S5 (cache version tag, resolved with a complete fix), N1 (units note), N2 (informational, now moot — all round-2 files exist), N3 (clean bill, unchanged) — is closed or was never a live concern. The workshop's round-2 handling of every finding I could verify against primary sources held up exactly as documented, with no gap between what was claimed fixed and what the code/ doc actually shows.