Commit Graph
290 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.8 a69f4cf3ec feat(simulation): wire L3->L4 GenerateSkeleton dispatch (T-1022)
The Layer-4 quarter-skeleton geometry (D-234: morphology-correct streets +
the D-234b waterfront rule) was fully implemented but dormant — no
production call site dispatched GenerateSkeleton from a settlement
placement, so founding_orientation stayed pinned to the
context_from_read_set Cardinal stub.

Expand the BodyAnalyzed arm of drain_generation_completions to, before
caching the body, submit one GenerateSkeleton per CityPlacement: build the
D-199 context from the read-set, override founding_orientation with the
attractor-matched value carried on the placement (D-213), and derive a
canonical namespace-isolated quarter_id from (world_seed, body, city)
(D-194/D-230) instead of the city_id*10 placeholder. Dispatch logic is
extracted into the pure build_skeleton_work_item helper for DB-free testing.

Reader + world seed are optional system params (mirrors serve_atlas_requests);
absent either, dispatch is skipped and the body is still cached. Empty
placements and per-placement read_set errors are handled (warn + skip).
Station/multi-source dispatch (Q-109) is out of scope — BodyAnalyzed only
fires on the planetary AnalyzeBody path.

Tests: orientation-override + canonical-quarter_id unit test and a
determinism/city-scoping test on the helper. Full lib suite green (1327).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 13:30:51 +02:00
jpmschweitzerandClaude Opus 4.8 6e24cc98df refactor(simulation): remove superseded skeleton String stubs (T-984)
D-229 makes BuildingPropertyTag the typed step-3 output that replaces the era/
society_profile/zone_palette String stubs on the skeleton. The tag + its derivation
(zone_type_id, flavor_ref, era/ConstructionEra, FloorExtent) all landed under T-957/
T-1006; this removes the now-dead stub fields they superseded:

- BlockSkeleton.era_modifications: Vec<EraModification> (era is per-footprint on
  BuildingPropertyTag.era now; era_cause retained as the block-level cause)
- QuarterSkeleton.society_profile: SocietyProfileRef (culture → tag.flavor_ref)
- QuarterSkeleton.zone_palette: Vec<ZoneDefinition> (zoning → tag.zone_type_id;
  distinct from FloorZone.zone_palette: ZonePalette, the real D-101 palette, kept)
- the now-orphaned `EraModification` and `SocietyProfileRef` String aliases

Kept: chunk_layout (real ChunkLayout / D-234 street network), z_band_zones (still
uses the ZoneDefinition alias), boundaries (separate stub, not D-229 scope).

cargo check + clippy -D warnings --all-targets clean; full server lib suite
1325 passed / 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 23:40:21 +02:00
jpmschweitzerandClaude Opus 4.8 f880635b24 fix(simulation): decorrelate per-footprint tag fields + test gaps (#957)
PR #153 review (Hoshe H1): zone_type/era/floors/flavor all shared one fp_chain
and each took splitmix64(seed) of the same value, correlating the fields (era
tracking floor height across a city). Derive a distinct sub-chain per field so
they draw independent entropy. Block_tags aren't serialized, so no fixture
change; behavior stays deterministic.

Also (review): n=1 corridor test (empty, no panic), Frontier-density waterfront
test, and a comment on the reserved `_morphology` param (Tyre).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 10:55:07 +02:00
jpmschweitzerandClaude Opus 4.8 b73b4e6d92 feat(simulation): Layer-1 water bearing + D-234 waterfront rule (#957)
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>
2026-06-06 10:22:14 +02:00
jpmschweitzerandClaude Opus 4.8 4c6fe596d1 feat(simulation): quarter street network — typed corridors/access/lattice (#957)
Complete the Layer-4 quarter geometry with the street layer (D-234), replacing
the ChunkLayout/CorridorSpine/AccessPoint String stubs with real types:
- AccessPoint{position,kind} + AccessKind (QuarterEdge/ReservationGate/
  BlockJunction): the quarter's road nodes (from road_entry_directions octants +
  reservation gates), the node set the D-097 audit reads.
- CorridorSpine{from,to,path}: arterial trunk edges, ±45°-snapped polylines
  (D-096 cap). Topology gated by morphology (D-234a): Ribbon (fjord/canyon/
  mountain-pass), HubSpoke (delta/island/enclosed water), Mesh/Prim-MST
  (plains/meander/coastal). A spanning tree over the access nodes.
- ChunkLayout{spacing,offset,rotation_steps}: per-block local ±45° lattice,
  spacing from density, offset/rotation modulated by the D-096 Grid/Organic mode.

Wired into generate_quarter_skeleton (access_points + corridors + per-block
chunk_layout). All integer-deterministic (D-010). 7 new tests.

Remaining D-234 piece: the per-edge waterfront pier/quay rule needs Layer-1
terrain water-adjacency threaded to the skeleton (cross-layer plumbing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:29:21 +02:00
jpmschweitzerandClaude Opus 4.8 728a27e1b4 feat(simulation): quarter footprint subdivision + block-tag assembly (#957)
Build the per-quarter building geometry + tags (absorbs #976's footprint
subdivision into #957 — one coherent walkable-quarter deliverable):

- subdivide_block_footprints: deterministic BSP of each 128x128-tile block into
  variable axis-aligned building plots (D-229 fast-path). Lot size + setback
  scale with D-220 density (Frontier -> few big lots/wide gaps; Compressed ->
  many small lots/shared walls); the D-233 BulkClass roofed-coverage fraction
  decides building-vs-interstitial. morphology is wired in for the D-234
  waterfront/street layer (needs Layer-1 terrain water-adjacency threaded up).
- assign_all_block_tags: subdivides + tags every non-reserved block's footprints
  via the #957 derivation helpers, populating QuarterWorldState.block_tags in the
  GenerateSkeleton plan-phase (D-230). Reserved blocks get no standard fill.
- Remove the write-only BlockSkeleton.era String stub - construction era now
  lives per-footprint on BuildingPropertyTag.era (typed ConstructionEra).

All integer-deterministic (D-010). 5 new tests. Doors stay Vec::new() (#979).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:08:19 +02:00
jpmschweitzerandClaude Opus 4.8 1909e15edb feat(simulation): building-property-tag derivation helpers (#957)
The pure per-footprint tag derivations for Layer-4 quarter generation (D-229),
ahead of the footprint-subdivision + assembly step:
- zone_type_for: the authored (ZoningType × economic_role × setting) → ZoneTypeId
  selection (D-229 amendment) — planetary variants only, setting as a tweaker,
  station-only ids never selected (Q-109); deterministic seed-pick, no empty slice.
- building_entry_class: zone × layout_mode × prosperity, with the D-217 Broken-band
  U-curve degrade (Commercial→BreachOnly) and the Grid/Organic credential fork.
- construction_era: founding_age + prosperity + seed (D-229); old+Broken → Derelict.
- floor_extent: D-220 density-class floor ranges from density_pct + seed-jitter.
- initial_condition: D-217 frozen-amber condition from prosperity + era cause.

All integer-deterministic (D-010). 9 unit tests incl. the no-station-ids invariant.
Footprint subdivision + assign_building_tags assembly + block_tags wiring follow
(needs the footprint-packing model decided).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 22:15:46 +02:00
jpmschweitzerandClaude Opus 4.8 21544cbb47 test(simulation): assert "mixed" faction → ContestedZone (#956)
PR #152 review (Hoshe): the faction→TerritorialStatus mapping test covered 7 of
the 8 D-237 values; add the missing "mixed" assertion (grouped with "disputed",
behavior already correct).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 15:05:40 +02:00
jpmschweitzerandClaude Opus 4.8 af60d85d94 feat(simulation): Layer-3 settlement spatial-character enrichment (#956)
Derive and store each placed settlement's spatial character on top of the
#955 placement (D-212/213/214/215). Derivation is pure + integer-deterministic
(D-010); the cascade stays DB-free (D-225) — the enqueuer pre-resolves the
body's system faction onto the work item, like #955's settlements.

- TerritorialStatus (D-212): mapped from the authored dominant_faction
  (territorial_status_from_faction). Adds an AutonomistHeld variant for the
  Compact of Westphalia (self-governing bloc that rejects Assembly authority —
  neither Commission, Corp, Contested, nor truly Frontier). Grounded in
  wiki/factions/. Stored per-province on DrainageBasin.territorial_status
  (uniform per body for now; forward-compatible for per-province faction data).
- PoliticalArchetype (D-214): political_archetype(status, role), status takes
  precedence over economic_role.
- ArrangementPattern (D-215): new 5-variant enum + arrangement_pattern(); the
  block-adjacency *enforcement* stays deferred to the Quarter-skeleton gen (#957)
  — this only derives + stores which pattern applies.
- FoundingOrientation (D-213): existing fn extended with a seed-derived Free
  bearing so pioneer/open-terrain grids vary per seed.

Wiring: dominant_faction threaded through CityContextReader
(read_body_dominant_faction) → atlas proxy (cache-miss read) → AnalyzeBody work
item → run_cascade → run_layer3/match_cities. Enrichment stored on CityPlacement
(political_archetype, arrangement_pattern, founding_orientation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 14:57:09 +02:00
jpmschweitzerandClaude Opus 4.8 8b1005221d docs(simulation): finish district→quarter doc residue in gen_queue (#950)
PR #151 review (Hoshe/Tyre): four doc comments in gen_queue.rs still said
"district" next to the renamed quarter_id field / generate_quarter_skeleton.
Pure comment update, no code change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 13:15:36 +02:00
jpmschweitzerandClaude Opus 4.8 c71c26a1a6 refactor(simulation): rename DistrictSkeleton→QuarterSkeleton per D-222 (#950)
D-222 renamed the 512m generation cell from District to Quarter (District
is now a new 2048m tier above it). Align the generation skeleton code to
the canonical vocabulary. Pure naming — no behavior change; all 1296 lib
tests + integration tests pass unchanged.

Renamed (spatial-cell identifiers):
- DistrictSkeleton → QuarterSkeleton, DistrictWorldState → QuarterWorldState
- DistrictId → QuarterId, DistrictContext/DistrictBoundaries → Quarter*
- field district_id → quarter_id, district_type → quarter_type
- BodyWorldState.districts map → .quarters
- generate_skeleton → generate_quarter_skeleton

Deliberately left as-is (these name functional ZONING, not the spatial
tier — orthogonal to D-222): DistrictType, DistrictLayoutMode, the
district_mix module (DistrictMix/compute_district_mix), and the
GenWorkItem::GenerateSkeleton / GenCompletion::SkeletonGenerated variants.

Also aligned the perception "sim tile" → "subtile" vocabulary (D-222:
Subtile = 0.5m) in decisions/perception.md and the generation-cascade code
comments. Historical D-066/D-094/D-201/D-220 decision bodies keep their
existing D-222 amendment notes (not rewritten in place); the public
max_offset_sim_tiles fn name is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 13:06:44 +02:00
jpmschweitzerandClaude Opus 4.8 f1d52944af fix(simulation): correct crate name in log10_floor doctest
The doctest imported `server::bps::log10_floor`, but the library crate is
`settled_reach_server` — so `cargo test --doc` failed to compile the
example (unresolved module `server`). Broken since the helper landed in
#145; it only surfaces under `--doc`, not `--lib`, so the pre-push gate
(which runs `--lib`) never caught it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:44:06 +02:00
jpmschweitzerandClaude Opus 4.8 2d8367d3bc docs(simulation): correct stale cascade docs and pin layer order (#955)
Address the doc/test-correctness items from PR #149 review:

- cascade.rs: Layer 3 is RNG-free (pure fn of attractors+cities); it does
  not consume the carried SeedChain. Corrected the "first RNG-using layer"
  claims on the module doc and the Settlement variant (Hoshe H3).
- cascade.rs: layers_are_ordered now also asserts Topography < Settlement,
  pinning the invariant the up_to >= Settlement guards rely on (Tyre T2).
- features.rs: strength is an integer 0-100 (quantized at extraction), not
  f32, and consumers that rank by it use the integer value (Hoshe H2).
- layer_proxy.rs: AtlasLayerRequest.up_to is not yet honored — run_work_item
  runs through Settlement unconditionally; per-request depth deferred to
  #1021 (Tyre T1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:33:36 +02:00
jpmschweitzerandClaude Opus 4.8 9d8ff8b341 fix(simulation): fall back on unrecognized settlement_class (#955)
read_body_settlements propagated a parse error for any non-NULL but
unrecognized settlement_class, which the atlas proxy swallowed and then
enqueued the body with zero cities — one malformed row silently dropped
placement for the entire body. Treat an unknown variant like NULL: fall
back to PopulationBudget with a warning. parse_settlement_class stays
strict for the D-199 read_set path, which must abort on bad fields.

Found in PR #149 review (Hoshe H1). Adds a test covering the fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:33:24 +02:00
jpmschweitzerandClaude Opus 4.8 e2787fbfb1 feat(simulation): run Layer-3 settlement placement in the runtime cascade (#955)
Wire the existing D-211 attractor-matching engine into the live
generation cascade so settlements are placed in-game, not just in tests.

- CityContextReader::read_body_settlements reads a body's settlements
  from atlas_city_names (ordered by id for determinism). NULL
  settlement_class defaults to PopulationBudget, not NameLocked: the
  class is NULL until placement runs, and NameLocked would force every
  settlement Tier-A in match_cities and collapse population tiering
  (D-211). NULL economic_role falls back to residential.
- The AnalyzeBody work item carries the body's Vec<CityRecord>, and
  run_work_item now runs up_to Settlement (was Topography). A body with
  no settlements yields empty placements at negligible cost.
- The atlas layer proxy reads settlements on a cache miss and pins them
  onto the work item, keeping the Rayon task DB-free (D-225). A read
  failure is non-fatal: log and place no cities (Layer 1 still runs).
  Threaded through a new CityContextReaderResource Bevy resource opened
  in main.rs, mirroring BodySourceResolverResource.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:14:18 +02:00
jpmschweitzerandClaude Opus 4.8 ab084269f3 feat(simulation): integer-deterministic Layer-3 settlement placement (#955)
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>
2026-06-03 16:30:57 +02:00
jpmschweitzerandClaude Opus 4.8 d6e37c0c0e fix(simulation): prosperity in basis points + shared log10_floor helper (#954)
Address PR #145 review. Convert prosperity from f32 to integer basis
points (matching the existing prosperity_bps, D-010 integer-only),
removing both float-determinism bugs by construction:

- New server/src/bps.rs: log10_floor (integer order-of-magnitude) +
  bps_to_f32 edge helper, with boundary tests.
- city_context_reader: prosperity_baseline_bps (u32, 0-10000). role base
  + pop bonus (400*log10_floor, cap 1200) + symmetric noise via integer
  modulo (fixes the always-negative bug) + clamp [1000,9500]. No floats.
- CityGenerationContext.prosperity_baseline -> prosperity_baseline_bps;
  updated the two test context builders. Not serialized — no wire break.
- district_mix population_tier now uses log10_floor (same determinism
  bug class as the comment claimed to avoid).
- Tests in bps + assert positive noise is achievable (the case the old
  test hid).

cargo check/clippy --all-targets -D warnings clean; 1291 lib tests pass;
fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 14:31:27 +02:00
jpmschweitzerandClaude Opus 4.8 a854b89aa0 feat(simulation): wire D-199 economic read-set into generation context (#954)
Layer 2 clean half. GenerateSkeleton now builds a CityGenerationContext
from a body's real economic read-set instead of stub defaults.

- New CityContextReader (server/src/atlas/city_context_reader.rs): reads
  the 6-field D-199 set (economic_role, prosperity_baseline, population,
  dominant_faction, founding_age_years, settlement_class) via one JOIN;
  prosperity_baseline derived per D-197.
- GenWorkItem::GenerateSkeleton carries the resolved context; run_work_item
  builds the real context instead of DistrictWorldState::default().
- Left at defaults (deferred, documented): dominant_bulk_class /
  dominant_production_ubiquity (#982, design-blocked), trait_selection
  (#1005), morphology_zone + Layer-3 fields (attractor placement / D-204 /
  D-214).

16 new tests; cargo check/clippy --all-targets -D warnings clean; 1279
lib tests pass; fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 13:46:24 +02:00
jpmschweitzerandClaude Opus 4.8 99da796cde fix(simulation): address PR #143 review (#1006)
- Add D-230 skeleton: DistrictSkeleton field to DistrictWorldState;
  cascade Default to DistrictSkeleton + contained enums/structs. Box the
  GenCompletion::SkeletonGenerated state to avoid large_enum_variant.
- Derive PartialEq on FloorExtent/FloorHeightProfile/DistrictWorldState/
  CityGenerationContext (+ minimal cascade) for downstream assert_eq tests.
- Add 4 unit tests for floor_at_voxel_z / voxel_range_for_floor (uniform,
  basement, variable heights, boundary) — the Q-104 deliverable.
- Drop unused smallvec direct dep (stays transitive via bevy_ecs).
- Key districts insert by skeleton.district_id, sharpen TODO(#957).

clippy --all-targets -D warnings clean; 1263 lib tests pass; fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 22:06:57 +02:00
jpmschweitzerandClaude Opus 4.8 899f447eed fix(simulation): review follow-ups on foundation types (#1006)
- Derive MorphologyZone Default (#[default] AlluvialPlain), drop manual
  impl — clears the clippy --all-targets warning (pre-push gate).
- Fix floor_at_voxel_z doc (described the inverse function).
- Document floor_at_voxel_z / voxel_range_for_floor voxel-z as
  building-relative (base_floor = z:0) for #982-985 callers.
- TODO(#957): districts keyed by city_id is a stub; use district_id.

clippy --all-targets clean; 1259 lib tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 19:16:03 +02:00
jpmschweitzerandClaude Opus 4.8 46b8688fa3 feat(simulation): foundation shared types + struct plumbing (#1006)
Define the type-definition layer the Phase-4 fill-seam tickets depend on
(D-229/D-230/D-231/D-232/D-233), compiling with stubs/defaults; behavior
logic lands in #982-985/#998.

- New types in generator.rs: BuildingPropertyTag, FloorExtent +
  FloorHeightProfile (floor_at_voxel_z/voxel_range_for_floor, resolves
  Q-104), BuildingEntryClass, ConstructionEra, ZoneTypeId, MorphologyZone,
  BulkClass(5), ProductionUbiquity, DoorSpec, InteriorDescriptor,
  DistrictWorldState.
- Rename spatial AccessTier -> ZoneAccessTier to free the name for the new
  per-building BuildingEntryClass.
- CityGenerationContext: +morphology_zone, +trait_selection,
  +dominant_bulk_class, +dominant_production_ubiquity.
- BodyWorldState: +districts (DistrictWorldState w/ block_tags).
- GenCompletion::SkeletonGenerated carries body_id + DistrictWorldState;
  plugin handler inserts into BodyWorldState.districts.
- Add smallvec as a direct dep (DoorSpec list stays Vec for now, TODO).

cargo check --all-targets / clippy clean; 1259 lib tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:07:33 +02:00
jpmschweitzerandClaude Opus 4.7 c1d0d382ac style(simulation): rustfmt trace_outer_boundary (#960)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:15:34 +02:00
jpmschweitzerandClaude Opus 4.7 adbd7f6744 fix(simulation): Layer-1 self-describing grid dims + Moore basin trace (#960)
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>
2026-05-25 10:02:32 +02:00
jpmschweitzerandClaude Opus 4.7 1bece0b201 feat(simulation): serve atlas layer requests over the bridge — completes #969 (D-225)
Closes the server-side layer-stream loop. serve_atlas_requests (PreInput) drains
the AtlasRequestBuffer and runs each through handle_atlas_request (cache hit ->
Ready; miss -> resolve via BodySourceResolver + enqueue an Immediate AnalyzeBody
-> Pending), buffering AtlasLayerResponses. send_atlas_responses (PostSnapshot)
flushes them to the client. main.rs wires BodySourceResolverResource (base root
= systems.db's 3rd ancestor; mod roots layer on later). Misses flow through the
#968 background tier and a re-request hits the now-warm cache.

Full path now live server-side: client request -> receive() demux -> serve ->
proxy -> (cache | queue+cascade) -> response -> client. The client half (send
request, decode response, render overlays) is #960.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 18:06:33 +02:00
jpmschweitzerandClaude Opus 4.7 dfdc578429 feat(simulation): demux inbound bridge stream — receive() + atlas routing (#969, D-225)
Replaces the fixed-type SimBridge::receive_inputs() with a tagged
receive() -> Option<Inbound>, where Inbound is Inputs(Vec<PlayerInput>) or
AtlasRequest(AtlasLayerRequest). A shared decode_inbound() demuxes a frame by
shape (msgpack array = inputs, map = atlas request) — additive, no wire change
to existing input/snapshot frames. Adds send_atlas_response() to the trait
(both TcpBridge + LocalBridge impls). receive_bridge_inputs routes inputs to
the InputQueue as before; atlas requests to a new AtlasRequestBuffer (drained
by the serve system next). Integration tests (bridge_tcp/bridge_ipc) updated to
the tagged receive(); a demux unit test covers all three branches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 18:00:30 +02:00
jpmschweitzerandClaude Opus 4.7 edbc0f4ff1 feat(simulation): atlas layer-proxy handler — cache hit/miss -> queue (#969, D-225)
handle_atlas_request: cache hit -> serialize Layer1Output, reply Ready; miss ->
resolve the source heightmap (BodySourceResolver) + enqueue an Immediate
AnalyzeBody on the #968 background queue, reply Pending (client re-requests; the
drain system populates the cache so a later request hits); unknown/no-terrain ->
NotFound. Adds the AtlasLayerRequest/Response/Status wire types (+ Serialize on
CascadeLayer). Pure handler; the bridge routing is the proxy's other half.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:41:28 +02:00
jpmschweitzerandClaude Opus 4.7 0097fd5027 feat(simulation): mod-first BodySourceResolver (#969, D-225)
First piece of the layer-stream proxy. Resolves body_id -> source heightmap.png
read-only from systems.db (terrain_reference, mirroring CultureResolver's
pattern), searching roots mod-first over the base install. Explicit errors
(UnknownBody / NoTerrainReference / SourceMissing). v1 callers pass the base
root only; the search-order logic is proven with synthetic mod roots so the
mod-first seam is ready (Q-099 covers registering a mod's new bodies).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:36:18 +02:00
jpmschweitzerandClaude Opus 4.7 8cedf670f0 feat(simulation): activate background generation tier end-to-end (#968, D-206/D-225)
The D-206 queue (#924) and D-203 cache (#917) were built but never connected to
the running app — the whole background tier was inert. This closes the loop:
submit -> Rayon -> cascade -> completion -> drain -> cache.

- gen_queue.rs: GenWorkItem::AnalyzeBody now carries enqueuer-resolved inputs
  { body_id, heightmap_path, sea_level, body_seed: SeedChain } (D-225 boundary —
  run_work_item stays pure compute, no path/DB resolution). run_work_item runs
  the real cascade: load heightmap.png -> downsample to GRID_W×GRID_H working
  grid (D-202) -> run_layer1 -> BodyWorldState; load failure -> Failed.
  GenCompletion::BodyAnalyzed carries the computed BodyWorldState.
- cascade.rs: CascadeSnapshot::into_body_world_state() conversion.
- plugin.rs (new): GenerationPlugin registers GenerationQueue +
  BodyWorldStateCache and adds a PreInput drain system that inserts BodyAnalyzed
  states into the cache (off the Rayon workers — a cheap channel drain, never
  the ~45ms cascade). Wired into both the production and test app setups.

Tests run the real cascade on a tiny temp heightmap PNG (no committed fixture);
the plugin test proves the full submit->...->cache loop. The proxy (#969) is the
production submitter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:47:59 +02:00
jpmschweitzerandClaude Opus 4.7 a61d030b47 refactor(simulation): harden #952 cascade per QA + architecture review
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>
2026-05-23 13:24:03 +02:00
jpmschweitzerandClaude Opus 4.7 aaee9ecb06 test(simulation): golden-seed regression for the Layer 0->1 cascade (#952, D-200)
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>
2026-05-23 12:26:07 +02:00
jpmschweitzerandClaude Opus 4.7 9bc2ed28f2 feat(simulation): extensible generation cascade harness (#952, D-200)
server/src/atlas/cascade.rs — run_cascade(body_seed, body_id, path,
default_sea_level, up_to) loads Layer 0 (heightmap.png) and runs every layer up
to the requested CascadeLayer, returning a CascadeSnapshot. The pure core
run_cascade_from_heightmap orchestrates the layers without file I/O (testable);
run_cascade is the thin path-loading wrapper.

CascadeSnapshot is extensible — each layer's artifact is an Option that becomes
Some once it runs (Layer 0 heightmap always present, Layer 1 topography next;
#954+ append their fields). The carried SeedChain is unused by the RNG-free
Layers 0-1 and feeds the RNG-using layers later (D-224). CascadeLayer is an
append-only ordered enum.

Tests: layer-gating, same-heightmap determinism, layer ordering. The golden-seed
regression fixture (the #952 deliverable) builds on this next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 12:09:49 +02:00
jpmschweitzerandClaude Opus 4.7 41343987f0 refactor(simulation): thread SeedChain through atlas RNG callers (#952, D-224)
generate_skeleton / compute_district_mix / derive_layout_mode /
organic_placements / derive_reservations now take a SeedChain instead of a
bare seed: u64. The two ad-hoc wrapping_add pre-mixing hacks are replaced with
real domain separation:
- district-type allocation draws from chain.derive(SeedDomain::Layer4Quarter, 0)
- organic block placement draws from chain.derive(SeedDomain::Block, 0)

DistrictSkeleton.seed now records chain.seed(); test call sites pass
SeedChain::root(N); stale seed-param docs updated.

Re-tuned all_district_types_can_appear: the seed-stream change exposed it as
latently fragile — every type has a clamped weight >= 1 (reachable), but 50
weighted draws can miss a low-weight type (Administrative) depending on the
sequence; the old seed got lucky. Raised the draw count to 500 so it tests
genuine reachability rather than a lucky sequence. Determinism-affecting by
design — this is the RNG-using layer D-224 flagged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 12:05:19 +02:00
jpmschweitzerandClaude Opus 4.7 de9142ce37 feat(simulation): SeedChain seed-derivation primitive in server/src/seed.rs (#952, D-224)
New top-level seed module that owns every deterministic-RNG primitive:
- splitmix64 — the one canonical mixer (was duplicated as a private fn in
  simulation/rng.rs; EntityRng now imports the shared one, no behavior change)
- AtlasRng — moved here from atlas/rng.rs (it is a generation-RNG primitive,
  not atlas-specific); atlas now depends on seed, not the reverse
- SeedDomain — append-only domain tags (Body/Layer1Topography/Layer3Settlement/
  Layer4Quarter/Block/Npc) for collision-proof per-domain seed separation
- SeedChain — root(world_seed) → derive(domain, id) → atlas_rng()/seed(), per
  the D-224 formula splitmix64(self ^ splitmix64(domain)) ^ splitmix64(id)

Structural move only — SeedChain is not yet threaded through the cascade
callers (skeleton_gen still uses ad-hoc wrapping_add pre-mixing); that is the
next step. Unit tests cover the splitmix64 known-vector, avalanche,
determinism, domain/id separation, and chain composition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 11:51:09 +02:00
jpmschweitzerandClaude Opus 4.7 8eb6c47f74 style(server): clear clippy-1.93 cfg(test)/test-target debt (#967)
Manual clippy-1.93 fixes that the prior machine-applicable sweep couldn't auto-
apply, all in cfg(test) modules and tests/ targets (invisible to the lib-only
pre-push clippy, hence accumulated unflagged):

- disallowed_types HashSet/HashMap → BTreeSet/BTreeMap (determinism rule):
  shadowcast_bench.rs (×8, (i32,i32) keys), mood.rs, sound.rs. SoundEventKind
  gains a PartialOrd/Ord derive (fieldless Copy enum) so it is BTree-usable.
- field_reassign_with_default → struct-init: disclosure.rs, monologue.rs (×2),
  save_io.rs (keeps `mut` for the deliberate last-write-wins overwrite).
- assertions_on_constants on the EAVESDROP_THRESHOLD invariant → compile-time
  `const _: () = assert!(...)`: listening.rs, cross_room_transitions.rs. This is
  stronger than the runtime assert and needs no #[allow].
- approx_constant: settings/types.rs round-trip literal 3.14 → 2.5 (the value is
  arbitrary test data, never meant to be PI — change avoids both the lint and a
  suppression).
- drop_non_drop: vision.rs early Mut<WalkabilityMap> release → scoped block.
- unnecessary_get_then_check → contains_key: information_boundaries.rs (×3).
- cloned_ref_to_slice_refs → std::slice::from_ref: triangle_validation.rs.
- unused_must_use: input.rs dropped the unused .id() on a spawn.

cargo clippy --all-targets -- -D warnings is clean; cargo test green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 11:03:15 +02:00
jpmschweitzerandClaude Opus 4.7 9a10c6ffd6 chore(deps): rand 0.9.4 + compatible sweep + serde_yaml→serde_norway (#966)
Focused Rust dependency-maintenance pass from the 2026-05-23 security/freshness
review. No CVEs; one advisory cleared and one deprecated crate replaced.

- rand 0.9.2 → 0.9.4 (lockfile): clears RUSTSEC-2026-0097 (unsound with a
  custom logger using rand::rng()). Semver-compatible; rand 0.10 is a separate
  major.
- Compatible-update sweep: ~90 lockfile-only patch/minor bumps (bevy 0.18.0→
  0.18.1, clap 4.5→4.6, rayon 1.11→1.12, pathfinding 4.14→4.15, uuid 1.20→1.23,
  zerocopy, serde_json, tracing-subscriber, etc.). cargo test green.
- serde_yaml 0.9 (deprecated/archived upstream) → serde_norway 0.9, an actively
  maintained drop-in fork. In the server it is test-only (poi.rs round-trip,
  trait_modifiers.rs fixture, tests/news_ticker.rs) so it moves to
  dev-dependencies; line-previewer parses dialogue/monologue pool YAML at
  runtime, so it keeps it as a normal dependency. API is identical (from_str/
  to_string).

news_ticker.rs also picks up its share of the #967 clippy sweep (HashSet/HashMap
→ BTree, doc-list indent) since it is the same file as the serde rename.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 11:02:57 +02:00
jpmschweitzerandClaude Opus 4.7 88712ba54f style: clippy-1.93 machine-applicable auto-fixes (#967)
cargo clippy --fix on files the new clippy (1.93) flags: unused imports
(name_index, storyteller), manual_range_contains (block_irregularity),
length-comparison/is_empty (layer3, serialization). All behavior-preserving.
Surfaced because a warm target/ makes the pre-push hook actually run clippy
(it skips on cold worktrees). Remaining non-auto-fixable test-code lints
tracked in #967.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 10:42:22 +02:00
jpmschweitzerandClaude Opus 4.7 f3e017a12f style(simulation): rustfmt + clippy fixes for Layer-1/heightmap code (#953 #963)
Pre-push surfaced fmt + clippy (-D warnings) failures in the new code:
- rustfmt the 6 new/changed atlas files + the bench example.
- features.rs: HashMap → BTreeMap (project bans HashMap for determinism via
  clippy disallowed_types; the bucket map is lookup-only either way).
- attractor_matching.rs: drop now-redundant .clone() on AttractorType (it
  became Copy in #953) — clippy clone_on_copy.
- drainage.rs tests: manual range → (1..=12).contains(&n).
- features.rs test: drop .clone() on Copy AttractorType.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 10:11:43 +02:00
jpmschweitzerandClaude Opus 4.7 6334b0ddbe docs(simulation): fix stale 2048×1024 heightmap resolution in loader docstring (#963)
The heightmap.rs module docstring still said 'canonically 2048×1024' (the
pre-decision figure); the canonical resolution is 1024×512 (D-202 amended).
Clerk caught the contradiction. Now consistent across D-200/201/202/208 + code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 09:26:01 +02:00
jpmschweitzerandClaude Opus 4.7 b71f339996 feat: complete file-based heightmap migration — tEXt sea_level, client relief, drop BLOB (#963)
- heightmap.rs: read sea_level from the PNG tEXt chunk (bake writes it),
  default-fallback param; new test reads_sea_level_from_text_chunk.
- client atlas_viewer.gd: load reliefmap.png (color display) instead of
  heightmap.png (now 16-bit grayscale elevation, cascade-only).
- drop atlas_body_heightmaps: removed from systems-schema.sql; DROP TABLE in
  import_economics MIGRATION_SQL (the PNG is the store now).
- D-202 amendment: implementation-status note (consumer + producer done),
  resolving the review's 'reads done but producer pending' point.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:23:23 +02:00
jpmschweitzerandClaude Opus 4.7 dd296b1ae5 test: review-driven determinism + coverage (#953 #963)
From the Hoshe/Tyre review:
- drainage: assert flow_accumulation/max_accumulation determinism + clamp ≥ 1
  (the D-209 strength denominator); isolated-basin merge path (no panic).
- subbiome: each derivable variant reachable + Volcanic never emitted.
- planet_simulation: new test_sim_determinism.py — same body simulates to a
  bit-identical elevation array at 1024×512 (the 271-body bake can't be cheaply
  re-run, so a silent drift = full re-bake). Verified PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:09:26 +02:00
jpmschweitzerandClaude Opus 4.7 abcb41deaa fix(simulation): type-aware attractor cap so RiverMouth isn't crowded out (#953)
Review (Hoshe) caught that the MAX_ATTRACTORS cap sorted by global strength,
and RiverMouth's normalized strength (accum/max_accum) is tiny — so on a
realistic body 108 river mouths produced 0 surviving RiverMouth attractors,
violating D-209 ('RiverMouth: always high-value') and starving #955 placement.
Replace the global-strength cap with group-by-type + round-robin so every
present type keeps representation (strongest-first within each type).
Deterministic. New test river_mouths_survive_cap locks it in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:08:57 +02:00
jpmschweitzerandClaude Opus 4.7 3886cea46e feat(simulation): load canonical 16-bit grayscale heightmap.png + downsample (D-202 #963)
Replace the atlas_body_heightmaps DB-BLOB loader with a per-body file
loader (D-202 amendment): heightmap.rs reads the 16-bit grayscale
heightmap.png (via the new png dep) from the body's terrain_reference
path, normalizes to f32 [0,1], and rejects RGB so a reliefmap can't be
misread as elevation. Adds BodyHeightmap::downsample (box-average,
deterministic) so Layer 1 drops the high-res stored heightmap to the
512x256 working resolution. sea_level becomes body metadata carried
alongside, not in the PNG. 4 loader tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:35:58 +02:00
jpmschweitzerandClaude Opus 4.7 d418eba8bb feat(simulation): Layer-1 topography pipeline — features, sub-biome, orchestrator (#953)
Wire the empty-world topography cascade (D-208/209/210):
- generator.rs: add SubBiomeVariant (11 variants, D-210) + sub_biome and
  terrain_modification_cost fields on GeographicAttractor; AttractorType
  is now Copy.
- features.rs (new, D-209): extract the 7 attractor tags from heightmap +
  drainage. Coast/lake derived from the heightmap (D-209/D-223
  reconciliation — markers are names-only now, no polygons). Deterministic
  (sorted seeds, integer keys, bucket-grid thinning); strength-capped at
  MAX_ATTRACTORS preserving type diversity. Shared TerrainAnalysis
  (masks/slope/moisture/percentile) feeds both extraction and sub-biome.
- subbiome.rs (new, D-210): classify sub-biome + terrain_modification_cost
  from elevation/slope/moisture/latitude. Volcanic stays in the enum but
  is not emitted (no heightmap signal).
- layer1.rs (new): run_layer1 orchestrator + attach_feature_names (D-223
  pool names to largest rivers / Alpine peaks).
- attractor_matching constructors updated for the new fields.

76 atlas tests pass; run_layer1 determinism verified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:35:39 +02:00
jpmschweitzerandClaude Opus 4.7 7cf7614342 perf(simulation): expose flow accumulation + optimize basin merge in D8 drainage (#953)
- DrainageResult now exposes flow_accumulation + max_accumulation for
  D-209 attractor-strength normalization.
- Rewrite merge_small_basins from an O(merges x n) loop (rescanned the
  whole grid per merge) to an adjacency-graph + union-find pass: one grid
  scan, lazy merges. Cuts D8 drainage at 512x256 from ~299ms to ~45ms,
  meeting the D-208 ~50ms target (the module had never been run at
  canonical resolution before — it was orphaned). Determinism preserved
  (smallest by (size,id), largest neighbor by (size, lowest id)).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:35:16 +02:00
jpmschweitzerandClaude Opus 4.7 b11e847337 chore(tooling): retire atlas geometry generator + LLM naming cluster (D-223 #951)
The procedural server cascade (Phase 4) and the frozen names-only pool
supersede the Python atlas geometry generator and the LLM namer. Retire:

- generate_atlas.py (geometry production — cities/roads/rivers placement)
- gemma_naming.py, naming_core.py + tests (test_batch_naming,
  test_register_selection, qa_naming) and run-atlas-naming.sh (the LLM
  place-namer; its output is now the frozen pool)
- apply_name_fixes.py (name-field patches), fix_fewshot_bleed.py /
  prune_atlas_features.py (geometry tools)
- import_city_names.py (redundant with import_economics name-pool path)

Pipeline updates: drop the generate_atlas step + atlas-generate /
test-atlas-determinism targets from the Makefile; remove generate_atlas
from the stamp registry (import_economics is the sole regen-db generator);
drop run-atlas-determinism from tests/run-all; refresh stale references in
schema_version, backfill_cultural_corridor, earth_blocklist (kept as
reference data), populate_terrain_reference, and heightmap.rs.

The Gemma prompting methodology is preserved in
docs/gemma-naming-methodology.md (separate commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:29:08 +02:00
jpmschweitzerandClaude Opus 4.6 0d09d905e4 fix(simulation): address PR #142 review round 2
gen_queue: add in_flight_count tracking for all work item types (not
just AnalyzeBody). Rewrite saturation test with AnalyzeBody items.
Fix priority_ordering test thread count to match new gate.

rng: collapse to single AtlasRng::new(seed) constructor — callers
own their seed transform.

import_province_boundaries: fix "savepoint" comment to "transaction".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 15:51:10 +02:00
jpmschweitzerandClaude Opus 4.6 332b9404be fix(simulation): address PR #142 review — 11 issues resolved
Rust fixes: D-218 Backwater complexity (moved to Full arm), priority
queue dispatch gated on thread saturation, panic→soft-fail in
name_index, duplicated LCG unified into atlas::rng module, misleading
Safety comment removed, D-210 SubBiomeVariant deferred to #948.

Schema/data fixes: atlas_city_positions table (D-211 persistence),
settlement_class column on atlas_city_names, per-body transaction
boundaries in import_province_boundaries, import_city_names.py added
to GENERATOR_SOURCES, Python perf documented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 15:30:50 +02:00
jpmschweitzerandClaude Opus 4.6 f1278f88fd chore(simulation): cargo fmt atlas modules
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 09:46:16 +02:00
jpmschweitzerandClaude Opus 4.6 6582ee4cca fix(simulation): resolve clippy warnings in atlas modules
Replace HashMap/HashSet with BTreeMap/BTreeSet per D-030 determinism
rule. Fix while_let_loop, map_or simplification, collapsible if,
unsigned_abs casting, iterator indexing, and redundant wildcard arms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 22:52:15 +02:00
jpmschweitzerandClaude Opus 4.6 ca9ab189e0 feat(simulation): generation pipeline Rust types + SystemNameIndex (#900, #912-#926)
WorldTier enum fixed to Epicenter/Regional/Backwater/Passage/Waypoint
(D-218). Full enum implementations for ComplexityTier, SettingType,
SettlementClass, DistrictType, PoliticalArchetype, FoundingOrientation,
TerritorialStatus, GeographicAttractor, AttractorType, and
CompatibilityMatrix. SystemNameIndex with Aho-Corasick text scanning
for background pre-generation queue integration (D-206).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 18:40:44 +02:00