Hoshe + Tyre review of the micro-habitat-mosaic branch requested changes on two
data-confirmed correctness bugs plus follow-through. All addressed.
Blocking:
- C1 (voxel.rs): the mosaic re-granted vegetation the climate withheld — a frozen
ice world (Edict) read 61% vegetated tundra. The Grassland apply-gate now fires
only where the climate already grants cover (!Barren); a climatically-barren
district (frozen < -50C = surface ice/geology D-239 §2, or hyper-arid moisture<5,
both resolved to VegetationClass::Barren upstream) is skipped. Genuine tundra
(cold + moisture>=15) is Scrub upstream and still reaches the tundra palette via
the same !Barren path. believability.json regenerated: Edict vegetated_districts
39->0, Arbour 20->8 (its real cold-pole/arid districts revert to barren);
intra-class variety preserved (Arbour vegetation_classes 3, micro_habitat_distinct 2).
- H1 (bodies.py): biosphere Gate 0 keyed on `inhabited`, conflating settlement with
biosphere and force-Airless'ing uninhabited-but-alive worlds (D-247: worlds were
alive before humans). Gate 0 is now settlement-independent — keyed on
atmosphere/hydrosphere/planet_class. 24 uninhabited-alive worlds now classified
(was 0); GJ0g-1 (dense atm + rivers) -> NativeMirror. Split 122 Compatible :
121 Mirror; all 9 canon exemplars hold.
Follow-through:
- C2 (bodies.py + D-247): Gate 0 now reads planet_class (barren -> Airless) as
D-247's "+ planet_class" phrasing names. D-247 amended to record the actual
two-gate signal set (atm/hydro/planet_class habitability; economic_base_primary
chirality; settlement-independence).
- C3 (believability.rs): micro_habitat_distinct criterion relaxed to `== 0 || >= 2`
— the min-over-patches estimator makes K=3 unreachable against a dominant-entry
palette; K=2 provisional, Q-123 calibrates.
- C4 + H2 (D-246): amended to record the v1 scope cut — only the Soil-derived
vegetated/wet classes take the mosaic; material-driven families are `_ => false`
so Hardpan/Scree are authored but unreachable in v1; no elevation change in v1;
the mosaic OWNS the §8 climate->vegetation law for the classes it touches.
- H3 (voxel.rs): three dedicated mosaic-pass regression tests — C1 barren-respect,
§8 material-untouched + forest-textured, T-1040 no-water-without-channel.
- H4 (voxel.rs): relief_signal comment corrected to ~[-1,+1].
- H5 (bodies.py): frontmatter override now strips quotes + validates against the
4-value enum (warn-and-skip on typo).
systems.db regenerated + stamped (H1). believability golden regenerated (C1).
Full cargo test green; clippy -D warnings + ruff clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mosaic palette gains a climate-biome dimension for the Grassland surface-class:
savanna / temperate / boreal / tundra now read distinctly rather than collapsing to
one biome-blind palette (the D-246 biome-awareness fix).
- atlas/mosaic.rs: ClimateBiome (from temperature_c + moisture_q) + four biome-keyed
Grassland palettes; palette(class, biome). Savanna carries scattered Forest copses,
tundra carries lichen + sparse bare ground and no forest. §8-safe (veg + water only).
- atlas/voxel.rs: derive the biome; a cold barren district (tundra/boreal) now takes
the sparse mosaic rather than staying blank — worlds that read "0 vegetated / blank"
now read structured-sparse (D-245 climate-appropriateness). vegetated_districts
8→20 and 0→39 on the cold validation bodies.
- data/mosaic_constants.toml: biome-keyed grassland mirror.
- docs/design/alien-ecology-vocabulary.md: Miri's full biome × register naming layer
(the cane/vane/wrack/husk/drum-root native lexicon + Earth-register names) — the
derived-label source for the palette and Phase-5 rendering.
Believability golden regenerated. Full server suite green. Forest biomes and the
introduced-Earth register are the noted continuation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sub-chunk mosaic that modulates the D-228 axes into a spatially-coherent,
relief/moisture-conditioned mosaic of micro-habitats within a terrain class —
the fix for the aliveness probe's uniform "Wetland 100%".
- seed.rs: SeedDomain::VoxelMosaic = 12 (append-only; pinned-discriminant guard)
- voxel.rs: five new D-228 axis values (Vegetation::{Meadow,Deadfall,Lichen},
TerrainMaterial::{Hardpan,Scree}); the per-family-gated mosaic pass
- detail_scatter.rs: voxel_mosaic() selection field (~8-64 m band)
- atlas/mosaic.rs: surface-class palette + integer-basis-point weighted selection
with moisture-affinity conditioning; temperate/native baseline palettes
- data/mosaic_constants.toml: source-canonical palette mirror
- believability.rs: micro_habitat_distinct metric + D-245 intra-class criterion
v1 scope (D-246-faithful): modulates vegetation + water only, in the vegetated
classes (Wetland/Forest/Grassland), so the D-239 §8 lithology law is untouched;
water is gated on the T-1040 has_active_channel hydrology rule. Material variants
(hardpan/scree) and the extreme classes are a §8-aware follow-up; the biome-aware
palette variants (T-1085 vocab) extend the baseline before generation.
Goldens regenerated (believability + derivation). Full server suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The push gate's integration tests (not in cargo test --lib) caught two regressions
from the relief change:
- derivation_harness::cross_district_elevation_blend_reduces_seam_step — the relief's
large per-position term (span 300 → ±200 m, far above the compressed elev_q/2 base)
swamped the T-1042 seam measurement and clamped heavily at sea level. Fixes:
(1) shift the voxel-relief octave band to 0.13–1 km (all sub-district, dropping the
2 km octave that competed with elev_q's district role); (2) reduce VOXEL_RELIEF_SPAN_M
300 → 100 so relief stays mostly below the base (fewer sea-level clamp artifacts,
proportional hills); (3) rewrite the seam test's avg_elev to average over an 8 km
multi-wavelength y-transect so the zero-mean relief cancels, isolating the base seam.
- derivation_harness::golden_seed_determinism_regression — legitimate golden refresh
(determinism still holds; elevation values changed intentionally).
Believability relief now ≈22 m mean (was 5 m flat; 59 m at the over-aggressive span 300)
— navigable hills without clamp artifacts, 7/8 criteria. Both goldens regenerated; full
cargo test (38 binaries) + clippy --all-targets -D warnings green. D-239 amendment +
Q-123 item 4 updated to the final span/octave/numbers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The voxel tier read flat (the D-245 "0–3 m, no hills to navigate by" bug):
family generators set elevation from elev_q at a compressed scale plus only ±4 m
micro-scatter, and detail-scatter's mid-scale relief (D-243 §2) only ever reached
the district tier (elev_q), never per-voxel elevation_m.
- detail_scatter.rs: extract the enveloped-fBm core; add `voxel_relief` at the
0.25–2 km octave band (vs the district 4–40 km band).
- voxel.rs: add the mid-scale relief post-dispatch in derive_voxel_column, body-global
SeedDomain::VoxelRelief seed (position-keyed), f64 truncated to integer metres (D-010).
Envelope = slope_q*3 + elev_q — the coarse heightmap gives slope_q ≈ 0 even on high
ground (Arbour max 13), so elevation must drive ruggedness. Flat families only
(Alluvial/Lava/BraidedDelta/Dune/Meander); the dramatic families already carry strong
internal relief and WaterBody stays at sea level.
- seed.rs: SeedDomain::VoxelRelief (D-224 domain separation).
- believability.rs: new `contrast.voxel_relief_m` metric — mean within-district elevation
range over a 2 km transect (a single 64 m sample chunk is narrower than the relief
band) + a "voxel relief" criterion.
Arbour @ yolo: voxel relief 5 m → 59 m, 6/8 → 7/8 criteria (remaining fail = the
Q-123 vegetation-denominator item). Edict 59 m. Golden regenerated; 1586 lib tests +
believability harness (determinism) pass; clippy -D warnings clean.
D-239 amended (T-1081); Q-123 item 4 updated. Deferred to Q-123/follow-up: the
provisional span + threshold, and a per-body hypsometric absolute-elevation model.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stack review (Hoshe + Tyre) of T-1083+T-1082+T-1080. Every finding fixed or disposed:
- believability.rs sample_indices: replace the rejection-sampling loop (could stall
when n ≈ take) with deterministic partial Fisher-Yates — O(n), guaranteed
termination, distinct, sorted (Hoshe+Tyre).
- believability.rs drainage proxy: exclude water-body districts (all elev 0, no banks)
and require both wet + dry voxels — they were passing vacuously and inflating the
metric (Hoshe).
- believability.rs read_cities: log skipped malformed rows instead of dropping silently
(Hoshe).
- voxel.rs: add the 3 WaterBody zones to all_families_no_panic_at_extreme_positions +
fix the "7 families" comment (Hoshe).
- climate_constants.toml: bold warning that [moisture_gradient] is not loaded until
T-1032 — tune ClimateConstants::default() (Tyre).
- Q-123: record the per-zone "water renders wet" threshold + the land-relative
vegetation-present denominator as calibration items (Tyre + the open 7th criterion).
- T-1083 ticket: note the sampling-fix attribution (landed in the T-1082 commit).
Believability golden regenerated (sampler + drainage fixes moved the metrics).
clippy --all-targets -D warnings clean; 1580 lib tests + harness pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
moisture_q was a single body constant (derive_moisture_q took only &BodyParams →
80 for every one of 2048 ocean-world districts), so vegetation/terrain/ecotones
were uniform — "nothing to fuzz" (the T-1080 believability bug).
- district_profile.rs: derive_moisture_q is now a body ceiling (hydrosphere+atmosphere)
× per-district gradient — latitude (equator wet → pole dry), elevation (orographic/
rain-shadow), continentality (100 − ocean_fraction_q). Integer/D-010, no new plumbing
(elev_q/ocean_fraction_q/latitude_deg already reach build_district_profile). Mirrors
D-240's temperature model. Coefficients in ClimateConstants + climate_constants.toml
[moisture_gradient] (provisional; Q-123 calibrates). Tuned so a wet body stays mostly
green with drier patches and a frozen body clamps mostly barren. + gradient unit test.
- region_profile.rs: region-tier moisture gets the region-latitude gradient (elevation/
continentality are district-scale).
- Propagates to precipitation_class / vegetation_class / morphology_zone (all read
moisture_q). D-239 §2 amended. Believability golden regenerated.
Probe (Arbour @ yolo): moisture_q distinct 1 → 49 (spread 0 → 48); 5/7 → 6/7 criteria.
Edict (frozen): moisture 0..20 ×21 — gradient within its low ceiling. Remaining fail
(vegetation-present) is the metric dividing by all-sampled incl. water on a 2/3-ocean
world — a Q-123 calibration concern, not a climate defect.
clippy --all-targets -D warnings clean; 1580 lib tests + harnesses pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The water zones (OpenOcean/Lake/TidalFlat) fell through to the dry-land AlluvialPlain
fallback, so the sea rendered as dry forested land (the D-245 believability bug).
- voxel.rs: new MorphologyFamily::WaterBody (9th family) + generate_water_body,
dispatched from zone_to_family. Deep open water with a Shallow shoal band on the
district-anchored coast line (wider for shallower bodies); TidalFlat all-Shallow.
Seabed Rock (steep) / Sand (gentle) / Wetland (tidal mud); Vegetation::Barren;
water surface elevation_m = 0; seasonal Ice via derive_cover (frozen seas). Wetland
stays AlluvialPlain (it is land — a marsh, not open water). Integer/D-010. + 4 tests.
- believability.rs: fix a sampling bias in the T-1083 enforcer found while verifying
this — the metrics sampled the first 64 districts in BTreeMap order (a spatial corner,
e.g. all-ocean), which reported a forested world as "0 vegetated". Now a deterministic
seeded spread across the body (matches D-245's "randomly-sampled locations").
- D-239 amended (§5 8→9 families; §6 refined — the 3 water zones now dispatch to
WaterBody). Believability golden regenerated.
Probe (Arbour @ yolo): water-renders-wet 4/64 FAIL → 42/44 PASS; terrain-material
variety 1 → 4 PASS; 3/7 → 5/7 criteria. (Remaining fails — moisture gradient,
vegetation-present — are T-1080.)
clippy --all-targets -D warnings clean; 1579 lib tests + the harness pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promote the throwaway aliveness probe into a committed, repeatable believability
protocol — the instrument that found T-1080/T-1081/T-1082.
- atlas/believability.rs (new): BelievabilityReport (serde) + analyze() computing
CONTRAST (per-field min/max/distinct for moisture/elev/slope/ocean over all
districts; distinct morphology zones / vegetation classes / terrain materials)
and COHERENCE (water-renders-wet, drainage-monotonic, vegetation-present) — never
marginal per-tile counts (the lesson: that called a broken uniform world ALIVE).
evaluate_criteria() = advisory D-245 checks; cascade_for_body()/seed_to_u64()
loader shared by the bin and the harness. Unit tests prove the metric tells a
uniform world (fails) from a varied one (passes) + determinism.
- bin/aliveness_probe.rs: refactored to a thin CLI over the module — prints the
body-level report + advisory D-245 criteria, drops the naive per-tile verdict.
- tests/believability_harness.rs (+ golden): runs the real cascade for Arbour
(temperate/ocean) + Edict (frozen/ice), asserts determinism, golden-snapshots
the reports (regression — updates when T-1080/T-1082 land), advisory criteria
with BELIEVABILITY_STRICT=1 to fail on unmet D-245 criteria. Skips if committed
data absent. x86_64 golden (cascade has f32 warp paths).
Baseline @ believability-v1: Arbour 3/7, Edict 3/7 criteria pass — moisture
gradient, water-renders-wet, drainage all FAIL (T-1080/T-1082); Edict vegetation
0/64 (blank tundra — the frozen 'reads dead' case D-245 targets).
PNG layer maps (ticket item 3) deferred — explicitly optional; the contrast +
coherence metrics are the core enforcer.
clippy --all-targets -D warnings clean; 1575 lib tests + the harness pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2 of the D-243 re-scale. The ~1km carrier is renamed and re-scaled to
the 2km district, and every consumer reads scale from the canonical scale.rs
instead of a local literal.
- region_profile.rs -> district_profile.rs; RegionProfile -> DistrictProfile,
RegionPos -> DistrictPos (re-exported from scale::DistrictPos), derive_*_regions
-> derive_*_districts, across all 10 consumers + the derivation harness.
- chunk_context now references scale:: for the carrier cell: district 1024m -> 2048m,
shift 4 -> 5 (CHUNK_DISTRICT_SHIFT). This caught a real latent bug: the seed-district
index used a literal '>> 4' while the anchor used the constant, so same-district
chunks could derive different anchors — now both use scale::CHUNK_DISTRICT_SHIFT.
- derivation_harness scale literals ('>> 4', '* 1024', '0..16') converted to
scale:: constants (the same drift the canonical source eliminates). Believability
tests pass at 2km; the voxel determinism golden re-pinned (anchors moved with the
re-scale — deterministic, intended).
Full suite + clippy --all-targets -D warnings green. The district DERIVATION still
tiles the heightmap at 8 cells (the old coarse path); replacing it with on-demand
heightmap interpolation + detail-scatter is phase 3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
derive_chunk_context now derives channel_anchor_m and coast_anchor_m from
the Region-or-higher seed path (D-239 §10); all five voxel placement sites
measure distance to these anchors in continuous world coordinates:
- T-1040: compute_channel_state / compute_meander_reach_channel /
in_levee_band subtract the region anchor instead of measuring from the
world origin axis — active channels now exist in their chunks anywhere
on the body, continuous across chunk boundaries. has_active_channel is
honest in both directions (band-crossing gate; floor covers the
BraidedDelta belt at short wavelengths).
- T-1041: fjord/cliff/gorge/delta drop their rem_euclid(64) chunk-frame
folds — one valley/coastline/fan per region instead of one per chunk.
D-239 §9 chokepoint widths preserved; two i32 wall-rise overflows on
region-scale distances fixed by clamping before multiply.
Harness: 6 new believability tests (channel presence + gate honesty at
chunk (1000,-750), cross-boundary continuity, one-landform-per-region,
braid-belt confinement); drainage sweeps strengthened with anchor-band
chunks. Voxel golden regenerated deliberately: case A now pins a genuinely
in-channel Shallow voxel on the anchor, case C a Deep trough voxel
matching its label; case B byte-identical and L0/L1 cascade golden
untouched (no upstream leak). Review: approved, three minors addressed
(truncation-convention docs, delta gate floor, case C relocation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adapt the believability/budget verification harness to the T-1033 BodyParams
strip: validation-body fixtures (Kallast/Glødberg/Marevna) no longer set the
removed orbit/star fields (orbital_period_days, axial_tilt_deg, spectral_class,
star_type). Temperature is now derived from the planet_class envelope (D-240),
so the Marevna validation note/assertion is rewritten — oceanic band [-12,28]°C
+ maritime 0.6 + standard-atmosphere greenhouse yields ~+26°C at the equator
(was a stale ~-56°C orbit-equilibrium note). Add the seed→tile golden fixture
(tests/golden/derivation_harness.json) that the determinism regression locks.
Also fmt the harness and fix one clippy lint (iter().copied().collect() ->
to_vec()) — the file predated the pre-push gate's cargo-test/clippy coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close the last D-234 piece — terrain water-direction extraction wired through
to founding orientation and the quarter waterfront rule:
- Layer 1: TerrainAnalysis::water_bearing — 8-octant integer bearing toward the
nearest water from the water_dist gradient (D-010, no atan2). Stored on
GeographicAttractor.water_bearing (360 = none).
- #956 founding orientation: coastal/river settlements now get a real
water-facing bearing (the anchoring attractor's), replacing the 0 stub.
- #957 waterfront rule (D-234b): the water-facing quarter edge (from the
settlement's Coastal founding orientation) drops its block setback to 0 so
buildings present flush to the quay (dock-orthogonal). Typed Edge + coastal_edge
+ per-block gating.
Golden + atlas_response fixture rebaked (additive water_bearing field only).
8 new tests. All integer-deterministic (D-010).
Pending: the waterfront rule reads context.founding_orientation, which
city_context_reader still stubs to Cardinal — real per-settlement orientation
reaches quarter generation once the Layer-3 placement -> Layer-4 GenerateSkeleton
dispatch is wired (the remaining cross-layer integration).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DrainageBasin gained a territorial_status field, so the Layer-1 golden and the
atlas_response msgpack fixture pick it up. Both changes are additive/
representational — basin/attractor/river selection is unchanged.
- cascade_golden.rs: pass None dominant_faction; golden rebaked (only
"territorial_status": "FrontierUnclaimed" added per basin).
- gen_fixtures.rs: set the new field in the atlas_response generator; fixture
regenerated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the existing attractor-matching engine (#919/#925) into the
generation cascade as Layer 3, and make the whole placement-scoring path
integer-deterministic.
Layer 3 (D-211):
- CascadeLayer::Settlement + Layer3Output (placements) on the snapshot;
BodyWorldState gains a `placements` field (the D-203 hot cache).
- run_layer3 runs the five-phase match_cities against Layer-1 attractors
via the authored D-195 compatibility matrix; pure function of
(attractors, cities) — no RNG. cities are passed in by the caller so the
cascade stays DB-free and testable. A `// cache seam` marks where a
persistent cache wraps it later (#1021).
- gen_queue passes &[] for now (Topography needs no cities); the runtime
settlement read (gen_queue/layer_proxy) is the #955 follow-on.
Integer determinism (D-010 / D-227 — D-195 amended):
- Wiring match_cities into the deterministic cascade made its f32 scoring
a live cross-platform divergence risk (a near-tie comparison or the
Hungarian's f32 reductions can round differently per platform → a
different world from the same seed). Converted the entire path to
integers: CompatibilityMatrix is a 0-100 affinity table; attractor
strength is 0-100 and terrain cost is a percent (100 = baseline),
quantized once at the Layer-1 feature boundary; cell_score, the
Hungarian, and CityPlacement.score are i64. No f32 in any placement or
ranking decision.
- Layer-1 golden fixture rebaked: confirmed selection/positions are
unchanged (same 256 attractors, 93 river cells) — only the strength/cost
representation changed.
Tests: lib green (1292); new settlement_layer_places_cities_deterministically
covers placement + determinism + propagation into BodyWorldState.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two Layer-1 generation fixes surfaced by the per-layer atlas viewer:
- Layer1Output now carries grid_w/grid_h (the downsampled working-grid the
positions live in). The client maps overlays from these, so the scale is
correct for any source heightmap resolution rather than assuming the texture
size — fixes overlays projecting at half scale into a corner.
- Drainage basin boundaries are traced as ordered, non-self-crossing contours
via Moore-neighbour tracing instead of an angle-from-centroid sort. The sort
produced star-shaped, self-crossing polygons for concave basins that rendered
as straight chords across the map.
Golden (cascade_layer1.json) and the cross-language atlas_response_ready
fixture regenerated. 100 atlas lib tests + the new tracer test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses the Hoshe (QA) + Tyre (architecture) review of the SeedChain/cascade
work:
- Golden was pinning an empty river network (128x64 produced 0 river cells).
Bumped to 256x128, where GJ1c yields a real network (93 river cells, 19
mouths) — Layer 1's rivers are now actually guarded, not just attractors.
- SeedChain::for_body(world_seed, body_id) + fnv1a_64: the single canonical
body_id(String) -> u64 path (FNV-1a, the repo convention), so callers can't
derive divergent worlds from the same seed via different ad-hoc hashes. The
golden now uses it.
- Stability guards: seed_domain_discriminants_are_pinned test (CI fails if a
SeedDomain tag is renumbered); AttractorType gains #[repr(u8)] + explicit
discriminants (it's cast as a sort key in features.rs).
- Tests: SeedChain::root(0) non-degenerate; run_cascade error path (missing
file -> Err, not panic).
- Comments: clarified the id=0 derivations (sibling separation is caller-side
via the per-district/quarter chain; #957 threads the index), tightened the
all_district_types reachability comment (it pins the seed-0 sequence, not a
probabilistic claim), and noted the golden's WORLD_SEED is cosmetic at
Layers 0-1 + the x86_64 f32 capture caveat.
Deferred with reason: the run_cascade -> CascadeInputs struct refactor (Tyre)
is left for #954 — designing Layer-2's context shape now would be later-phase
detail, and there's a single caller to migrate then. EntityRng keeps its
domainless combine (migrating is stream-changing) — noted in D-224.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end determinism guard. cascade_golden.rs pins two artifacts for a real
committed body heightmap (GJ1c) in one diffable JSON golden
(server/tests/golden/cascade_layer1.json):
- Layer 0: SHA-256 of the source heightmap.png bytes (flips if the Python
heightmap generator or the file changes)
- Layer 1: the serialized Layer1Output of run_cascade on a 128x64 downsample
(flips if the Rust drainage/feature/sub-biome code changes)
JSON (not the msgpack discussed in refinement) to match the existing
golden_suite.rs convention and stay diffable — a failure shows what drifted.
UPDATE_GOLDEN=1 regenerates; wired into `make golden-update`.
Adds Serialize/Deserialize to RiverNetwork/DrainageBasin/Layer1Output and a
sha2 dev-dependency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three root causes: SnapshotBuffer hard-dependency in economy.rs
(Option-wrapped), TickPhase::configure missing from SimulationPlugin
(added idempotent call), and stale golden file after D-192 dropped
the version field (regenerated).
All 6 previously-failing tests now pass with zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per R-012: delete conversation.rs, both overheard content files, and
remove all 6 wire-up points (social_plugin, bridge/types, monologue,
voice/integration). Protocol version 22 → 23. Scope confirmed by
#842 audit — npc/ and content/global/ untouched. Surviving NPC
components (NpcName, NpcColorIndex, NpcConversation) migrated to
simulation/npc_components.rs for use by D-080 knowledge propagation.
Also applies pre-existing cargo fmt debt (names.rs and 4 others).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 7 archetype→monologue regression tests (smuggler/detective pool partitioning)
- 3 tell escalation unit tests (RoutineDeviation insertion + expiry)
- 6 news ticker tests (pool loading, SimRng rotation, zone gating)
- 3 live integration tests against real server binary (Layer 3)
- Update existing tests for current_ticker field and protocol v19
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add debug_response: None to all ObserverSnapshot constructors in
integration tests
- Bump PROTOCOL_VERSION assertion 17 → 18 in serialization tests
- Regenerate golden proof_room_tick_10.json (BoundaryWall tiles)
- Regenerate client msgpack fixtures for new snapshot fields
- Fix debug.rs resource optionality (Option<ResMut> for
ContaminationActive/EventQueue)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the peripheral sector (100° half-angle, reduced range) and blind
spot classification. The server now sends only tiles within the 120°
forward cone; the client renders previously-explored tiles behind the
player with a light fog overlay instead.
This eliminates complexity in both the cone classifier and the snapshot
protocol while preserving the core information asymmetry — you still
can't see behind you, and the monologue system (D-016) still bridges
the perceptual gap.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. CRITICAL: spawn_template_npcs now inserts NpcVisionState, NpcMemory,
PlayerAwareness on template-spawned NPCs — matches spawn_npc() pattern
from PR #66. Without these, template NPCs were invisible to vision and
awareness systems.
2. WARNING: generate_intra_template_triangles now calls validate_triangle_def
before generating TriangleState — mirrors cross-template path. Updates
test TriangleDefs and logistics-hub.yaml to pass all three quality checks
(conflict viability, relationship coherence, interest divergence).
3. WARNING: state_hash in compute_observer_snapshot now uses FNV-1a instead
of DefaultHasher — consistent with D-010 principle 4 and the pattern in
TemplateId/TriangleId. Updates golden file for new hash value.
4. WARNING: TriangleCrisisEventWire.role_assignments now filtered against
observer KnowledgeGraph — unknown NPCs redacted from wire event per
D-010 principle 2 (information boundaries).
5. WARNING: ActiveTemplateInstances::insert now despawns previous instance
entities before overwriting — prevents orphaned ECS entities.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Protocol v17: add state_hash (desync detection) and sim_errors
(structured error reporting) to ObserverSnapshot. Add SimError,
SimErrorKind, SimErrorBuffer types. Wrap main loop app.update() in
catch_unwind — on panic, send a final SimError snapshot before exit.
Report recoverable deserialization errors to client via SimErrorBuffer.
Compute per-tick state hash from player position + NPC count + tick.
Update all test fixtures and golden files for protocol v17.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates all client-side msgpack test fixtures and server test
harnesses to include the new triangle_crisis_events field added
in protocol v16.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Protocol handshake (#555): HandshakeMessage as first IPC frame,
HandshakeState resource, forward-compatible input handling.
State serialization (#96): serialize_npc_to_frozen/deserialize with
full D-024 axis coverage (10 new optional fields on NpcSaveState).
Scope tags (#98): ScopeTagKind enum, ScopePinned marker, automatic
assignment from KnowledgeGraph and RelationshipGraph.
Timestamp eviction (#97): LastInteractionTick, SimSpacePressure,
BinaryHeap LRU eviction respecting ScopePinned entities.
Save/load (#553): save_to_file/load_from_file via MessagePack,
SaveGame/LoadGame IPC commands, SaveLoadResultWire on snapshot.
Test infrastructure (#200): Layer 3 integration test entry point,
three-layer architecture documented per D-030.
Information boundary tests (#272): 4 negative tests proving no
passive KG leakage, LOS fog holds, tier boundary holds, save
isolation per NPC.
1063 tests passing, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regenerated MsgPack fixtures for protocol v14 (examine_result and
character_pressure fields). Updated golden test, serialization
assertions, and cross-language fixture generator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add three new ObserverSnapshot fields for client Sprint 18 tickets:
poi_list (Vec<PoiWire>) for minimap #151, examine_result
(Option<ExamineResultWire>) for #174, player_knowledge
(Option<PlayerKnowledgeWire>) for journal #264. POI list populated
live from KG-discovered PointOfInterest components. KG dump
serializes entity/fact knowledge with confidence, source, and state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regenerate all msgpack test fixtures with tell_state and follow_state
fields. Update golden proof_room snapshot. Adjust serialization and
bridge tests for new ObserverSnapshot fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Server-side infrastructure for information asymmetry in dialogue.
NPCs display role labels (Worker, Supervisor, Patron) instead of
real names until the player explicitly learns them via KG lookup.
NpcColorIndex (stable_id % 8) persists across name reveal.
ConversationEvent and DialogueResponseEvent carry display names +
color indices on the wire with serde(default) for compat.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds rng_seed: Option<u64> to ObserverSnapshot. The WRONG button (#507) captures
inputs.jsonl and seed.txt for replay, but seed.txt was writing "unavailable"
because the server did not include the RNG seed in ObserverSnapshot.
Changes:
- bridge/types.rs: PROTOCOL_VERSION 9→10, rng_seed field with serde(default,
skip_serializing_if = "Option::is_none") for backward compatibility
- perception/observer/mod.rs: inject Res<SimRng> into compute_observer_snapshot,
populate rng_seed: Some(rng.seed()) each tick
- All test files: add rng_seed: None to ObserverSnapshot constructors
- tests/serialization.rs: bump protocol_version_constant assertion 9→10
- Regenerate msgpack fixtures and golden file for protocol v10
Completes the WRONG button capture loop: replays can now fully reproduce
observed bugs with the exact RNG seed from the capture.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements 4 completed tickets + partial progress on 2 more:
- #93 Tier marker components (ActiveSim, BackgroundSim, StateSaved + TierPlugin)
- #138 Information tag schema (ObserverAccess enum in knowledge/types.rs)
- #124 Sound event system (SoundEventEmitter, SoundEventQueue, bridge wiring)
- #193 Line previewer CLI (line_preview binary with filter/explain/sequence modes)
- #94 Active tier simulation (in progress — With<ActiveSim> filters)
- #139 Component-level access control (in progress — filter_by_access)
Updates snapshot fixtures and test golden files for new sound_events field.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cross-room transition tests (server/tests/cross_room_transitions.rs):
- T1: sprint suppresses interaction buffer, restores on Walk (D-055)
- T2: CarriedBy survives room transition — no TilePosition leak (D-065)
- T3: pause mid-corridor discards movement, Unpause resumes (D-031)
- T4: KnowledgeGraph persists across player position change (D-041)
- T5: entity knowledge downgrades Direct→KnowsDetails on LOS exit (D-060)
- T6: eavesdrop cut immediately on first movement out of corner (D-071)
- T7: confrontation verb disappears on retreat beyond MID_RANGE=5 (D-057/D-070)
- T8: Sprint blocks eavesdrop accumulation, Careful enables it (D-055+D-071)
All 8 tests pass. Test suite grows from 545 → 563 (18 tests added across sprint).
Tests use direct ECS World + Schedule pattern; T3 uses full App + SimulationPlugin.
Test suite expansion:
- content_scaling.rs: max_npc_pack_behavioral_regression + stress tests (#513)
- golden/proof_room_tick_10.json: updated golden file for gauntlet world changes
- golden_suite.rs, serialization.rs, bridge_ipc.rs, bridge_tcp.rs: adapted to
new world entity count and wire types
- gen_fixtures.rs, perf_bench.rs, content_runtime.rs: minor test adaptations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>