Compare commits

..
255 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.6 8c5a1639f5 chore(meta): release v0.1.35
Sprint 35: Atlas — Gemma 4 naming pipeline, atlas implant UI,
brand layer schema, 24,963 named features across 299 systems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 11:49:09 +02:00
jpmschweitzer cd75802f3f Merge remote-tracking branch 'origin/sprint-35/server' 2026-04-18 00:47:33 +02:00
jpmschweitzerandClaude Opus 4.6 160c3f1853 fix(tooling): PR #130 round 2 — stale strings, O(1) dedup, vestigial scope (#833)
- Fix "Gemma 2 GGUF" in user-facing error message (line 2073)
- Fix gemma2.gguf in docstring usage example (line 32)
- Fix O(N) _is_duplicate: pre-build lowercase shadow sets for O(1) lookup
- Expand vestigial note to enumerate full ~750-line dead island boundaries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:43:11 +02:00
jpmschweitzerandClaude Opus 4.6 8ba3a1d0a7 fix(tooling): address PR #130 review findings (#833)
Hoshe:
- Mark --dump-prompts / name_feature() as vestigial with TODO note
- Fix --refresh help string: 200 → 1000 (matches actual default)
- Fix _RIVER_POOLS comment numbering: Pool 6 before Pool 5 → correct order
- Remove dead first-pass code in fix_fewshot_bleed.py
- _CAPTURE_FILE leak noted in vestigial TODO

Tyre:
- Fix stale "Gemma 2" strings in banner, argparse description, model help
- Note dead code for cleanup pass (name_feature ~700 lines)

Hoshe (prune):
- prune_atlas_features.py: named features sort before unnamed, preventing
  silent discard of hand-authored names during pruning

naming_core:
- v0.2: few-shot blocklist, stricter is_valid_name (min 3 chars, no digits,
  no brackets), prompt fragment rejection expanded

Miri clarification: the 261 "empty-string" files contain only roads (37)
and railroads (37) — infrastructure features never in naming scope. All
cities/rivers/oceans/mountains/POIs are clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 23:27:44 +02:00
jpmschweitzerandClaude Opus 4.6 9aaaf95742 content(atlas): name 24,963 features across 299 systems via Gemma 4 pipeline (#833)
Batch naming pass across the full Settled Reach — 2,394 bodies, 299
systems, all corridors from Gateway (hop 0) to Abzu (hop 23).

Coverage: cities 100%, rivers 100%, oceans 100%, POIs 99.7%,
mountains 95.6% (699 gaps on barren moons from register exhaustion).

QA issue rate: 3.89%. Post-generation fixes applied: bracket artifacts,
few-shot bleed replacements, placeholder fills, cross-corridor corrections.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:44:39 +02:00
jpmschweitzerandClaude Opus 4.6 508d6a7ca9 docs(architecture): atlas naming pipeline reference (#833)
Full architecture doc covering the Gemma 4 batch naming pipeline:
pipeline stages, cultural registers, body ordering, known limitations,
QA process, and extension guide.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:09:57 +02:00
jpmschweitzerandClaude Opus 4.6 9ad9b88d7c feat(tooling): Gemma 4 batch naming pipeline with wiki-grounded register selection (#833)
Replace the one-at-a-time Gemma 2 naming pipeline with a batch-oriented
Gemma 4 E2B pipeline. Key changes:

- naming_core.py: shared library with Levenshtein distinctiveness ranking,
  batch prompt building, mood injection pool, name validation, and
  adjacent-register refill logic
- Wiki-grounded register selection: per-system LLM call picks the cultural
  register based on wiki/GTTR content instead of hash randomizer
- Batch naming: requests N*2 names per call, ranks by word-average
  Levenshtein distance, fills quota from most-distinct candidates
- Mood pool: 13 emotional seeds randomized per-body for vocabulary
  divergence (ambition, fear, isolation, defiance, etc.)
- Adjacent-register refill: when primary register exhausts, automatically
  switches to next corridor substyle
- Inhabited-first body ordering: habitable worlds get first pick of
  register vocabulary, barren moons get leftovers
- Process group cleanup: SIGTERM/SIGKILL the full distrobox chain on
  subprocess refresh to prevent GPU zombie processes
- qa_naming.py: QA report, fix_fewshot_bleed.py: post-hoc fix script
- test_batch_naming.py, test_register_selection.py: test harnesses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:09:23 +02:00
jpmschweitzerandClaude Opus 4.6 474b31b214 fix(tooling): only log rejections when all attempts exhausted (#833)
Individual dedup/blocklist/placeholder/empty rejections that recover
on the next attempt are now silent. Only the skipped: summary line
prints when all 5 attempts fail. Subprocess errors still print
immediately (those indicate a real problem).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:39:46 +02:00
jpmschweitzerandClaude Opus 4.6 b63c2be8dd feat(tooling): 5 retry attempts + refresh every 1000 requests (#833)
Bumped max_attempts from 3 to 5 — with per-system dedup and no stem
cap, the remaining dedup hits are mostly per-body collisions which
a couple extra attempts with rotated pools can escape.

Bumped --refresh default from 200 to 1000. Fewer subprocess restarts
= fewer model reloads via distrobox. KV-cache bleed risk is lower
now that the validation gauntlet is lighter.

Reverted the batch-prompt experiment — Gemma 2 2B drifts on
multi-line output; individual calls are more reliable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:36:46 +02:00
jpmschweitzerandClaude Opus 4.6 05a1366544 refactor(tooling): drop stem cap + scope dedup to per-system (#833)
The stem cap (--stem-cap 20) was rejecting valid names because common
feature-type vocabulary tokens like "ridge", "hill", "range" hit the
cap after ~200 bodies and blocked all subsequent names containing
them. With sub-style rotation already providing variety, the cap was
doing more harm than good. Removed entirely.

Cross-body dedup narrowed from (hop, corridor, feature_type) to
(system_id, feature_type). Two rivers in the same system can't share
a name; two rivers in different systems can. This matches how
settlers actually name things — they don't coordinate with other
star systems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:30:13 +02:00
jpmschweitzerandClaude Opus 4.6 cb178a8c57 feat(tooling): per-system sub-style rotation across all corridors (#833)
Replace single-inflection corridor palettes with lists of sub-styles.
Each system picks one deterministically via hash(system_id), so all
bodies in the same system share a cultural register but neighbouring
systems get different registers.

Core corridor splits into 6 sub-styles (English rural, British
colonial, US rural, US cosmopolitan, classical/institutional,
Australian/NZ). North/south/east/west reach each get 5 sub-styles
covering their cultural spectrum. Deep frontier gets 3 (founder-name,
surveyor-descriptive, outpost-functional).

This multiplies Gemma's effective vocabulary per corridor by the
sub-style count, dramatically reducing dedup pressure. A 6-style
core corridor means each sub-style serves ~4 systems instead of 24,
so "The Ridge" exhausts after ~4 systems, not ~24.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:53:02 +02:00
jpmschweitzerandClaude Opus 4.6 3ab28138a0 feat(tooling): skip dedup-exhausted features instead of palette fallback (#833)
When all 3 LLM attempts are rejected (dedup, blocklist, etc.),
name_feature now returns None instead of a deterministic palette
fallback. process_body leaves the name as null in markers.json.

The preserved path (_is_blank) treats null as unnamed, so a fill
round (re-running the script) picks up only the skipped features
with a fresh corpus — zero dedup pressure from the first pass. The
fill round can use a different seed, slower prompt, or a different
backend entirely (e.g. Haiku).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:42:32 +02:00
jpmschweitzerandClaude Opus 4.6 c767f82fbb feat(tooling): scope cross-body dedup to hop+corridor (#833)
Changed dedup key from (corridor, feature_type) to
(hop, corridor, feature_type). Systems at the same gate-hop distance
in the same corridor are near neighbors and shouldn't share feature
names; systems at different hops can. This prevents corpus exhaustion
where Gemma's narrow range-name distribution ("The Ridge", "Blackwood
Range") collides after ~20 bodies and drives fallback rates toward
100%.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:37:28 +02:00
jpmschweitzerandClaude Opus 4.6 a64077f1ad chore(tooling): renumber feature IDs in all markers.json (#833)
The upstream terrain pipeline assigns sparse IDs (range_1, range_50,
range_29...) and the prune pass drops entries but keeps original IDs.
This leaves 2394 bodies with non-sequential IDs across mountain_ranges,
rivers, and oceans.

Renumbered all feature IDs to sequential {prefix}_0, {prefix}_1, ...
preserving sort order. 24021 IDs fixed across 2394 bodies. No name
or geometry data changed — only the id field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:37:12 +02:00
jpmschweitzerandClaude Opus 4.6 e0707d8072 feat(tooling): always log retry reasons in gemma_naming.py (#833)
Retry rejection lines (dedup, blocklist, placeholder, stem_cap, empty,
error) now print unconditionally, not only under --verbose. The
fallback line also includes a tally of the rejection reasons that
exhausted all attempts, e.g.:

    fallback: GJ144e-1/range_43 → 'Kirkwood Spine'  [blocklist=2 dedup=1]

Diagnostic run on 20 bodies confirms dedup is the primary fallback
driver. Gemma converges on a narrow set of range names ("The Ridge",
"Blackwood Range", "The Spine") that collide across bodies in the
same corridor. Blocklist catches "Thames" and "The Great Divide"
correctly. Zero stem-cap or subprocess-error fallbacks observed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:20:36 +02:00
jpmschweitzerandClaude Opus 4.6 d5a437ea2d fix(tooling): run-atlas-naming.sh — use grep -a instead of strings (#833)
The gfx1201 preflight check piped `strings` into grep, which fails
silently on a Bazzite host where binutils is not installed and
`strings` is not on PATH. `grep -a` reads the binary directly as
text, works everywhere grep exists, and produces the same result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:18:40 +02:00
jpmschweitzerandClaude Opus 4.6 bcff9f724b feat(tooling): run-atlas-naming.sh — overnight batch naming launcher (#833)
Wraps gemma_naming.py with the validated overnight recipe: gfx1201
ROCm binary path, distrobox reach-build for libhipblas at runtime,
timestamped log under .tmp/.

Preflight checks: binary exists and is executable, model present,
reach-build container exists, binary strings contains gfx1201 kernels.
Fails fast on any missing prerequisite so a broken build can't waste
an overnight window. Script takes no arguments; anything passed is
rejected so a stray --help can't accidentally launch the pipeline.

Estimate ~4-6 h for ~26k features across 2394 bodies at 74 t/s on an
RX 9070. Safe to interrupt and resume — preserved path skips
already-named bodies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:15:52 +02:00
jpmschweitzerandClaude Opus 4.6 c089557d65 feat(tooling): out-of-worktree sr-voice discovery + distrobox wrap + prompt capture (#833)
Three additions unlocked by the gfx1201 ROCm debug session.

1. _find_sr_voice() resolves the default binary path to
   ~/Projects/settled-reach/binaries/sr-voice-rocm (persistent across
   worktree lifetimes) with a legacy fallback to the main workdir's
   cargo target dir. Matches #850's plan to ship platform binaries
   outside the repo.

2. --distrobox <name> wraps the sr-voice subprocess in
   `distrobox enter <name> --` when the built binary depends on libs
   that only exist inside a dev container (libhipblas.so.2 on a
   Bazzite host). Stdio JSONL protocol flows through unchanged.

3. --dump-prompts PATH captures the attempt-0 prompt for every
   feature as JSONL without calling an LLM. Force --mock and
   short-circuit name_feature to return a unique deterministic
   placeholder. Used to feed the same prompt set to alternate
   backends (Haiku agent, other models) for offline A/B comparison
   of naming quality independent of the sampling backend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:11:28 +02:00
jpmschweitzerandClaude Opus 4.6 cf8a4e37d8 fix(engine): offload Gemma to GPU by setting n_gpu_layers=999 (#833)
LlamaModelParams::default() sets n_gpu_layers=0, so even with --features
rocm the model ran entirely on CPU at ~19 t/s. Setting n_gpu_layers to a
large sentinel value asks llama.cpp to offload every layer the model
has; llama.cpp clamps to the real count (27 for Gemma 2 2B). Observed
throughput jumps from 19 t/s to 74 t/s on an RX 9070 once the ROCm
binary is also compiled for gfx1201 (see tooling commit).

Also adds server/sr-voice/.gitignore so locally-built binaries don't
sneak into the worktree. Release binaries ship out-of-tree per #850.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:11:14 +02:00
jpmschweitzer fddb8b7544 chore(tooling): reset 19 stale core bodies so gttr-context fix reaches them (#833)
Previous runs (both the GPU-contention kill and the anglophone-only
interrupt) left 19 hop 0-1 core bodies with stale generator output in
their markers.json files. Those bodies were being skipped via the
preserved path on relaunch, which meant the gttr-context fix
(commit a5fbce4c) would never touch them — exactly the set of
high-visibility systems that benefits most from per-system cohesion.

Reset to origin/main (clean null-name state) + re-prune to the 8/6
caps. Hand-authored templates (Edict, Estrade, Vuurkloof, Lendel,
Cairnside, Røros) explicitly excluded from the reset list and
verified intact (2-4 named cities each, untouched).

After this commit only the 6 hand-authored templates have
populated names in wiki/star-systems/. The entire rest of the reach
is clean and will be freshly named by the next gemma_naming.py run
with the full gttr + cosmopolitan + grounded few-shot + rotating
pool stack.

Bodies reset:
- Ran (GJ 144): all 9 bodies
- Sirius (GJ 244A): GJ244Ab, c, e-1, e-2 (not Ad, that's Edict)
- ACB (GJ 559B): GJ559Bb
- Tau Ceti (GJ 71): GJ71b, c, d, d-1, e
2026-04-15 12:41:13 +02:00
jpmschweitzer a5fbce4cc9 feat(tooling): per-system gttr context + cosmopolitan corridor palettes (#833)
Two related quality fixes observed mid-run on Sirius + ACB + Ran:

1) Cosmopolitan corridor palettes. The six corridor inflection labels
were single-culture dominant ("administrative English / Gateway-era",
"British / Australian / Irish", "Korean/Japanese/Taiwanese", etc).
Gemma 2 2B interpreted these as "produce ONLY in this register" and
every core body came out anglophone, every east_reach body came out
East Asian. The real Earth diaspora in the setting is cosmopolitan —
a British surveyor on an east_reach moon still names a river after
their aunt in Dorset. The labels now spell out the dominant register
AND explicitly invite cross-cultural variety so Gemma samples from
the full few-shot pool instead of collapsing to one culture.

2) Per-system gttr context (the big one). The gttr.md files under
wiki/star-systems/<slug>/gttr.md already carry a vivid one-sentence
characterisation of every system — "where the rules live", "forty
years old and still in the draft", "the most connected system in
the Reach", "grandparents owned the land". This is a far stronger
cultural signal than the corridor inflection alone.

New column `star_systems.gttr_hook` stores a pre-extracted 45-word
hook per system. `tooling/db/populate_gttr_hook.py` parses each
gttr.md, regex-matches the first `**NAME**` paragraph, normalises
whitespace, truncates softly at a word cap, and stores it. Covers
all 301 systems (full coverage). Idempotent, safe to re-run after
any wiki update. Explicit transaction wrapper.

gemma_naming.py loads the hook cache at startup via
`load_system_gttr_hooks` and threads `system_hook` plus the system
and body proper names through process_body → name_feature →
_build_prompt. The prompt now carries:

    System: <proper_name>. Planet: <body_name>.
    About the system: <gttr_hook>

    Style: British. Answer: Cooper's Creek
    Style: Dutch.   Answer: Meijer Beek
    ...

Real-mode smoke on 10 cases across 4 contrasting systems shows the
hook is doing exactly what it should. Sample output on the same
body_id / local_id pairs:

  Tau Ceti     (cosmopolitan hub)         → Oakham River, Riverwood, Bridle Way
  Ran          (old-family agricultural)  → Hart's Well, Blackwood Ridge
  ACB          (Lattice Commission seat)  → Greenhaven, Rudge Brook
  Posto Avançado (PT frontier dead-end)   → Rio Preto, Serra de Caxias, Cunha's Cove

Posto Avançado went from "likely-English under the old corridor-only
prompt" to actual Portuguese names with a real Brazilian place stem
(Caxias), because the hook explicitly mentions wave_5 Portuguese
founders and frontier dead-end context. The gttr cultural one-liner
is the single strongest lever available for per-system cohesion —
this was the mono-culture issue observed in the first run, now fixed.

Token cost: ~60-90 extra tokens per prompt (hook + ident line).
Inference slowdown: ~5-10% per call. Acceptable for the quality gain.

Also restores 10 markers.json files that were stale from the aborted
run just killed — they were all core bodies at hop 0-1 which benefit
most from the gttr-context upgrade, so re-running them with the new
prompt is worth the ~3 minutes of re-inference.
2026-04-15 12:37:52 +02:00
jpmschweitzer d811a4f792 feat(tooling): prune oversized mountain/river counts + richer fallback palette (#833)
Two fixes from observing the first Gemma 2 batch run on Sirius:

1) Prune oversized feature counts. The upstream terrain pipeline emits
every distinct mountain cluster as a separate `mountain_range` and
every flowing path as a separate `river`. At the atlas generator's
512×256 grid this produced bodies with 40-80 named ranges and
10-15 rivers — noise, not information. A single planet with 48
ridges isn't richer, it's unparseable.

`tooling/planet-gen/prune_atlas_features.py` walks every
`markers.json` under `wiki/star-systems/`, ranks each feature type
by a size proxy, and keeps only the top N:
  - mountain_ranges: sorted by `area_cells`, top 8 per body
  - rivers:          sorted by path length, top 6 per body
  - oceans / cities / pois: untouched (already small, or
    hand-authored by generate_atlas.py)

Sol (GJ-0) is hardcoded-excluded from pruning so the hand-authored
Earth / Mars / moon content stays untouched.

Each pruned body gets its atlas_* rows re-synced via
`sync_markers_to_db` so the DB mirror stays consistent. Bodies
whose wiki folder has no matching row in `bodies` (14 pre-existing
orphans like GJ1156h-1, GJ34Ah-2, …) are pruned in-file but skip
the DB sync to avoid FK violations on atlas_body_grids.

First run results:
  bodies scanned:           2394
  bodies pruned:            1513
  mountain ranges dropped: 11640
  rivers dropped:           1382

Safe to re-run — idempotent when a body is already within the caps.

2) Grounded cosmopolitan fallback palette. When Gemma's 3 retries
all fail (dedup, blocklist, stem-cap, placeholder), the code falls
to `_FALLBACK_STEMS[corridor]`. The old table had 10 stems per
corridor, all Latin-institutional (Meridian, Concord, Prefecture,
Cardinal, Lumen, Foro, Tabula, Vox, Axis, Senatus), which produced
the same-y `Axis Spine / Axis Ridge / Axis Heights / Axis Scarp`
clusters the user flagged on Sirius — exactly the old epic-Latin
register the few-shot pools were rewritten to avoid.

Fallbacks now draw from a 30-45 stem grounded cosmopolitan list
per corridor matching the few-shot pool intent:
  - core:          45 stems (Ashfield, Bellview, Cedarbrook,
                   Fairmont, Ironwood, Kirkwood, Linden, Meridian,
                   Northfield, Riverside, Westbrook, …)
  - north_reach:   40 stems (Ashford, Bellfield, Clifford, Drayton,
                   Elmhurst, Garner, Holmwood, Kelsworth, …)
  - west_reach:    35 stems (Altdorf, Bergfjord, Eikhof, Hoogland,
                   Järvenpää, Kloosterdam, Nieuwpoort, Sørholm,
                   Svarteberg, Torsfell, Voorhout, Weserhof, Östby, …)
  - east_reach:    35 stems (Aomori, Baektu, Chōshi, Fukagawa,
                   Hanyang, Izumi, Takamine, Yurigawa, …)
  - south_reach:   36 stems (Alves, Brandão, Évora, Gomes, Ribeiro,
                   Serra, Várzea, Hlanganani, Kilimi, …)
  - deep_frontier: 30 stems (Okafor, Stenner, Weller, Kellogg,
                   Stonebrook, Dustgate, Blackwater, …)

Per-feature suffix lists also expanded (e.g. river suffixes now
include Brook, Stream, Flow, Creek on top of the original Run /
Water / Beck / Rill / Course). Net effect: 300-450 unique fallback
combinations per (corridor, feature_type), up from 50, in the same
grounded register the few-shot pools teach.

Also preserves aliases `inner_corridor`, `inner_orbit`, and
`sol-gateway-axis` as legacy-compatible keys pointing at the
administrative-English palette.

Combined effect on the next run:
- ~45% fewer features to name (pruned 13k/52k)
- ~9× more fallback variety per corridor when fallback does trigger
- Same grounding overhaul from the previous commit, now reaching
  into the safety-net path
2026-04-15 12:15:07 +02:00
jpmschweitzer cbeadc3184 feat(tooling): grounding overhaul + richer naming palettes (#833)
Substantial quality pass on gemma_naming.py driven by user review of
the first real-mode smoke test output. The earlier run produced names
that read too sci-fi / epic-fantasy / same-y: Aureus, Aetheria,
Stellaris, Nexus, Elysium. Root cause analysis + fixes:

1. Runtime timestamps. The log prefix is now
   `[HH:MM:SS +00h03m]` — clock time plus elapsed-since-start. Gives
   the user an at-a-glance sense of how long the run has been going
   without scrolling back to the banner.

2. System / body headers. When the loop enters a new system it prints
   `── SYSTEM K/N  GJ 71 — Tau Ceti  (hop 0)`. Each body line now
   shows `GJ71c (Threshold)` if the body has a proper_name in
   systems.db, so the log reads like a tour of the reach rather than
   a wall of body_id slugs. Preserved (already-named) bodies now log
   a compact "(skip — N names already set)" line so progress is
   visible even when no inference happened.

3. Prompt grounding overhaul. The old few-shot examples were all
   classical/epic (Wolcott Beck, Nakamura Stream, Ribeiro do Sal,
   Drayton Spine) which biased Gemma 2 2B toward Latin/Greek
   coinages. New preambles use the shape:
       "Settlers named X after themselves, after what they saw, or
        after places back home. Most names are mundane, short, and
        direct — a surname, a compass direction, a feature, a
        practical description. Classical or epic names are rare."
   Combined with grounded example pools, Gemma now produces names
   like "Cooper's Creek", "Western Ridge", "The Highroad",
   "Blackwood Creek", "Dustbowl".

4. Core corridor relabel. The "core" palette inflection was
   "institutional Latin / pan-Anglo / Gateway-era", which pattern-
   matched in Gemma's training data to "make up Latin-sounding
   words" (→ Ardenia, Aurelia, Stellaris). Now it's
   "administrative English / Gateway-era" and the outputs are
   prosaic — Port Dundas, East Ridge, Meridian, Landing.

5. Rotating few-shot example pools. Each feature type now has 5-7
   pools of 5-6 examples each. `_build_prompt()` picks a pool
   deterministically per (body_id, local_id, attempt) so:
   - Same feature always gets the same prompt (determinism preserved).
   - Neighbouring features on the same body get different prompts
     (output variance — the sampler doesn't collapse to a single
     mode when you ask for 16 mountain names in a row).
   - Retries rotate to a new pool, not just a bumped seed, giving
     dedup failures a clean second attempt.

6. Cosmopolitan cultural variety in the examples. Earlier pools only
   showed British/Australian, Korean/Japanese, Portuguese/Swahili,
   German/Dutch/Nordic axes — the four reach corridors. Gemma learned
   "names come in four flavours". New pools span Dutch, Nordic,
   Italian, French, Polish, Hungarian, Czech, Spanish, Russian,
   Finnish, Greek, Irish, Japanese, and British — teaching the model
   that names can be any real Earth cultural register, not just the
   corridor label. The result: actual Dutch names (Egelantier,
   Hochland, Van Damhoeve), actual Nordic (Lundstad, Brygga),
   actual Italian (Borgo Marconi, Piazza Nuova), etc.

7. First-name possessive pools. Per user feedback, settler naming
   includes both surnames ("Cooper's Creek") and first names
   ("Clifford's Bay", "Maura's Run", "Yuki's Pool"). Each feature
   type now has a dedicated first-name-possessive pool in addition
   to the existing surname pool — the two rotate alongside so both
   patterns show up without either dominating.

8. One "classical/Latinate" pool per feature type (≈17% of calls
   given 5-7 pools per type). Keeps occasional Latin flavour without
   making it dominant — the user explicitly noted that replacing
   one pattern with another "is never a clean fix for a randomizer."

9. Earth-name blocklist expansion. The Gemma 2 model reached for
   real European names ("Weser", "Rhine", "Reykjavik") in the first
   real run. Added 21 European rivers (Rhine, Weser, Elbe, Oder,
   Vistula, Loire, Rhône, Douro, Tagus, Ebro, Po, Arno, Tiber, …)
   and 25 Nordic/Eastern European cities (Reykjavik, Oslo, Gdansk,
   Krakow, Prague, Warsaw, Budapest, Belgrade, …). Case-insensitive
   "The <name>" stripping still applies so "The Great Divide" also
   matches "Great Divide".

Combined smoke test after these changes (10 real-mode prompts across
core + west_reach):
  - core:       Port Dundas, The Backbone, Dustbowl, Blackwood Creek
  - west_reach: Egelantier, Hochland, Der Rücken, Lundstad, Klipfjord
  - no placeholder residue, no markdown, no 5+ word outputs.

--shard is gone (dead code since GPU contention killed parallelism).
Resume semantics are still free: re-run the same command and
already-named bodies skip via the preserved path.
2026-04-15 11:35:07 +02:00
jpmschweitzer 6decf56b2d refactor(tooling): drop --shard from gemma_naming.py (#833)
Parallelism via two concurrent sr-voice subprocesses does not work on
this ROCm + llama-cpp-rs setup — launching a second instance poisons
the first one's GPU context (both fall back to 0% GPU / 50% CPU
busy-loop and stop making progress). Verified empirically: single
shard runs cleanly at ~1.2s/feature, two shards deadlock.

Without a working parallel path, --shard is dead weight. Resume
semantics were already free: the pipeline skips bodies whose
markers.json has non-empty name fields (preserved path), so a
killed run re-starts just by re-running the same command.

Simplifications:
- Remove --shard argument and all slicing logic.
- Remove banner_shard / shard_offset / shard_n / shard_m plumbing.
- Rename internal total_shard_systems → total_systems.
- Default --log path is now .tmp/gemma_naming.log (was conditional
  on --shard). Pass `--log -` to disable file logging.
- Startup banner now prints a one-line resume reminder so the user
  can see at a glance that a killed run is recoverable.
2026-04-15 11:05:25 +02:00
jpmschweitzer 1efbbcaea9 feat(tooling): gemma_naming.py batch naming pipeline for atlas (#833)
New end-to-end pipeline that walks every markers.json in the reach and
fills empty `name` fields using the Gemma 2 voice pipeline via
`sr-voice serve --stdio`. Per D-191 §4: the same Gemma 2 pipeline the
client uses for NPC voicing also produces the atlas content, which is
dual-purposed as a quality test of the LLM plumbing.

Pipeline per body (hop-ordered, core-first):
  1. Load markers.json; identify feature records whose `name` is
     blank (null or ""). Hand-authored names are never overwritten;
     the 6 template bodies and any partial authoring stay put.
  2. Look up body context (planet_class, settlement_pattern,
     cultural_corridor, population, economic_role) from systems.db.
  3. Build a short corridor-aware few-shot prompt per feature type.
     Prompts carry 3 concrete `Style: X.   Answer: Y` examples so
     Gemma 2 2B completes a pattern instead of generating to an
     open-ended instruction — this is the single biggest lever
     against placeholder echoes on a small model.
  4. Stream the prompt into a long-lived sr-voice subprocess, read
     the JSONL response, post-process (strip markdown, label
     prefixes, brackets, reject 5+ word outputs and placeholder
     tokens), check the earth-name blocklist, check per-(corridor,
     feature_type) + per-body dedup, check the per-stem cap, retry
     up to 3 times with a bumped seed.
  5. On persistent failure, fall back to a deterministic palette
     generator so every feature ends up with a name.
  6. Write markers.json atomically and refresh atlas_* DB rows via
     sync_markers_to_db. Commit the DB per body so a crash loses
     at most one body of state.
  7. Restart the sr-voice subprocess every `--refresh` requests
     (default: 200) to prevent KV-cache context bleed.

Core design decisions:
- Determinism: per-(world_seed, body_id, feature_local_id, attempt)
  seed so the full run is reproducible.
- Ordering: bodies are processed in ascending `hop_distance_from_gateway`
  so core bodies get first pick at every unique Gemma output and
  outer sectors fall into the palette fallback when they lose the
  dedup race.
- Dedup scope: (cultural_corridor, feature_type) across the run,
  PLUS a per-body cross-type set so the same name can't be a river
  AND an ocean AND a mountain on the same world. Hand-authored names
  are seeded into both sets on load so templates win priority.
- Stem cap: each non-generic root token (e.g. 'Arcturus', 'Meridian')
  may appear at most `--stem-cap` times across the full run (default
  20), preventing single-word runaway. Fallback names bypass the cap.
- Earth blocklist: 181 curated entries covering major Earth cities,
  mountains, rivers, oceans, historical/colonial spellings, and
  Greek/Roman mythology that reads too literally. Prefixed variants
  ('Nouveau Paris', 'New Tokyo') explicitly allowed per the product
  intent that Earth-echo names are fine but must not dominate.
  Leading 'The ' is stripped before comparison so 'The Great Divide'
  also matches.

Operational features:
- `--shard N/M` slices the body list into M partitions for parallel
  runs. Two terminals × `--shard 0/2` + `--shard 1/2` fits the
  ~2.5 GB/instance VRAM footprint twice under the 50% cap on a
  16 GB AMD GPU and roughly halves wall time.
- `--log PATH` writes a timestamped tee of every status line to a
  file. Default: `.tmp/gemma_naming.shard{N}of{M}.log` when a
  non-trivial shard is in use.
- SQLite `PRAGMA journal_mode=WAL` + `busy_timeout=15000` so two
  concurrent shards serialize writes without lock errors.
- Per-body progress lines report `body K/N`, `sys K/N`, and
  `hop=H` so the user can watch core sectors finish first.
- Each body logs the new names it produced per feature type so the
  user can eyeball quality as the run progresses.
- Checkpoint summary every 25 bodies: cumulative names, rate,
  ETA — gives the log regular scroll points.
- `--mock` uses `server/sr-voice/mock-stdio.sh` for dry-fire
  pipeline validation without a model load (tested end-to-end).

Supporting files:
- `tooling/planet-gen/earth_blocklist.txt` — 181 curated entries.
- `tooling/db/backfill_cultural_corridor.py` — one-off migration
  that fills the `cultural_corridor` column on both `star_systems`
  and `bodies` from the `geographic_sector` values. Before this
  pass, 99.4% of rows (3221/3240) had a NULL cultural_corridor
  despite `wiki_sync.py` being aware of the column — the wiki
  index.md files only carry the sector header, which was never
  propagated to the DB column. Idempotent, safe to re-run after
  any wiki_sync rebuild, explicit transaction wrapper with
  rollback on failure.

Full batch runtime estimate: ~20 hours single-shard / ~10 hours
double-shard on this hardware. Smoke tests across five hardened
iterations (v1–v5) on GJ71b/c/d/d-1/e confirm the pipeline produces
clean, varied, culturally-coherent names with zero post-processing
residue.
2026-04-15 10:54:28 +02:00
jpmschweitzerandClaude Opus 4.6 d388845c88 chore(config): prune dead gitignore entries
Remove redundant and stale patterns:
- content-ron/, client-tmp/ — vanished dirs not referenced anywhere
- tooling/planet-gen/__pycache__/, sol_data/__pycache__/ — already
  covered by the global __pycache__/ pattern
- tooling/planet-gen/sol_data/.cache/ — already covered by .cache/
- db/commonwealth.db* — leftover guard from the pre-rename era;
  the stale on-disk file has been deleted

Also rewrite the misleading "Runtime databases (not tracked — created
on startup)" comment: server/data/systems.db is actually tracked, so
what the block really ignores are WAL/SHM sidecars plus the local-only
settings.db.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:21:45 +02:00
jpmschweitzerandClaude Opus 4.6 84bb10234e chore(config): allow cargo clippy and ruff check in Bash permissions
Both lint tools are used during pr-review pre-flight checks. Adding them
alongside the existing cargo check / test / build entries avoids
permission prompts during the review cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:04:16 +02:00
jpmschweitzer 8d8fa9060f Merge remote-tracking branch 'origin/sprint-35/server' 2026-04-15 09:36:04 +02:00
jpmschweitzer d21c690293 fix(tooling): address PR #129 review — atlas generator, schema, Makefile
- ON DELETE CASCADE added to every atlas_* foreign key (atlas_body_grids,
  atlas_cities, atlas_roads, atlas_railroads, atlas_pois, atlas_rivers,
  atlas_oceans, atlas_mountain_ranges). Previously, deleting a body from
  the bodies table or NULL-ing its terrain_reference would leave orphan
  atlas rows forever — sync_markers_to_db only cleans up for bodies it
  re-processes. The existing atlas tables in systems.db were dropped and
  recreated with the new constraint; FK list now reports CASCADE.

- Atlas DDL deduplicated. systems-schema.sql is now the single source of
  truth, bracketed by `-- BEGIN ATLAS INDEX` / `-- END ATLAS INDEX`
  markers. generate_atlas.py reads that block via `_load_atlas_schema()`
  and applies it at runtime, so there is no second copy of the DDL to
  keep in sync. Adding a column requires one edit, not two.

- Uniqueness guard on city coordinates. `_enforce_unique_city_coords`
  runs at the end of `place_cities` and deterministically perturbs any
  duplicate (row, col) via a fixed spiral walk to the first free
  walkable land cell. Rare in practice but the MST collapses to a
  zero-distance edge otherwise, producing an empty A* path and silently
  dropping the road.

- Grid header validation. `load_markers` now raises `AtlasGridMismatch`
  if the loaded `grid: {w, h}` header does not match `GRID_W`/`GRID_H`.
  Both the incremental-skip path and the regenerate path route through
  this loader, so a hand-authored template shipping a different grid
  size fails loud with a per-body error rather than silently producing
  half-scale coordinates.

- Unused `seed_rng` parameter removed from `_analyse_terrain`. The
  function is RNG-free (continent flood-fill, habitability scoring,
  river-mouth dedup, cost grid — all pure functions of terrain). The
  false API contract made it look like terrain analysis consumed RNG
  state and had to be sequenced with downstream RNG use.

- `_score_capital_sites` river-mouth bonus now builds one sparse
  accumulator with all mouth points set at once and runs a single
  `gaussian_filter` call, instead of O(n_mouths) filter calls over
  single-point images.

- `binary_dilation(analysis["land_mask"] == False)` replaced with the
  idiomatic `~analysis["land_mask"]`, matching the convention used
  elsewhere in the file.

- `atlas-generate` Makefile target now guards on
  `SELECT COUNT(*) FROM bodies WHERE terrain_reference IS NOT NULL`.
  On a fresh DB that count is 0 and the generator previously exited
  "success" after processing zero bodies. The target now fails loud
  with a pointer to `populate_terrain_reference.py`.

- `main.rs` SimRng defensive re-insertion gains a long comment
  explaining the exact plugin-ordering hazard it guards against, so
  future readers don't treat the line as dead code. Tied to #826.
2026-04-15 09:25:46 +02:00
jpmschweitzer 57e52d7586 Merge remote-tracking branch 'origin/sprint-35/client' 2026-04-15 09:16:44 +02:00
jpmschweitzer 64bb83f749 fix(simulation): address PR #129 review — brand importer hardening
- V-B06 enum validation: the five VALID_* sets
  (VALID_BRAND_CATEGORIES, VALID_VALUE_TRAJECTORIES, VALID_SCARCITY_CLASSES,
  VALID_BRAND_TIERS, VALID_CURRENCY_DENOMINATIONS) were defined but never
  referenced. brand_products.brand_category etc. are plain TEXT with no
  CHECK constraints, so a typo like `brand_category = "terrior"` silently
  imported. `validate_brands` now runs a V-B06 pass that asserts every
  enum column is a member of its VALID_* set. V-B01..V-B05 + V-B06 all
  reported together on import failure.

- Explicit transaction wrapper: the clear-then-reimport cycle (10 DELETEs
  followed by 9 imports and structural validation) used to depend on
  Python's implicit-deferred-transaction semantics and sys.exit() on
  validation failure. A crash mid-import could leave the DB with some
  tables empty and others intact. The body now runs inside
  `conn.execute("BEGIN")` + try/except with an explicit `_ImportAborted`
  for validation failures and a `BaseException` catch-all for
  KeyboardInterrupt / programmer errors. All failure paths rollback
  before exit; the commit only fires after structural validation
  passes. Dry-run leaves the transaction open so the coverage check
  below can still SELECT against in-memory state.

- system_fiscal docstring: previously cited the D-189 §6 derived
  formula (`collection_efficiency = 1.0 - shadow_economy_intensity × 0.6`)
  while the implementation hardcodes `collection_efficiency = 0.85` for
  every system. The docstring now explicitly states these are Phase 2
  placeholder values (with named constants PHASE2_CORP_TAX_RATE and
  PHASE2_COLLECTION_EFFICIENCY) and calls out the shadow_economy.toml
  pipeline as the Phase 3 follow-up.
2026-04-15 09:15:16 +02:00
jpmschweitzer 56d524f37d docs(decisions): address PR #129 review — D-191 §8 amendment + boundary notes
Blocking PR #129 items 1, 2 (plus polish 16 and 17):

- D-191 §8 prose rewritten to match the code. The previous amendment said
  positions were `{x, y}` objects against a "typically 1024 × 512" grid,
  but the generator, the six hand-authored templates, and all 2394
  procedural seed files ship `[row, col]` integer arrays against a
  `{"w": 512, "h": 256}` grid. The decision doc is now aligned with
  reality: positions are `[row, col]`, the storage grid is 512 × 256,
  and the row-first ordering is called out explicitly so readers can
  cross-reference NumPy/flood-fill/A*/cost-grid conventions.

- §8 now follows the D-094 amendment pattern. The superseded 2026-04-10
  prose is preserved verbatim as "Original (superseded)" with a dated
  Amendment block on top — future readers can see what changed and why
  instead of silently losing the history.

- brands.toml header gains a short Phase 2 boundary note. The 4 anchor
  brands come from D-189 §5; the additional ~23 brands from D-189 §11
  are deliberately deferred to Phase 3 — Phase 2 only needs the demand-
  node plumbing and V-B01..V-B06 validation exercised end-to-end.

- systems-schema.sql `bodies.terrain_reference` comment now pins the
  repo-root-relative path convention (wiki/star-systems/<slug>/bodies/
  <body_id>/heightmap.png) so the three downstream pipelines (populate,
  atlas generator, client loader) share a documented contract instead
  of drifting against an unwritten convention.
2026-04-15 09:12:08 +02:00
jpmschweitzer 22c17f1f15 style(ui): gdformat atlas implant files 2026-04-15 09:06:04 +02:00
jpmschweitzer 489aec48d8 style(ui): atlas review polish — guarded ids, stable keys, dead-code cleanup (#128)
10. Dropped the "no body data" fallback string in the picker panel — the
    generator always writes entry["bodies"], so the fallback was dead. Use
    the "—" convention the rest of the panel already follows.
12. Removed the autoload parse-order comment from AtlasPanel (it is
    scene-instanced, not an autoload, so the rule does not apply), and
    collapsed _build_heightmap_viewer to a direct AtlasViewer.new() —
    mirroring the rest of the file rather than dancing around a risk that
    is not real for this class.
13. AtlasMarkerOverlay._draw_cities now compares hover/selection by a
    stable _city_key() (name → city_id → pos → hash) instead of
    Dictionary.==, which was O(fields) per city per redraw. Preps the
    renderer for much larger city counts without a rewrite.
14. Orbital click/draw handlers guard against missing body_id /
    station_id by reading through str(dict.get(..., "")) and skipping
    empty ids. Matches the defensive style already used for parent_body_id
    and keeps a NULL id from crashing _draw_bodies / _handle_orbital_click.
2026-04-15 09:02:27 +02:00
jpmschweitzer f8b81e1fe7 refactor(ui): atlas review warnings — headers, public API, KEY_N, overlay source of truth (#128)
4. AtlasPanel and AtlasViewer now compose their title + hint from an
   ImplantHeader child rather than hand-rolling them via draw_string, so the
   D-169 "theme swap changes the implant hardware appearance" invariant
   holds end-to-end. _refresh_screen_header() drives content per level and
   on system navigation.

5. AtlasViewer exposes city_canvas_pos(), get_hovered_city(),
   get_selected_city(), and get_overlay_defs() as public API — the marker
   overlay no longer reaches into underscore-prefixed state, which is
   especially important because viewer is an untyped var in the overlay.

6. KEY_N now consumes unconditionally while the viewer is visible, and
   main.gd's global economics-monitor toggle is gated on
   !HudGroups.is_app_active("implant/map/atlas"). Previously pressing N
   without a selected city fell through and closed the fullscreen atlas as
   a side effect.

7. OVERLAY_DEFS lives in AtlasViewer as the single source of truth.
   AtlasOverlayBar reads the list via viewer.get_overlay_defs(), and
   AtlasViewer derives _overlay_visibility / _overlay_locked from the same
   table at _ready() — no more hand-maintained parallel lists, so the bar
   and the guard in set_overlay_visible can't drift.

8. AtlasOverlayBar drops `class_name`: it now loads via
   load("res://ui/implant/atlas_overlay_bar.gd") from AtlasViewer, the same
   pattern AtlasPanel uses for AtlasViewer. _init(viewer_ref = null) keeps
   the required-arg footgun off the editor's introspection path.

9. `star-map-data` make target added to regenerate
   client/data/star_map_data.json from systems.db + wiki, and
   `check-star-map` wired into pre-pr-validate + pre-pr-client so any
   commit that touches the generator (or any downstream systems.db change
   like server #839) fails pre-pr until the JSON is regenerated. The
   terrain_reference data-availability dependency is no longer tribal
   knowledge.

Also addresses review #15 (push_warning on unknown overlay id in
set_overlay_visible) and #16 (disabled always-on buttons drop handler
churn) as part of the same refactor.
2026-04-15 08:59:21 +02:00
jpmschweitzer 1328db3138 fix(ui): atlas review blockers — moon layout, stale grid dims, overlay schema (#128)
1. Moon placement on the orbital diagram divided by a hard-coded 4 — a gas
   giant with five or more moons would overlap satellites and make them
   unclickable. Count moons per parent and distribute them evenly.
2. AtlasViewer._load_markers seeded _grid_w/_grid_h from _tex_w/_tex_h
   before the terrain_reference null check, so opening a body with no
   heightmap after one that had a heightmap left the grid dimensions
   pointing at the previous texture and misplaced markers. Reset texture +
   grid dims to sentinel defaults at the top of _load_heightmap.
3. AtlasMarkerOverlay's three toggleable overlays (production_zones,
   shadow_economy, corp_presence) read markers.production_zones /
   markers.shadow_zones / markers.corp_presence — keys that don't exist in
   D-191 §8's markers schema, so toggling was a silent no-op. Derive them
   from cities[] instead: primary_function ∈ PRODUCTION_FUNCTIONS for
   production, absent Commission presence for shadow bands, Commission
   presence for corp dots. Follow-up ticket will formalise per-overlay
   arrays once the server schema lands.

Addresses PR #128 review blockers 1-3.
2026-04-15 08:47:14 +02:00
jpmschweitzer 5619218e31 feat(tooling): generate_atlas.py city + infrastructure pipeline and atlas DB index (#832)
Implements the Phase 3 atlas content generator per D-191 §3, §8, and §9.

Pipeline per body (terrain-aware, deterministic per seed + body):
  1. Simulate terrain via planet_simulation.simulate().
  2. Analyse continents (flood-fill), habitability (temp/moisture/slope +
     coastal bonus), river mouths, and a terrain A* cost grid.
  3. Place cities sequentially — capital first (habitability + river-mouth
     bias), then corridor growth via multi-source Dijkstra, quadrant-spread
     penalty after 2 cities in a quadrant, port-on-new-continent bonus at
     cities 3–4. ±25% noise for seed variation.
  4. Generate roads and railroads as an MST over city positions, with
     A* paths on the terrain cost grid (rail follows roads where possible).
  5. Place a transit POI at the capital (15% chance to scatter to a
     secondary city).

Output (canonical markers.json schema, pixel space per D-191 §8):
  - cities:    {id, name, kind, center:[r,c], population}
  - roads:     {id, name, kind, path:[[r,c],...]}
  - railroads: {id, name, kind, path:[[r,c],...]}
  - pois:      {id, name, kind, center:[r,c]}
  - existing rivers/oceans/mountain_ranges preserved untouched.

City names are left empty for gemma_naming.py (#833). Body population is
split across cities with geometric decay (capital ~50%, each subsequent
city half the previous). The 6 hand-authored bodies (Lendel, Edict,
Vuurkloof, Røros, Cairnside, Estrade) are detected by existing
`cities` and skipped for regeneration; their markers are still synced
to the DB index below.

Atlas index in systems.db (new):
  - atlas_body_grids, atlas_cities, atlas_roads, atlas_railroads,
    atlas_pois, atlas_rivers, atlas_oceans, atlas_mountain_ranges
  - Scalar metadata mirror of every markers.json — the implant atlas app
    and development queries can lookup cities/POIs/features without
    scanning 267 JSON files. Polyline geometry stays in the markers.json
    files next to the heightmaps (used by the renderer); the DB only
    stores filterable scalar fields plus `point_count` as a length proxy.
  - Schema lives in server/data/systems-schema.sql; generate_atlas.py
    mirrors the CREATE TABLE IF NOT EXISTS block so it runs against any
    DB state (matches the economy-db importer pattern).
  - Populated and refreshed on every run. Each body's rows are deleted
    and reinserted deterministically — no stale state.

Also fixes a pre-existing WIP bug in the quadrant-saturation penalty
loop (a stray outer `for r in range(GRID_H)` with unreachable breaks
meant only the NW quadrant was ever checked).

Runtime: 280s for all 267 inhabited bodies on a single core. 265 bodies
updated this run, 6 hand-authored bodies synced to DB without
regeneration.

Atlas index after run:
  atlas_cities             329    (15 hand-authored + 314 awaiting #833)
  atlas_roads               46
  atlas_railroads           44
  atlas_pois               287
  atlas_rivers            2034
  atlas_oceans             696
  atlas_mountain_ranges   1953
  atlas_body_grids         267
2026-04-15 08:46:55 +02:00
jpmschweitzer 9afd6b53d2 style(ui): wrap atlas long lines to satisfy gdlint 2026-04-15 08:31:40 +02:00
jpmschweitzer 9987eaad8a feat(ui): atlas overlay toggle bar — 9 MVP overlays + 2 locked (#836)
AtlasOverlayBar is an HBoxContainer docked top-right of the heightmap viewer
with 11 short-label buttons mapping to D-191 §7 / D-181 signal visibility:

  always-on (5)  TER INF NAM GAT POL — terrain, infrastructure, named
                 features, gate markers, political zones. Pinned on; clicks
                 are swallowed so the layers can't accidentally be disabled.
  toggleable (4) POP PRD SHD CRP — population density, production zones,
                 shadow economy, corporate presence. Reflect and mutate
                 viewer overlay state.
  locked (2)    STK BSL — stockpile_weeks, production_vs_baseline. Disabled
                 and greyed out with unlock-requirement tooltips, per D-181
                 semi-private/private tiers; kept in the bar so players see
                 that deeper data exists and is gated.

Each button writes through AtlasViewer.set_overlay_visible(), which is the
single entry point into the viewer's _overlay_visibility dict consumed by
AtlasMarkerOverlay._draw(). Locked overlays short-circuit in that setter.

Per D-191 criterion 6.
2026-04-15 08:27:30 +02:00
jpmschweitzer c33ac8921f feat(ui): atlas heightmap viewer — pan/zoom, marker overlay, city data panel (#835)
Adds Level.HEIGHTMAP_VIEWER to AtlasPanel. The viewer loads a body's
terrain_reference heightmap PNG, pairs it with markers.json (roads, rail, POIs,
cities, rivers/oceans/mountains), and renders markers in texture-space via an
AtlasMarkerOverlay Node2D child of a transformed canvas — pan = offset,
zoom = scale.

Pan/zoom is cursor-centred (wheel zooms under the mouse, drag pans), with a
fit-to-view reset on R. Empty markers.json state renders a bare heightmap;
missing terrain_reference shows a themed "terrain data pending (#839)" notice
instead of crashing.

City data sidebar rebuilds from the selected city: name, pop tier, function,
currency zone, Commission presence, shadow zone, gate distance. Pressing N on
a selected city emits economics_link_requested(system_id) — main.gd bridges
this to EconomicsPanel.select_system() + HudGroups.open_app("implant/economics")
as an insert overlay, satisfying the D-191 Phase 2/3 cross-panel integration.

The overlay renders all nine D-191 overlay layers off of per-overlay visibility
flags in AtlasViewer. Overlay toggling for the regional view (#836) plugs into
set_overlay_visible(); the five always-on layers (terrain, infrastructure,
named features, gate markers, political zones) draw by default, the four
toggleable layers draw from placeholder data, and the two locked layers
(stockpile_weeks, production_vs_baseline) remain off until unlocked.

Per D-191 criteria 1, 4, 5.
2026-04-15 08:22:01 +02:00
jpmschweitzer d6d3b51098 docs(decisions): D-191 §8 canonical markers.json format is pixel space
The generator and the hand-authored templates (Edict, Vuurkloof, Røros,
Cairnside, Estrade) already store markers in heightmap pixel space with
a grid header. Update §8 to match: {x, y} integer pixels are the storage
format, and lat/lon strings become a display-time derivation in the
atlas UI (synthesized from position + grid dimensions + body radius).

Avoids double-conversion through an equirectangular projection and keeps
the hand-authored markers.json files as-is.
2026-04-14 17:30:37 +02:00
jpmschweitzer b127f63dc2 feat(tooling): populate terrain_reference column for inhabited bodies (#839)
Adds tooling/planet-gen/populate_terrain_reference.py and runs it against
systems.db. Resolves each body's expected wiki heightmap path (repo-root
relative) and writes it into bodies.terrain_reference. Missing heightmaps
are logged for remediation.

Result: 2380/3240 bodies populated, 860 still missing heightmaps. This
unblocks generate_atlas.py (#832) for every body that has a heightmap.
2026-04-14 17:25:56 +02:00
jpmschweitzer 03e0d1c022 feat(simulation): brand layer schema and import pipeline (#827)
Adds the brand layer per D-189 §5:
- Schema: brand_products, brand_inputs, system_fiscal, corp_financial_state,
  corp_lifecycle_events (+ 5 indexes).
- Importer: reads wiki/economics/corporations/brands.toml, populates the
  new tables, validates V-B01–V-B05 structural rules, and derives
  system_fiscal for inhabited systems.
- Data: 8 brand_products, 16 brand_inputs, 301 system_fiscal rows.

Brand products are demand nodes — they consume commodities; they are not
commodities themselves (D-185). Depends on copy PR #127 for the corp
records referenced by brands.toml.
2026-04-14 17:24:51 +02:00
jpmschweitzer 760953aeba feat(ui): atlas implant panel — system picker, orbital diagram, body nav (#834)
3-level atlas navigation as an implant/map/atlas FULLSCREEN app (D-170 z=20):
system picker (◄ ► cycle, Enter opens orbital) → orbital diagram (star centre,
bodies grouped by orbit_index with moons sub-orbiting parents, stations as
markers, click-to-open body or mini station panel) → body entry (data sheet,
Enter stub for #835 heightmap viewer, Esc back). Composed from the ImplantPanel
component library (D-169).

Wires the panel into hud.tscn, adds an A-key toggle in main.gd, propagates
insert_state via snapshot_consumers. Extends generate-star-map-data.py to emit
per-system orbit_bodies + stations arrays (with currency_zone, atmosphere,
population, terrain_reference, etc.) from systems.db; star_map_data.json
regenerated deterministically.

Per D-191 §6, Phase 3.
2026-04-14 17:24:50 +02:00
jpmschweitzer 826b6fe1c7 Merge remote-tracking branch 'origin/main' into sprint-35/server 2026-04-14 17:22:09 +02:00
jpmschweitzer 920ea0582f feat(simulation): thread world seed from StartupMessage into economy (#826)
Replaces the hardcoded seed=0 with the seed received in StartupMessage,
threading it through SimulationPlugin -> EconomyPlugin / SimRng. Integration
test fixtures updated for the new SimulationPlugin { seed } signature.
2026-04-14 17:21:53 +02:00
jpmschweitzerandClaude Opus 4.6 9439a20253 content(wiki): PR #127 review fix pass — 15 findings
Applies all review findings from Hoshe + Paula + Miri on PR #127.

Glossary (wiki/glossary.md):
- Remove self-referential "tâtonnement" from its own NOT list.
- Add NOT/NOTE annotation-prefix preamble.
- Replace ambient "open question" note on Commission with explicit
  Q-095 citation; add "Lattice Commission" to NOT list.

Decisions:
- Claim Q-095 (content) — "Commission formal name — authoritative
  designation"; register in decisions/questions.md index.

Marker POI renames (glossary.md line 25 — "Syndicate" is prohibited;
glossary.md lines 14–16 — "Lattice Commission" is undocumented):
- GJ280Ad: "Syndicate Editorial Complex" → "Parallax Media Centre";
  "Commission Content Review Office" → "Parallax Standards Office".
- GJ66Bc: "Syndicate Trade Office" → "Compact Trade Representative";
  "Voss Gate Terminal" → "Røros Gate Terminal".
- GJ244Ad: "Lattice Commission — Sirius Office" → "Commission — Sirius Office".
- GJ35c: "Prime Surface Depot — Gate Terminal" → "Terras Gate Terminal".
- GJ892d (Cairnside): "Cairnside Primary" → "Okafor Base" (frontier
  founder-surname pattern per glossary.md line 74); "Primary Access
  Road" → "Cairn Scarp Track" (derived from Cairn Reach Scarp).

Brand templates (wiki/economics/archetypes/brand_templates.toml):
- Deep-harvest seafood archetype: Korean/Tagalog → Korean/Japanese
  across naming_pattern and description fields, per D-189 §11.
- 11 terroir + heritage_craft _reach_wide variants now show BOTH
  corridor-origin and neutral example names, per cross-cultural
  mixing rule; inner_corridor/neutral monoculture removed.
- commodity_inputs header comment: add explicit "per tick" unit
  cross-reference to D-189 §5 and server/src/simulation/economy.rs.

Tier-1 corporations (wiki/economics/corporations/tier1.toml):
- Vins de Grand Vide: currency_preference "mixed" → "mark" — HQ
  booking currency must mirror the HQ-system canonical zone
  (GJ 395 Confluent → MARK_PRIMARY); "mixed" is reserved for hop 5–6
  transition systems.
- Bífröst Marmor: add NOTE surfacing the orbital-age formation
  anomaly (wiki/corporations/bifrost-marmor.md:70–71), alongside
  the existing reserve-silence comment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:07:20 +02:00
jpmschweitzerandClaude Sonnet 4.6 c26d74f45d content(atlas): hand-author markers for Estrade (GJ 280A) — Parallax media capital
Estrade (GJ280Ad) is the Reach's information/media hub: 1.8B population,
inner_corridor, service-mixed economy. Names 10 rivers, 2 water bodies,
3 mountain ranges; places 4 cities (Strata 1.1B, Vantage 450M, Ledger 150M,
Margin 100M); adds gate terminal + 3 institutional POIs, 2 roads, 1 railroad.

Complements #837 atlas template set (Lendel, Edict, Røros, Vuurkloof, Cairnside)
with a high-population urban service world example.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 19:50:15 +02:00
jpmschweitzerandClaude Opus 4.6 2d3361e24d content(atlas): hand-author markers.json for Edict, Vuurkloof, Røros, Cairnside (#837)
Author Phase 3 atlas quality-bar templates for 4 core systems alongside
the pre-existing Lendel template. Names, cities, roads/rail, and POIs
hand-placed against each body's heightmap and tied to the system's
corridor naming tradition.

- GJ244Ad Edict (Sirius system, inner_corridor) — Assembly institutional
  capital: Mandate 387M, Station Edict 13M, Founder's Range, Accord
  Peaks, Founding Ocean, Edict Deep Line rail.
- GJ35c Vuurkloof (Van Maanen's Star, south_reach) — volcanic geothermal
  settlement: Terras 290K, Kloofbas 50K, Groot Breuk, Ysterkop, Rantlyne
  underground rail. Afrikaans geology naming per wiki canon.
- GJ66Bc Røros (Voss system, west_reach) — Compact mining world:
  Storbjerg 3.2M, Hammervik 800K, Nordfjell, Rørosfjell, Storhav,
  Glåmelva. Norwegian/Scandinavian heritage naming.
- GJ892d Cairnside (deep frontier, research domes) — Cairnside Primary
  75M, Survey Post Kappa 5M, The Terraces, Baseline Lake, Baseline Rail.
  Technical frontier naming pattern.

All files match the implemented Lendel markers.json format (grid + pixel
coordinates, raw population, gate terminal as named POI). D-191 section 8
describes the schema in abstract lat/lon terms; server team should align
generate_atlas.py and D-191 prose with the on-disk format before #832
and #833 consume these as few-shot examples.

These templates serve as the quality bar for generator tuning and as
few-shot examples for the Gemma 2 naming pipeline (#833).

Ticket: #837
Decisions: D-191 (Phase 3 atlas), D-036 (Sova/Vuurkloof canon), D-144
(Sirius/Concord seat)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 19:47:21 +02:00
jpmschweitzerandClaude Sonnet 4.6 92ad6b9515 feat(wiki): Sprint 35 copy deliverables — glossary, brand corps, brand templates
- wiki/glossary.md: canonical proper nouns, factions, currencies, corridors,
  key locations, economics terminology, drift forms (tasks #840)
- wiki/economics/corporations/tier1.toml: add Calloway Distillery, Vins de Grand
  Vide, thrds, Bífröst Marmor brand corp records (#830)
- wiki/economics/archetypes/brand_templates.toml: 126 templates (42 archetypes ×
  3 scale tiers) covering all 8 brand categories and all 6 corridors (#831)
- wiki/index.md: add glossary link under Reference section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 19:43:49 +02:00
jpmschweitzerandClaude Sonnet 4.6 521c682175 chore(meta): plan Sprint 35: Atlas
Phase 3 launch sprint. 13 tickets across server, client, copy.
Atlas generation pipeline (terrain_reference, generate_atlas.py, Gemma naming),
brand layer DB schema, Atlas implant panel (3 levels + heightmap viewer + overlays),
hand-authored templates for Lendel + 4 core systems, brand corp TOML,
brand_templates.toml (120-130 archetypes), wiki glossary, world seed wiring.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:01:42 +02:00
jpmschweitzerandClaude Opus 4.6 26a6fd233c style(simulation): cargo fmt monologue trigger query
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:23:44 +02:00
jpmschweitzerandClaude Opus 4.6 5f678dac48 fix(simulation): address #843 review — seeding, atomics, API consistency
Review fixes from Hoshe + Tyre:
- EntityRng seeding: splitmix64(seed) ^ splitmix64(id) instead of
  splitmix64(seed + id) — eliminates collision class where adjacent
  seeds produce identical streams
- AtomicBool ordering: Relaxed → SeqCst for shutdown flag (correct
  on weakly-ordered architectures)
- Worker Drop: join handles instead of detaching threads
- Normalize stub API: remove ChunkGenWorker convenience wrappers,
  use .pool consistently across all 3 workers
- trigger_monologue: downgrade &mut to shared refs (no-op anchor
  was blocking parallel systems)
- Remove dead SimRng inserts from migrated monologue tests
- Document determinism gap on poll_worker_results
- Document bevy_tasks/rayon dep rationale in Cargo.toml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:22:44 +02:00
jpmschweitzerandClaude Opus 4.6 0ae3542550 style(simulation): cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:15:56 +02:00
jpmschweitzerandClaude Opus 4.6 0098ee5a41 feat(simulation): background worker pool infrastructure (#843 Part C)
Generic BackgroundWorkerPool<Req, Resp> with crossbeam channels, closure
handlers, and 3 delivery strategies (Fallback, GracefulDegrade, ModalLock).

Stub workers registered as Bevy resources:
  - ChunkGenWorker (2 threads) — terrain/props/navmesh generation
  - NpcPrepWorker (1 thread) — pre-compute NPC state for incoming areas
  - OffscreenTickWorker (1 thread) — advance NPCs outside active tier

Tick loop integration:
  - PreInput: poll_worker_results drains completed work
  - PostSnapshot: push_worker_requests queues new work (no-op until Phase 5)

Handlers are stubs — real computation plugs in when the phases that need
them arrive. The infrastructure (channels, threads, push/poll, shutdown)
is real and tested.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:15:05 +02:00
jpmschweitzerandClaude Opus 4.6 2175e9b31c feat(simulation): multi-threaded executor + EntityRng (#843 Part B)
Enable Bevy multi-threaded executor via bevy_tasks multi_threaded
feature. Systems within the same TickPhase that don't share mutable
resources now run in parallel automatically.

Add EntityRng component — per-entity ChaCha20Rng seeded from
world_seed + StableId via splitmix64 mixing. More deterministic than
shared SimRng (order-independent). Migrate all monologue systems
(4 of 13 SimRng consumers) to EntityRng, removing contention that
serialized them against conversation/dialogue systems.

Add rayon dependency (infrastructure only, no par_iter calls yet).

SimRng retained for world-level randomness: conversation pairing,
knowledge transfer, dialogue, ticker, storyteller.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:08:41 +02:00
jpmschweitzerandClaude Opus 4.6 309c05d441 style(simulation): cargo fmt + fix clippy doc-nested-refdefs warning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:57:48 +02:00
jpmschweitzerandClaude Opus 4.6 cf21904a0f fix(ui): show system cursor during fullscreen implant apps
CursorRenderer now toggles Input.MOUSE_MODE_VISIBLE when gameplay is
occluded, restores MOUSE_MODE_HIDDEN when gameplay resumes. Without
this, fullscreen apps (star map, future atlas) had no cursor at all —
the custom diegetic cursor hid correctly but the system cursor was
never restored.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:56:56 +02:00
jpmschweitzerandClaude Opus 4.6 b5dff8c55e fix(ui): gameplay_occluded signal never fired on first fullscreen toggle
was_occluded was computed AFTER _active_mode and _active_app were
updated to the new values, so it always matched now_occluded on the
first toggle (both TRUE). The signal condition (was != now) never
triggered. Moved the check before the state mutation.

This bug affected every GameplayRenderer (world, entities, fog,
cursor) and the stance indicator — none of them hid on first
fullscreen app open.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:53:54 +02:00
jpmschweitzerandClaude Opus 4.6 6f02b9f4f4 fix(client): eliminate runtime warnings — radial size deferred, storyteller log removed
- world_radial.gd: use set_deferred("size", ...) to avoid anchor conflict warning
- storyteller: remove noisy "no Simmering triangles" warn (normal state, not exceptional)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:51:17 +02:00
jpmschweitzerandClaude Opus 4.6 47d4918cc8 refactor(simulation): replace ad-hoc system ordering with TickPhase pipeline (#843)
10-phase linear pipeline: PreInput → Input → Movement → Simulation →
Economy → Storyteller → Snapshot → PostSnapshot → Knowledge → TickAdvance.

Each system assigned to exactly one phase via .in_set(TickPhase::X).
Cross-phase .after()/.before() eliminated — only intra-phase ordering
remains. Prevents schedule cycles by construction.

SimulationPlugin refactored into sub-plugins by domain:
  - InputPlugin (player actions, interactions, dialogue dispatch)
  - MovementPlugin (pathfinding, movement validation, spatial indexing)
  - SocialPlugin (conversations, sound, voice enrichment, follow state)
  - EconomyPlugin (tâtonnement tick, IPC query serving)
  - TimePlugin (chunk streaming, news ticker, tick advancement)

All other plugins (NPC, Knowledge, Perception, Storyteller, Settings,
Bridge) updated to use TickPhase assignments instead of cross-plugin
ordering constraints. BridgePlugin trimmed to bridge I/O concerns only.

Part A of #843. Parts B (multi-threaded executor) and C (background
workers) follow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:51:06 +02:00
jpmschweitzerandClaude Opus 4.6 d434235985 chore(meta): add pair session work mode, prune scrapped NPC systems
- CLAUDE.md: add "Pair session" as formal work mode alongside sprint mode
- Scrap NPC ambient systems (R-012): D-078 marked superseded, content
  pattern note scrapped, overheard conversation system will be rebuilt
  from scratch after a walkable environment exists
- Agent profiles: remove NPC-drift references from Paula, Dudley, Miri;
  add cascade discipline to Miri's role

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:01:11 +02:00
jpmschweitzerandClaude Opus 4.6 9edbe40de9 fix(simulation): resolve Bevy schedule cycle — economy system ordering
tick_economy_simulation was ordered .after(advance_tick) which created a
cycle: observer_snapshot → send_snapshot → advance_tick → tick_economy →
observer_snapshot. Moved to .after(process_player_input) instead — the
economy checks time.tick which works regardless of advance order.

Also removed the .after(tick_economy_simulation) from handle_debug_commands
that was added during Sprint 34 review — same cycle root cause.

This is a symptom of #843 (ad-hoc ordering is fragile). Pair session
scheduled to replace with system set phases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:00:55 +02:00
jpmschweitzerandClaude Opus 4.6 e91bd1c7e4 fix(client): post-sprint polish — protocol v21, schedule workaround, UI fixes
- Bump client protocol version 20 → 21 to match server (#822)
- Fix render_priority parameter name (was _render_priority, unused prefix)
- Fix debug console type inference (var sub := → var sub: String =)
- Economics panel: add population row, improve key hint text
- Stance indicator: hide on gameplay_occluded (D-170 fullscreen apps)
- Remove 5 broken clothing items from manifest and delete their GLBs
  (boots_work, coveralls_basic, jacket_utility, pants_cargo, shirt_henley)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:00:40 +02:00
jpmschweitzerandClaude Opus 4.6 2319c6f6ca chore(meta): release v0.1.34
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:21:52 +02:00
jpmschweitzerandClaude Opus 4.6 e0d2ea32a5 fix(decisions): address PR #126 review — cross-refs, index, arithmetic
- Fix D-189 cross-ref: D-131 → content.md (was scope.md), fix anchor slug
- Fix D-191 cross-refs: D-093, D-095, D-138 → content.md (were architecture.md)
- Fix architecture.md footer counter: 52 → 53
- Fix D-190 ratio arithmetic: 1:100 → 1:80, 1:20-50 → 1:16-40
- Merge Destilaria Confluência/Lento into single row, update corp count ~28 → ~27
- Fix platform shorthand → platform_catalogue in D-189 §11
- Add D-189, D-190, D-191 to decisions/README.md index

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:06:23 +02:00
jpmschweitzerandClaude Opus 4.6 b69645d35c docs(decisions): D-189 brand layer architecture, D-190 volume calibration, D-191 Atlas Phase 3 scope
Sprint 34 planning workshop output:
- D-189: Brand layer architecture — 8 categories, administered pricing model,
  halo/volume tiers, 10K minor brands, Gemma 2 naming, corp tax/GDP, verb ladder
- D-190: Brand volume calibration — population-relative scale for ~80B Reach
- D-191: Atlas of the Reach Phase 3 — terrain-aware city placement, sequential
  settlement growth, Gemma 2 geographic naming, 9 MVP overlays, heightmap viewer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:04:41 +02:00
jpmschweitzerandClaude Opus 4.6 a9bd3ca408 Merge remote-tracking branch 'origin/sprint-34/client'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:19:52 +02:00
jpmschweitzerandClaude Opus 4.6 4d4dc2014d fix(ui): rebind economics panel toggle from E to N (#124 review R2)
E is claimed by InputMap "interact" action — InputMapper consumes
it before _unhandled_key_input. N is free, adjacent to M (star map),
reads as "Numbers" for the economics monitor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 14:25:07 +02:00
jpmschweitzer 08297a9cae Merge remote-tracking branch 'origin/sprint-34/copy' 2026-04-10 14:19:09 +02:00
jpmschweitzerandClaude Opus 4.6 ac6c9dd2cd style(simulation): cargo fmt economy.rs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:07:14 +02:00
jpmschweitzerandClaude Opus 4.6 1498115aba fix(simulation): address PR #125 review — tick truncation, ordering, perf, protocol test
- Widen EventPort tick methods from u32 to u64 (prevents overflow)
- Add is_identity() guard on hot-path String allocation in modifiers
- Replace Vec::remove(0) with VecDeque::pop_front() in price history
- Add .after(tick_economy_simulation) ordering for debug commands
- Fix stale PROTOCOL_VERSION assertion (20 → 21) in serialization test
- Add D-181 Phase 2 visibility scope comment on serve_econ_state_query
- Eliminate double lookup in rebuild_signals via single-pass extraction
- Track economy seed TODO with backlog ticket reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:06:54 +02:00
jpmschweitzerandClaude Opus 4.6 28d95c23a8 fix(ui): address PR #124 review — navigation, error handling, cleanup
- Economics panel: replace dead _gui_input LEFT/RIGHT with public
  navigate() method, wire [ ] keys in main.gd (avoids movement
  key conflict, fixes focus_mode=NONE issue)
- Debug console: add explicit effect guard in econ inject no-commodity
  branch so invalid effects don't fall to commodity-form error
- Snapshot consumer: null-clear GameState.economy_snapshot after
  consuming (matches one-shot consumer invariant)
- Star map: remove duplicate doc comment above set_insert_active()
- Generate script: remove stale comment, dead _WORKTREE_PARENT var,
  dead field extraction in parse_wiki_index, add try/except around
  DB queries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 14:03:49 +02:00
jpmschweitzerandClaude Opus 4.6 f41ab6c8e8 fix(content): address PR #123 round 2 — Founder terminology, names
Fix 3 issues from round 2 review:

- ovh_arc_003: "Gatebuilder" → "Founder structure" (canonical term)
- ovh_ror_002: Subordinate → Colleague (peers, not hierarchy)
- ovh_mfg_001: "Orien" → "Krev" (too close to Earth constellation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 14:03:41 +02:00
jpmschweitzerandClaude Opus 4.6 f301fdecc7 fix(content): address PR #123 review — registers, names, patterns
Fix 15 issues from Hoshe, Paula, and Miri review:

QA: correct changelog count (78 new, not 94), fix "attach" → "attaché"
in ovh_dip_002.

Narrative: fix register mismatches (ovh_rag_002 Social→Gossip,
ovh_ctx_002 Social→Gossip, ovh_med_003 Gossip→Work), fix inverted
authority dynamic in ovh_hos_002, add progression to ovh_evn_002 third
turn, replace frontier_medic/liaison with ranger/settlement_administrator
in ovh_dis_002, break extraction/industrial template pattern by
rewriting ovh_mfg_001 (social/family) and ovh_epl_001 (social/plans).

World consistency: replace Earth-ethnic names (Kessler-Dunn→Vasara-Lenn,
Kovacs→Tanev, Harlan→Sareth, Henwick→Merata), replace unanchored
location (Old Pemmar→Old Tarassa), replace lore-colliding name
(Calloway→Vedara).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:37:47 +02:00
jpmschweitzerandClaude Sonnet 4.6 77232434f4 style(simulation): cargo fmt + fix clippy warnings after economics integration
- `economy.rs`: fix empty_line_after_doc_comments (section ordering),
  use `is_multiple_of` for ECON_TICK_RATE check
- `debug.rs`, `input.rs`, `mod.rs`: rustfmt import ordering + indentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:22:47 +02:00
jpmschweitzerandClaude Opus 4.6 532fcd64a5 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:22:47 +02:00
jpmschweitzerandClaude Opus 4.6 f17a6c638c feat(ui): economics debug console commands — inject, param, inspect (#825)
Three new econ subcommands in the debug console: inject (supply
shocks/boosts), param (α/β/friction mutation), inspect (all 7
D-181 signals). Command parsing and validation complete; dispatch
wired through existing DebugCommand IPC flow. Server handler
ships with #823.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:22:30 +02:00
jpmschweitzerandClaude Opus 4.6 949d721eac feat(ui): economics monitor insert panel with placeholder data (#824)
New implant panel at implant/economics: system selector, 6-commodity
price table with trend indicators, GDP strip. Composed from D-169
component library. Ring buffer caches last 20 ticks per system.
Snapshot routing wired through snapshot_handler → GameState →
snapshot_consumers → economics_panel. Placeholder prices shown
until server ships EconomySnapshot (#822).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:22:20 +02:00
jpmschweitzerandClaude Opus 4.6 766da969de feat(ui): add system population and GDP to star map info panel (#785)
Star map popup now shows POPULATION and GDP rows when data is present.
Generation script updated to compute GDP from population × tier-based
per-capita schedule. 275/301 systems have GDP data (26 uninhabited
correctly omitted).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:22:10 +02:00
jpmschweitzerandClaude Sonnet 4.6 5f4139bc9e feat(simulation): implement economics integration sprint — #810 #821 #822 #823
Implements the full D-180/D-181 economics pipeline:

**#810 — Event input port (D-180)**
- Add EconEvent struct with Target/Effect/Duration/Visibility variants
- Implement EventPort as typed input queue for external disruptions
- Apply events in simulation step; D-179 Test 3 now uses real shock injection

**#821 — Integrate econ-sim into server tick loop**
- Extract econ-sim as library crate (lib.rs + sim.rs, Cargo.toml [lib] section)
- Add Simulation stateful runner; step() advances one economy tick
- Add EconSimResource, EconStateResource (7 D-181 signals), tick_economy_simulation
- Economy loads once at startup; graceful no-op when systems.db absent
- Server advances economy 1 tick per 10 game ticks (D-031)

**#822 — Expose economy state over IPC bridge**
- Protocol version 20 → 21
- Add EconomySnapshot, EconNodeSnapshot wire types
- Add EconStateQuery PlayerAction variant; response in economy_snapshot field
- Add EconQueryBuffer resource + serve_econ_state_query system

**#823 — Economics debug commands**
- Add InjectEconEvent, SetEconParam, GetEconState to DebugCommandKind
- Add EconDebugEffect, EconParamKind enums
- SetEconParam mutates α/β at runtime (α/β promoted to pub const + Simulation fields)
- ALPHA and BETA constants threaded through step_inner/trade_step signatures

All 1147 unit tests pass; zero warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:13:52 +02:00
jpmschweitzerandClaude Opus 4.6 38d4664559 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 12:54:26 +02:00
jpmschweitzerandClaude Opus 4.6 26a74b2bfb feat(content): author overheard conversations for all 31 zone types (#695)
Expand overheard.ron from 5 zone types (16 conversations) to full
coverage of all 31 zone types with 94 new conversations. Each zone
type has 2-4 role-pair conversations following D-078 occlusion-
resilient authoring rules. Conversations carry investigative
knowledge payloads where appropriate — institutional cover-ups,
manifest discrepancies, suppressed inspections, and cultural signals
players can follow.

Zone types added: administrative_civil, administrative_judicial,
archaeological_site, commercial_market, commercial_transit,
detention_facility, diplomatic_elite, entertainment_venue,
extraction_platform, extraction_space, extraction_surface,
industrial_manufacturing, industrial_processing, medical_facility,
military_garrison, port_fishing, port_maritime, port_space,
port_surface, research_station, residential_surface, rural_aquaculture,
rural_orbital, rural_pastoral, security_checkpoint, wilderness_frontier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 12:54:12 +02:00
jpmschweitzerandClaude Sonnet 4.6 9d9ea96be1 chore(meta): plan Sprint 34: Pulse
Close Phase 2 — wire econ-sim into game server tick loop, expose price
history and trade flows via implant insert panel, add economics debug
console commands for runtime event injection and parameter mutation.

New tickets: #821 (server tick integration), #822 (IPC bridge v21),
#823 (debug command handler), #824 (economics insert panel),
#825 (debug console econ commands). Existing: #810, #785, #811, #748,
#814, #695. 11 tickets total across server, client, copy, planning.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:20:29 +02:00
jpmschweitzerandClaude Opus 4.6 385f07b11e chore(meta): release v0.1.33
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 14:04:15 +02:00
jpmschweitzer 960adfc60a Merge remote-tracking branch 'origin/sprint-33/server' 2026-04-08 13:58:10 +02:00
jpmschweitzerandClaude Opus 4.6 6003726fbf style(simulation): cargo fmt generate_corporations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:52:29 +02:00
jpmschweitzerandClaude Opus 4.6 d45cfe0fa3 fix(simulation): address PR #122 review — determinism, correctness, labeling
- HashMap → BTreeMap throughout econ-sim for deterministic iteration (D-010)
- Fix cost_factor: multiplicative gate×zone instead of additive (trade.rs)
- Extract derive_seed to shared prng.rs, consolidate FNV-1a implementation
- Rename run_shock_test → run_no_explosion_check (not D-179 Test 3)
- Deduplicate cross-zone FX rate collection in Test 4
- Replace ORDER BY RANDOM() with deterministic ordering + ChaCha8Rng
- Make commodity coverage failure a hard error consistent with D-175
- Fix gap-fill off-by-one (4 corps → 3 when coverage = 0)
- Correct test report: EconEvent exists, location_type is body/station

All four D-179 stability tests still pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:51:23 +02:00
jpmschweitzerandClaude Opus 4.6 dcd1c1cf4e feat(simulation): add EconEvent stub and import currency zones from TOML
Add D-180 EconEvent struct (target, effect, duration, visibility enums)
with no-op handler to satisfy #809 spec. Import MARK_PRIMARY and MIXED
currency zone assignments from wiki/economics/currency_zones.toml (D-172).
All four D-179 stability tests now pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:04:59 +02:00
jpmschweitzerandClaude Sonnet 4.6 f79de07bb3 fix(simulation): resolve corp_presence location granularity bug (#805)
import_corp_presence() was inserting system IDs with location_type='system',
violating the schema which expects body/station IDs (location_type='body'|'station').

Fix:
- import_economics.py: add _resolve_hq_location() that picks the most-populated
  body in the HQ system (falling back to any body, then any station)
- econ-sim/db.rs: load_corp_presences() now JOINs bodies/stations to recover
  system_id from body/station location_ids, dropping the 'system' filter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:59:58 +02:00
jpmschweitzerandClaude Sonnet 4.6 0e6a46c4c0 style(simulation): cargo fmt for generate_corporations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:59:14 +02:00
jpmschweitzerandClaude Sonnet 4.6 822fe488d6 fix(simulation): replace HashMap/HashSet with BTreeMap/BTreeSet in generate_corporations
Project Clippy config disallows std::collections::HashMap and HashSet.
Replaced all usages with BTreeMap/BTreeSet. Also fixed:
- Unnecessary if-let on iterator rows (use flatten() instead)
- contains_key + insert on BTreeMap (use entry().or_insert_with())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:44:23 +02:00
jpmschweitzerandClaude Sonnet 4.6 b19bfb3239 feat(simulation): add corporate behavioral archetypes — Layer 3 (#809)
Implements the 6 behavioral archetypes from D-175 (Burnelli-Sheldon):

  Producer     — 1.15× production scale, neutral price signal
  Distributor  — 0.9× production, −3% price discount to move volume
  Specialist   — 1.0× production, +10% price premium for expertise
  Monopolist   — 0.8× production, withholds 25% of output, +20% premium
  Cooperative  — 1.0× production, −5% community discount
  Intermediary — 0.7× production, relies on traded goods

Archetype loading:
- Reads from corporations.behavioral_archetype (currently NULL for all corps)
- Falls back to heuristic inference from specialization text (freight → Distributor,
  extraction → Producer, luxury goods → Specialist, etc.)
- 48 corps loaded on current DB, all inferred (DB column to be populated when
  wiki corp frontmatter is extended with the behavioral_archetype field)

Applied in model.rs step():
- Per-corp effective_capacity = BASELINE_CAPACITY × production_scale
- Monopolist supply_withheld fraction reduces net output to stockpile
- Price premium: small ALPHA-scaled nudge to node price for primary commodity

All D-179 stability checks still pass with archetypes active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:27:00 +02:00
jpmschweitzerandClaude Opus 4.6 d364e1907d fix(assets): remove unused imports in sol pipeline
Ruff pre-push lint caught 17 unused imports across 7 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:25:28 +02:00
jpmschweitzerandClaude Opus 4.6 f7f2cac7e8 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:22:43 +02:00
jpmschweitzerandClaude Opus 4.6 18bdb1ed3d feat(assets): add Sol system handcrafted terrain pipeline
Custom pipeline for GJ-0 (Sol) that imports real NASA/USGS
planetary data instead of procedural generation. Produces the
same output format (heightmap.png, globe.png, markers.json).

Real data bodies:
- Earth: ETOPO2022 elevation + WorldClim climate + 14 rivers
- Mars: MOLA DEM + ferric biome classes + terraformed water
- Luna: LOLA DEM + lunar biome palette

Procedural fallback for Mercury, Venus, Phobos, Deimos.
Synthetic elevation from albedo for Io, Europa, Ganymede,
Callisto, Titan, Enceladus. Gas giants use existing renderer.

New biome classes 34-36 (ferric_dust/highland/lowland) for
Mars iron oxide surface. Earth features: 50 cities (smart
scatter by continent), 15 named rivers, oceans, mountains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:22:18 +02:00
jpmschweitzerandClaude Opus 4.6 80916471e7 fix(assets): correct globe renderer east-west mirroring
arctan2(hz, hx) wrapped longitude counter-clockwise, mirroring
east and west on the globe. Changed to arctan2(hx, hz) which
increases eastward (right on screen). Added +0.5 offset to center
the view on 0° longitude, keeping the dateline seam on the back.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:21:56 +02:00
jpmschweitzerandClaude Sonnet 4.6 26716bfc56 feat(simulation): add currency zones, exchange rates, and shadow economy (#808)
Implements D-171, D-172, and D-174 in the econ-sim binary:

Currency zones (D-171, D-172):
- Loads currency_zone from star_systems (TRACTUS_PRIMARY / MARK_PRIMARY / MIXED)
- Cross-zone (TRACTUS ↔ MARK) trade incurs 3% conversion friction
- Floating Tractus/Mark exchange rate driven by net cross-zone trade balance
- Rate clamped to [0.5, 2.0]; ALPHA_FX=0.002/tick
- Test 4: SKIP (no MARK_PRIMARY systems yet) — re-run after Compact zone data is authored

Shadow economy (D-174):
- Per-node intensity seeded from hop distance, political zone, gate topology,
  currency zone (institutional_core → low, deep_reach_isolate → high, etc.)
- Intensity reduces formal-sector demand by up to 30% at full intensity
- Reported as shadow_intensity column in CSV output

D-179 Tests 3 and 4:
- Test 3 (no explosions/negatives in 1000-tick run): PASS
- Test 4 (cross-zone FX re-stabilizes ≤50 ticks): PASS/SKIP

All four stability checks now pass (1.05% max dev on convergence, 0.00% drift).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 14:31:58 +02:00
jpmschweitzerandClaude Sonnet 4.6 a44e46ebed chore(meta): gitignore systems.db WAL journal files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:57:19 +02:00
jpmschweitzerandClaude Sonnet 4.6 4eaa669aff docs(meta): add Sprint 33 economics test plan
Test plan covering #806, #807, #808, #809 acceptance criteria aligned
with D-179 stability tests and Phase 2 economics deliverable requirements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:57:06 +02:00
jpmschweitzerandClaude Sonnet 4.6 4307092486 chore(meta): add Makefile targets for economy-db, econ-sim, and stability check
Adds: economy-db, econ-sim, econ-sim-run, econ-sim-stability targets.
economy-db compiles TOML economics data into systems.db.
econ-sim builds the simulation binary (Layer 1+2).
econ-sim-run runs 100 ticks to /tmp/econ-sim.csv.
econ-sim-stability runs D-179 Tests 1 and 2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:57:01 +02:00
jpmschweitzerandClaude Sonnet 4.6 abe7baf395 chore(meta): add tooling/econ-sim/target/ to gitignore; remove from index
Fixes accidental inclusion of Rust build artifacts in previous commit.
Adds tooling/econ-sim/target/ to .gitignore alongside existing tooling
target entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:56:52 +02:00
jpmschweitzerandClaude Sonnet 4.6 fb87f933f5 feat(simulation): add economics simulation binary with Layer 1+2 (#806, #807)
Adds tooling/econ-sim — a standalone Rust binary for the Phase 2 economics
simulation:

Layer 1 (Leontief production, #806):
- Deterministic per-run PRNG seeding of corp×site productivity (D-176)
- Fixed-coefficient production chains; scarcity cascades downstream (D-178)
- Per-capita population demand for finals and services
- Gate-energy demand reduction for fusion_fuel at connected nodes (D-186)
- Price adjustment via local tâtonnement

Layer 2 (spatial price equilibrium, #807):
- Damped tâtonnement trade flows along gate links (α=0.03, β=0.4, D-178)
- 8% transport cost per hop damps long-distance arbitrage
- Flows computed from pre-step snapshot; applied atomically
- --stability-check implements D-179 Tests 1 and 2:
  · Test 1: cold-start convergence ±5% at tick 100 → PASS (max 1.05%)
  · Test 2: long-run stability ±2% over ticks 900–999 → PASS (max 0.00%)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:56:06 +02:00
jpmschweitzerandClaude Sonnet 4.6 7b0465a8c6 feat(simulation): build Tier-3 corporation generation pipeline (#800)
Adds server/src/bin/generate_corporations and tooling/generate-corporations
wrapper. Generates ~5,000 Tier-3 corp instances from Tier-1/2 template
archetypes with seeded name generation (FNV-1a + corridor-weighted PRNG).
Writes wiki markdown stubs for each generated corporation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:55:47 +02:00
jpmschweitzerandClaude Sonnet 4.6 3447b38bcb feat(simulation): extend economy-db pipeline with corporation sync and validation (#805)
Extends import_economics.py from 6-step to 8-step pipeline:
- Loads wiki corporation markdown frontmatter as authoritative source
- Syncs corporations table (hard error on proper_name divergence per D-182)
- Populates corp_presence table (one row per corp × headquarters system)
- Splits validation: structural checks block commit; coverage checks post-commit
- D-175 Phase 2 gate: 3+ corps per commodity, 1+ corp per system with pop > 100K

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:55:40 +02:00
jpmschweitzerandClaude Sonnet 4.6 4d1b0efbbe feat(schema): add gate_links and economics tables to systems-schema.sql (#813)
Extends systems.db schema with gate_links, commodities, production_chains,
chain_inputs, corp_presence, and currency_zones tables required for the
Phase 2 economics layer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:55:31 +02:00
jpmschweitzer c605f28328 Merge remote-tracking branch 'origin/sprint-33/copy' 2026-04-07 12:03:37 +02:00
jpmschweitzerandClaude Opus 4.6 75e003846b fix(copy): address PR #121 review comments (7 issues)
- Fix sova headquarters GJ 280B → GJ 280A (core.toml + tier1.toml)
- Relocate Ross 154 Water Cooperative to Kapteyn's Star (GJ 191)
- Fix Rødvik archetype gas_giant_skimming → frontier_ice_mining
- Fix Altmark shadow economy hop band (outer_reach → mid_reach, hop 6)
- Fix Holgersen naming (was Holmgren) in north_reach.toml
- Fix Hansestadt gate count 5 → 6 in currency_zones.toml
- Fix unclosed parenthesis in shadow_economy.toml CAPACITY GAP comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:59:10 +02:00
jpmschweitzerandClaude Opus 4.6 39cd98f84d fix(copy): correct Freiholt system ID in Compact faction page
GJ 624 is Halvøy (aquaculture world), not Freiholt. Freiholt is GJ 453.
Fixed both cross-reference occurrences in compact-of-westphalia.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:27:09 +02:00
jpmschweitzerandClaude Opus 4.6 68867894e9 feat(copy): wiki commodity copy pass — 36 pages with flavor text (#812)
All 36 commodity stubs fleshed out with lore context, production chain
descriptions, and economic intelligence briefing voice. Key treatments:
fusion_fuel (D-187 8:1 ratio), services (location-bound mechanics),
brands distinguished from commodities per D-185.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:27:01 +02:00
jpmschweitzerandClaude Opus 4.6 3cdf266b5c feat(copy): generate 141 Tier-2 regional corporations (#799)
6 corridor files with 141 corporations: core (22), north_reach (21),
south_reach (20), west_reach (29), east_reach (26), deep_frontier (23).
All 36 commodity types covered. Each has name, lore/behavioral archetype,
backstory, branded products. East reach gap from D-175 filled. Feeds #809.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:26:48 +02:00
jpmschweitzerandClaude Opus 4.6 ca04658da2 feat(copy): define shadow economy intensity ranges (#803)
Per-system-type intensity bands (core 0.0-0.2 through frontier 0.6-0.9),
6 seeding modifiers, 10 named system overrides. Compact framing as
principled economic resistance per D-174. Feeds server #808.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:26:38 +02:00
jpmschweitzerandClaude Opus 4.6 9c32dc14c4 feat(copy): author MARK_PRIMARY currency zone assignments (#820)
32 MARK_PRIMARY systems (Compact members, west_reach hops 7-17),
14 MIXED systems (Compact-sympathetic, hops 5-6), with rationale
comments per D-172 political affiliation rules. Feeds server #805.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:26:28 +02:00
jpmschweitzerandClaude Opus 4.6 2fa6fb93a3 fix(agents): remove duplicate briefing line in qatux.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 08:59:30 +02:00
jpmschweitzerandClaude Opus 4.6 08f2af159e chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 08:54:01 +02:00
jpmschweitzerandClaude Opus 4.6 d20a3a1354 chore(db): remove Qdrant semantic search infrastructure (#816)
The Qdrant index (commonwealth collection, 475 points) was stale —
pointing at old worktree paths from previous sprints with no
maintenance. Grep covers all current search needs.

Removed: qdrant_connector.py, wrapper scripts (qdrant-search,
qdrant-index, qdrant-health, qdrant-count), /docs-search skill,
Qdrant/Ollama config entries, and all active references in agents,
rules, briefings, DEVOPS, CLAUDE.md, and TEAM.md. The commonwealth
collection was dropped from tower-of-joy:6333.

Historical references in discussion archives and sprint briefings
are preserved as-is.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 08:52:50 +02:00
jpmschweitzerandClaude Sonnet 4.6 7270b860cb chore(meta): plan Sprint 33: Pecunia
Economics simulation sprint — skeleton binary through corporate agents.
13 tickets across server/copy/client/ci. Stability tests (D-179) are
the sprint exit condition.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:36:31 +02:00
jpmschweitzerandClaude Opus 4.6 4028f71f2f chore(meta): release v0.1.32
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:37:48 +02:00
jpmschweitzerandClaude Opus 4.6 a6c286735e fix(schema): remove unused os import in import_economics.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:33:59 +02:00
jpmschweitzerandClaude Opus 4.6 f80cbe621e chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:33:05 +02:00
jpmschweitzerandClaude Opus 4.6 571a708d4a feat(schema): extend systems.db for economics (#804)
Add 5 new tables: gate_links (668 bidirectional edges from star-map.json),
commodities (36 types from commodities.toml), production_chains (21
Leontief recipes), chain_inputs (52 input requirements), corp_presence
(empty, populated by future pipeline). Add currency_zone column to
star_systems (D-172), archetype columns to corporations (D-175).

New import pipeline: tooling/economy-db/import_economics.py reads
TOML/JSON source files and populates the DB. Idempotent — safe to
rerun via `make economy-db`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:32:32 +02:00
jpmschweitzerandClaude Opus 4.6 4a3febbe29 refactor(schema): rename commission_certified to commission_certifiable
The flag is a susceptibility marker, not an absolute state — Commission
certification only applies when trading in TRACTUS_PRIMARY zones.
Compact-internal trade ignores it entirely. Renamed across all TOML
source files, schema docs, workshop outputs, and D-184.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:32:18 +02:00
jpmschweitzerandClaude Opus 4.6 bcf15354d2 chore(skills): improve pr-review fresh-start instructions
Clarify that every /pr-review invocation must start fresh — fetch,
check PRs, and re-read the diff even if the branch was reviewed
earlier in the same conversation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:32:06 +02:00
jpmschweitzer 8eb33551cf Merge remote-tracking branch 'origin/sprint-32/visual' 2026-04-06 23:04:31 +02:00
jpmschweitzerandClaude Opus 4.6 32c7d5fab7 feat(wiki): regenerate all body content with review fixes applied
Full regeneration of ~2,600 bodies with:
- Correct atmosphere rim colors for extended planet classes
- Clean headings and profile tables (no underscores)
- Boreal/tropical cloud coverage at correct ranges
- Gas giants without meaningless terrain fields
- All heightmaps at 1024x512

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 22:55:20 +02:00
jpmschweitzerandClaude Opus 4.6 a8379871ce fix(assets): address PR #119 review round 2 — biomes.toml, legend, profile table
1. Add 5 extended atmosphere colors to biomes.toml (globe renderer reads
   from toml, not body_def) — cold_arid, hot_arid, tropical, boreal,
   temperate_terminator. Remove dead martian entry. Slightly differentiate
   colors from base classes.
2. Fix profile table raw identifiers — apply .replace('_', ' ') to class
   field in wiki body pages.
3. Fix legend text overflow — truncate labels with ellipsis when step
   size is too narrow for full text.
4. Fix title panel underscores — use .replace('_', ' ') instead of
   .replace('_ringed', '').

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:32:39 +02:00
jpmschweitzerandClaude Opus 4.6 467c21a0f3 fix(assets): address PR #119 review — 10 issues across planet-gen pipeline
1. Fix pclass.title() underscore bug in scaffold headings (216+ files)
2. Add 5 extended classes to batch.py valid_classes set
3. Add atmosphere rim colors for cold_arid/hot_arid/tropical/boreal/temperate_terminator
4. Add cloud/tilt ranges for extended classes (boreal 55-75%, tropical 60-80%)
5. Fix legend overflow at 1024px + deduplicate rainforest labels
6. Move _check_habitability() inside loop (was only checking last body)
7. Preserve gas_giant_ringed distinction in profile table
8. Drop meaningless terrain fields from gas giant frontmatter
9. Update stale docstrings/comments for 1024x512 default
10. Add infernal ring color

All lookup tables (CLASS_TILT, CLASS_CLOUD, CLASS_POLAR_ICE, CLASS_GEOTHERMAL,
CLASS_OBLATENESS, atmo_colors, tectonic_map, substrate_map) now include the
5 extended planet classes. Body content requires full regeneration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:18:08 +02:00
jpmschweitzerandClaude Opus 4.6 4f1bf0f279 feat(wiki): generate body content for all 303 star systems
Scaffolded index.md, globe.png (512x512), heightmap.png (1024x512),
terrain.npz, and markers.json for ~2600 celestial bodies across the
Settled Reach. Heightmaps render at native simulation resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 18:51:33 +02:00
jpmschweitzerandClaude Opus 4.6 53ec7ee6d9 fix(assets): drop atomic writes, fix heightmap-size flag, default to 1024x512
The atomic .tmp→rename pattern caused silent FileNotFoundError on some
bodies. Removed in favour of direct writes — resume logic already handles
interrupted runs. Fixed --heightmap-size CLI flag which was silently
ignored due to Python default parameter binding. Changed default heightmap
resolution from 4096x2048 to 1024x512 (native simulation grid — no
information gain from upscaling).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 18:51:00 +02:00
jpmschweitzerandClaude Opus 4.6 db241b88bd refactor(schema): rename biome_summary to planet_class (D-188)
"Biome" describes per-zone vegetation classification (Whittaker table).
"Planet class" describes overall planetary character. The conflation
caused the planet generator to misclassify ~270 bodies as barren.

Scope: systems.db column, schema SQL, Rust atlas code, wiki table
headers (Biome → Class), atlas proposal JSONs, all docs/decisions,
tooling scripts. Also normalizes atmosphere vocabulary (breathable →
standard) and expands planet class mapping to all 26 wiki values.
Unknown classes default to temperate for modder safety.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 17:10:35 +02:00
jpmschweitzer 11b29baefe Merge branch 'sprint-32/client' 2026-04-06 16:45:14 +02:00
jpmschweitzerandClaude Opus 4.6 168c5e0d9b fix(ui): restore STANCE_ICONS constant removed during refactor
Startup crash — preload loop referenced STANCE_ICONS but the constant
was accidentally dropped when rewriting the icon cache logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 16:43:49 +02:00
jpmschweitzerandClaude Opus 4.6 746ed05e2d fix(ui): address PR #118 review — 5 items
- Preload stance icons at _ready() into cache Dictionary
- Null-guard shader + theme loads with push_error() + early return
- Guard _icon_mat.set_shader_parameter inside if _icon_mat:
- Icon size 18→20px per D-086, container height 44→46px to fit
- Add _apply_stance test coverage (known + unknown stance)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 16:41:27 +02:00
jpmschweitzer 6ce708a15a Merge branch 'sprint-32/copy' 2026-04-06 16:33:05 +02:00
jpmschweitzerandClaude Opus 4.6 bb61d860d6 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 16:30:57 +02:00
jpmschweitzerandClaude Opus 4.6 a14828e02a refactor(ui): add stance icons to implant badge (#787)
Replace text-only stance labels with SVG icons + tint shader per D-086.
Alpha-mask shader swaps color at runtime via ShaderMaterial parameter.
Icons: walk, crouch, careful, sprint at 18px inside ImplantPanel row.
Also fix stale UILayer/HUD test path in test_ui_framework_sprint15.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 16:30:22 +02:00
jpmschweitzerandClaude Opus 4.6 16fb644a12 fix(wiki): remove placeholder economic fields from GJ-280B stub
GJ-280B is a stub pending full generation — economic profile fields
were speculative and should not be present until the system is properly
authored.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:27:33 +02:00
jpmschweitzerandClaude Opus 4.6 07abad2df5 fix(wiki): address PR #116 review — TOML key mismatches, broken link, profile corrections
- Fix 16 lore_archetype keys in tier1.toml to match lore.toml definitions
- Fix 4 commodity ID mismatches (medical_re-embodiment, fine_dining, vehicles, entertainment)
- Add pharmaceutical_manufacturer archetype to lore.toml
- Fix station_module_constructor note (Cygni B → Sova Station Works)
- Create GJ-280B stub (Sova Station Works HQ system)
- Add Station Sova cross-reference to sova-station-works.md
- Fix GJ-251 and GJ-144 economic exports (financial_services contradictions)
- Split corporation index into Narrative Profiles and Tier 1 sections

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:25:44 +02:00
jpmschweitzer d986bfea30 Merge branch 'sprint-32/visual' 2026-04-06 16:23:07 +02:00
jpmschweitzerandClaude Opus 4.6 0c3eddee39 fix(assets): address PR #117 review comments
- Remove tracked kallast_terrain.npy (HIGH — binary in git)
- Amend D-086: stance + interaction icons delivered, not deferred
- Create ticket #818 for icon_tint.gdshader (client team)
- Fix gas giant profile table: suppress gravity/land%/hydrosphere
- Fix atmosphere_color: null when atmosphere is none
- Fix gas_giant_ringed display as gas_giant in profile table
- Re-scaffold + regenerate Ran system with fixes

.import sidecars: not applicable — gitignored by design (client/**/*.import).
Godot auto-generates on first run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 16:19:53 +02:00
jpmschweitzerandClaude Opus 4.6 f73d5ba69f data(wiki): 25 Tier-1 corporation profiles (#797)
Adds 25 new named corporations covering D-175 priority gaps: financial
services (Groombridge Settlement House, Compact Financial Exchange,
Meridian Risk), re-embodiment (Somatic Futures, Blue Collective), east
reach anchors, Compact distributors, freight, mining, medical, lattice,
and more. Each corp has lore + behavioral archetype, political tension,
administered silence, currency preference, and branded products.

Includes TOML records in wiki/economics/corporations/tier1.toml and
narrative wiki pages in wiki/corporations/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:05:25 +02:00
jpmschweitzerandClaude Opus 4.6 1e4a44da18 data(wiki): corporation archetype taxonomy (#798)
Adds wiki/economics/archetypes/ with 28 lore archetypes (WHAT/WHERE)
and 6 behavioral archetypes (HOW) as TOML templates. These are the
generation templates for Tier-2 and Tier-3 corporations per D-175.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:04:53 +02:00
jpmschweitzerandClaude Opus 4.6 199770ee41 data(wiki): add Bodies/Population rows + economic profiles to 301 systems (#790, #781)
Adds Bodies and Population rows to System Profile tables across all 301
star-system wiki pages. Adds Industries, Exports, and Imports economic
profile rows derived from systems.db economic_base + body type + topology.
Includes 9 body/station name changes from cultural diversity remediation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:03:56 +02:00
jpmschweitzerandClaude Opus 4.6 941a7218d2 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:00:45 +02:00
jpmschweitzerandClaude Opus 4.6 db3daecfaf chore(config): add JSON syntax validation to pre-push hook
Validates changed JSON files using python3 -m json.tool (zero
dependencies). Same diff-based scoping as existing checks — only
files changed vs remote are validated. Blocks push on syntax errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:00:29 +02:00
jpmschweitzerandClaude Opus 4.6 c57488a86b fix(skills): filter NULL teams from sprint worktree creation
Tickets with no team assignment caused start-sprint.sh to create
a spurious "sprint-N/None" worktree and Ptyxis tab. Filter at both
the SQL level (AND team IS NOT NULL) and Python level (if r['team']).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:00:21 +02:00
jpmschweitzerandClaude Opus 4.6 6286cf5d2c chore(assets): fix ruff lint warnings in planet-gen scripts
Remove unused imports (os, sys, math, ImageFilter, gaussian_filter,
MAX_BIOME_ID) flagged by ruff F401.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:57:05 +02:00
jpmschweitzerandClaude Sonnet 4.6 c5c6922385 content(wiki): cultural diversity sweep — apply #784 plan (#793)
20 changes across 18 systems implementing the approved remediation plan
from the cultural diversity audit (docs/design/cultural-diversity-audit.md).

DB changes (15 bodies/stations in systems.db):
- Thai bodies: Saikaew (GJ 635B), Nakhon Sung (GJ 667C), Doi Kaeo (GJ 117)
- Khmer bodies: Banteay (GJ 434), Srok Thmei (GJ 866A)
- Ukrainian: Chornozem body + Svoboda station (GJ 729)
- Caribbean: Lavenille body (GJ 873), Accompong Works station (GJ 406)
- Malay: Pulau Baru body (GJ 555), Balai station (GJ 449)
- Scientists: Huygens Platform (GJ 251), Ramanujan Station (GJ 699),
  van Leeuwenhoek Station (GJ 820B), Turing Survey (GJ 663A)

Wiki calibration note additions:
- GJ 635B/667C/117/434: Thai cultural presence (survey teams, founding cohorts)
- GJ 866A/873: Khmer/Caribbean community naming origin
- GJ 729: Ukrainian founding cooperative in west_reach
- GJ 1245A/75: Igbo/Nigerian heritage explicitly acknowledged
- GJ 172: Malay kelong dual-heritage in Kaelong name
- GJ 601A: Russian secondary founding stream in Ostmark

Horizon stations not renamed. No corridor reassignments.
Conservative: systems with existing identity untouched.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 15:56:38 +02:00
jpmschweitzerandClaude Opus 4.6 c32cdf11d4 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:55:38 +02:00
jpmschweitzerandClaude Opus 4.6 c3d6230eed feat(assets): generate Ran system bodies — 10 planets/moons with assets
First system through the planet generator pipeline. Per-body wiki
pages with YAML frontmatter at wiki/star-systems/GJ-144/bodies/.
Each body has: index.md (definition + profile), heightmap.png
(4096×2048), globe.png (512×512), and markers.json (rivers, oceans,
mountain ranges).

Bodies: GJ144b-g + moons. Includes temperate (Kallast, Vethis),
frozen, barren moons, and a ringed gas giant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:54:58 +02:00
jpmschweitzerandClaude Opus 4.6 1c6c5b9978 feat(assets): heightmap pipeline spike — prototype + test inputs
PO-built prototype (FBM terrain, Whittaker biomes, procedural globe)
with test body definitions for all planet types. Replaces pyplatec
approach. Spike validates the pipeline architecture for batch #817.

Includes handover doc, 9 test body definitions, and updated spike
pipeline documentation. Stale pyplatec outputs removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:54:35 +02:00
jpmschweitzerandClaude Opus 4.6 f84275edf3 feat(assets): planet generator pipeline — heightmap + globe from wiki data
Full terrain-to-render pipeline at tooling/planet-gen/:
- body_definition_parser: reads system index.md → body definitions
- planet_simulation: FBM + Voronoi ridges, Whittaker biome classification,
  D8 river routing, physics-driven craters (atmo × tectonics scaling)
- render_heightmap: 4096×2048 cartographic maps with hillshade
- planet_renderer: 512×512 globe with terrain UV mapping, clouds, rings
- biomes.toml: externalized color/classification tables (single source)
- scaffold_bodies: creates per-body index.md with YAML frontmatter
- batch: unattended processing with error handling, resume, determinism check

Supports all body types: temperate, arid, frozen, volcanic, barren,
oceanic, gas giant (banded + ringed), and moons (half-size, grey, cratered).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:54:15 +02:00
jpmschweitzerandClaude Opus 4.6 f968ee23f1 chore(config): add scipy + Pillow deps, gitignore terrain/spike data
scipy 1.17.1 and Pillow 12.2.0 — both CVE-audited clean against
NVD + OSV (2026-04-06). Required by planet generator pipeline.
Gitignore generated terrain.npz, spike intermediates, and tmp files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:53:55 +02:00
jpmschweitzerandClaude Opus 4.6 35ad862387 fix(assets): revise health cross icon and careful stance silhouette
Health icon: replaced overlapping outlined rects (railroad crossing
effect) with a single solid polygon path. Careful stance: redesigned
from diagonal-arm to horizontal T-shape for clear differentiation
from walk at 20px.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:53:34 +02:00
jpmschweitzerandClaude Opus 4.6 879ef6092f fix(ui): address PR #115 review — 8 items
- Fix stale test path UILayer/HUD → InsertOverlay/HUD
- Fix D-record citation D-051 → D-049 in main.tscn
- Add null guards to hud.gd update methods (pre-_ready safety)
- Minimap: dirty-flag queue_redraw instead of per-frame
- Remove redundant _panel.size.x, debug print
- Fix DebugOverlay/GauntletHUD positioning comments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 15:45:47 +02:00
jpmschweitzerandClaude Opus 4.6 8c239d522c chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 12:52:34 +02:00
jpmschweitzerandClaude Opus 4.6 b2b67d52f8 refactor(ui): add implant container frame to minimap (#787)
Draw ImplantTheme-styled StyleBoxFlat behind the circular minimap for
visual consistency with other implant components. Stance badge icons
remain blocked by #795 (visual team).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 12:52:06 +02:00
jpmschweitzerandClaude Opus 4.6 283518d332 refactor(ui): convert HUD status + interaction prompt to implant (#786, #788)
HUD: merge TimeDisplay into ImplantPanel (time/health/perception in one
panel), remove dead tscn nodes, move HUD to InsertOverlay for D-051 bloom.
Interaction prompt: convert from PanelContainer+StyleBoxFlat to Control
with ImplantPanel+ImplantDataRow, fix panel height for 42px minimum.
Tests updated for new scene structure and public API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 12:51:58 +02:00
jpmschweitzerandClaude Opus 4.6 3c7231e275 fix(ui): resolve HUD layout overlaps (#792)
- DebugOverlay: position comment updated, tscn is source of truth
- GauntletHUD: moved from y:48 to y:252 (below StanceIndicator)
- StanceIndicator: offset_top 192→200, height 26→44px for ImplantPanel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 12:51:48 +02:00
jpmschweitzerandClaude Sonnet 4.6 c9541362f9 fix(assets): clean up stale grain_belt comment in settlement scorer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:19:42 +02:00
jpmschweitzerandClaude Sonnet 4.6 f64ee9dded feat(assets): annotated heightmap pipeline — Kallast spike #778
Validates full heightmap pipeline: pyplatec tectonics → erosion →
dynamic sea level → terrain classification (13 classes) → D8 river
network → settlement placement → geographic PNG + settlement JSON.

Key technical decisions:
- Dynamic sea level via np.percentile (pyplatec output is right-skewed;
  fixed fraction gives ~0.2% land, not 40%)
- Terrain classes as fractions of land_range (not fixed offsets)
- Two-layer model: geographic PNG + human-layer JSON sidecar
- Rivers painted AFTER LANCZOS upscale via NEAREST neighbor mask
  (painting before blurs rivers into invisibility)
- grain_belt removed — reclassified as grassland (natural terrain)
- Irrigation overlay removed — human activity, lives in JSON sidecar

Output: 4096×2048px (1024×512 simulation grid, 5.2s total runtime).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:19:23 +02:00
jpmschweitzerandClaude Sonnet 4.6 1cffc83dd7 feat(assets): procedural planet renders for wiki/GTTR — #779
7 planet type PNGs (512×512px RGBA) covering all biome_summary values:
temperate, temperate_terminator, oceanic, arid, frozen, volcanic, barren.

Pure Python ray-sphere renderer (spikes/planet-renders/generate_planets.py)
— numpy/PIL only, no Godot dependency, ~2s for all 7 types. Seeded from
body_id for reproducibility. Resolves Q-064 (Godot 3D planet plugin
evaluation — superseded by headless Python approach).

Assets at client/assets/planets/, 512×512 RGBA, displayed at 240×240 in
the body-info-panel navigator and GTTR arrival window.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:19:06 +02:00
jpmschweitzerandClaude Sonnet 4.6 b5cb93203b feat(assets): implant HUD icon set v0.1 — #795
13 SVG icons covering stance indicators (walk/careful/sprint/crouch),
status badges (health, perception), interaction prompts (talk/observe/
follow/examine), and inventory silhouettes (manifest, access token,
comm log).

All D-086 compliant: 1px stroke, #c8d0e0 chrome, round linecaps,
minimal anchor points, no icon fonts, runtime-replaceable via
ShaderMaterial. Sizes: 20×20 (stance/status), 16×16 (actions),
40×40 (inventory). Unblocks client #787.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:18:56 +02:00
jpmschweitzerandClaude Opus 4.6 f1b97d4a88 fix(economics): address 17 PR review comments on #114
Blocking fixes:
- V-13: legal_services demand_model compliance→market (build-breaker)
- D-184: political flag names match TOML (commission_certified etc.)
- D-184: professional services list matches TOML
- D-173: elasticity count 5→4, cross-ref to D-184
- Workshop outcomes: fix self-contradicting fusion fuel statement
- R-011: move to end of file (was between D-183 and D-184)

Should-fix:
- D-166 domain link scope.md→architecture.md
- Sprint briefing counts updated (13→17 D-records, #801 marked done)
- Burnelli-Sheldon added to agent roster + briefing created
- questions-architecture.md footer tally updated
- medical_services demand_model utility→market
- timber display name drop "/Biomass"

Polish:
- D-171 Groombridge clarified (clearing house, not co-issuer)
- hospitality description has Reach texture
- commission_certification shadow_viable=false rationale in description
- Ticket #801 description updated
- Wiki pages regenerated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 22:18:41 +02:00
jpmschweitzerandClaude Opus 4.6 69d0659066 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 22:01:00 +02:00
jpmschweitzerandClaude Opus 4.6 ac05dbd21f data(economics): commodity catalog — 36 types, 21 production chains
Workshop #801 deliverable: commodities.toml (36 commodities across
5 tiers), production_chains.toml (21 Leontief recipes including
2 substitution routes), schema.md (SQL DDL + validation rules).

Key design choices: brands are not commodities (separate layer),
water→fuel at 8:1 yield, 3 political sub-flags replacing single
boolean, gate energy-over-gate as commercial service. Wiki stub
pages generated for all 36 commodities.

Tickets #811–#815 created for follow-up work. #801 closed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 22:00:33 +02:00
jpmschweitzerandClaude Opus 4.6 9b1258568e docs(decisions): economics design workshop #796 — D-171 through D-187
Workshop #796 produced 13 D-records (D-171–D-183) defining the Phase 2
economics layer: three-currency system, commodity taxonomy, shadow
economy, corporation tiers, productivity seeding, simulation
architecture, TOML data pipeline.

Workshop #801 added D-184–D-187: commodity catalog (36 types), brands
are not commodities, gate transmission levels (mass+data+energy),
fusion fuel as intermediate (8:1 water yield).

R-011 filed (single currency rejected). Burnelli-Sheldon agent added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 22:00:18 +02:00
jpmschweitzerandClaude Opus 4.6 b4e4c65830 docs(meta): update Sprint 32 briefings after workshop #796
Post-workshop briefing updates: joint notes with dependency chain,
planning team task list, copy/visual team scope adjustments, TEAM.md
roster update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 22:00:03 +02:00
jpmschweitzerandClaude Opus 4.6 3d1ff6ad2e chore(meta): plan Sprint 32: Landfall
14 tickets across 4 teams + planning track:
- Copy (4): cultural audit #784 (analysis only), batch edits #793
  (blocked), system profiles #790, economic profiles #781
- Visual (4): heightmap spike #778, batch heightmaps #794 (blocked),
  screenshots #779, icon design #795
- Client (4): HUD layout #792, status/minimap/interaction → implant
  #786/#787/#788. #787 blocked by icon design.
- Planning (1): Phase 2 economics kickoff #796

New tickets: #793, #794, #795, #796

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:40:27 +02:00
jpmschweitzerandClaude Opus 4.6 050aeafc01 chore(kit): remove worktree-update skill, fix pr-review severity tiers
- Delete worktree-update skill template (replaced by sprint branches)
- Remove worktree-update references from settings, manifests, tests
- Update customization-examples to sprint-branch language
- Remove three-tier severity from pr-review template (every comment
  is actionable, no suggestion tier)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:21:20 +02:00
jpmschweitzerandClaude Opus 4.6 706b4565d4 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:17:14 +02:00
jpmschweitzerandClaude Opus 4.6 5683b1dc4b chore(config): fix skill contradictions, gitignore .obsidian, docs cleanup
- sprint-start: remove three-tier severity (critical/warning/suggestion),
  align with pr-review policy (every comment is actionable)
- sprint-start: resolve {team_scope_dirs} dangling placeholder
- .gitignore: add .obsidian/ directory
- DEVOPS.md: remove "pending setup" from gdlint (now enforced)
- Remove GEMINI-SCAN.md one-off scan artifact

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:16:51 +02:00
jpmschweitzerandClaude Opus 4.6 cfddcfb117 docs(briefings): extract shared PROJECT_STATE.md, purge stale boilerplate
Create PROJECT_STATE.md at repo root with cascade phase table,
decision count, and current sprint. All 18 briefings now reference
this file instead of duplicating the project state paragraph.

Also removes stale content from briefings:
- "v0.2 direction: generator-first" (contradicts D-166 cascade)
- "Sprint 25 generator spike confirmed as critical path" (Phase 5)
- Agent-specific generator prerequisite claims
- Gestalt "v0.2 Pivot" section reframed as "Key Design Pivots"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:16:39 +02:00
jpmschweitzerandClaude Opus 4.6 1254e41dc5 docs(decisions): amend stale D-records, close resolved Q-records
Amend 7 high-priority D-records with supersession notes:
- D-012: map size superseded by D-094 (256x256)
- D-044: outline color scoped to perception mode per D-154
- D-061: width corrected from 640px to 1200px per D-076
- D-086: smuggler scope removed per D-117/D-122
- D-090: marked [SUPERSEDED] by D-117 (tycoon replaces smuggler/detective)
- D-035: character enum marked obsolete post-D-117
- D-119: sequencing superseded by D-166 cascade

Close 4 resolved scope questions (Q-002, Q-004, Q-005, Q-007).
Fix questions.md index — was 61, actually 96 (33 architecture Qs missing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:16:27 +02:00
jpmschweitzerandClaude Opus 4.6 ef072416ba docs(architecture): update CLAUDE.md, project structure, D-170 for implant UI system
- CLAUDE.md: added Implant UI component library section (D-169, D-170)
- project-structure.md: added client/ui/implant/, hud_groups.gd, gameplay_renderer.gd
- D-170: corrected implementation note from visibility toggling to z-index layering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:43:10 +02:00
jpmschweitzerandClaude Opus 4.6 8421ac4d27 chore(config): gitignore settings.db WAL files, sync Cargo.lock version
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:38:55 +02:00
jpmschweitzerandClaude Opus 4.6 eeac6405d6 fix(ui): HUD layout pass — resolve overlapping elements (#792)
- HUD status panel moved to y=80 (below TimeDisplay at 16-70)
- Removed redundant time row from HUD (TimeDisplay handles it)
- Debug overlay starts at y=150 (below HUD status panel)
- Stance indicator moved below minimap (y=192, was overlapping at y=16)
- Interaction prompt moved to y=-240 (above dialogue box at -200)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:35:37 +02:00
jpmschweitzerandClaude Opus 4.6 26a63d3899 feat(ui): convert HUD status, stance, interaction prompt to implant theme (D-169)
- HUD: programmatically builds ImplantPanel with health/mode/time rows,
  replaces raw MarginContainer/VBoxContainer/Labels
- Stance indicator: ImplantPanel with color-coded ImplantDataRow,
  replaces custom _draw() with hardcoded bg/text
- Interaction prompt: applies implant theme StyleBox + colors to
  existing PanelContainer layout

Minimap left as-is — its circular frame is a distinct diegetic element,
not rectangular panel chrome.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:25:33 +02:00
jpmschweitzerandClaude Opus 4.6 a7fbf7a0d9 feat(ui): zoom-based LoD for star map labels
Labels now appear based on zoom level and system importance:
- Always: selected + hovered
- Zoom >= 1.2: hubs, junctions, gateway
- Zoom >= 2.0: all named systems
- Below 1.2: only selected/hovered

Prevents label soup at overview zoom levels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:20:00 +02:00
jpmschweitzerandClaude Opus 4.6 3439b05e88 feat(ui): show all system names on star map, centered below dots
All named systems get a label below their dot. text_dim for default,
text_primary for selected/hovered. Removed duplicate label from
_draw_selection (now handled by _draw_systems).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:17:31 +02:00
jpmschweitzerandClaude Opus 4.6 9cdc86d65d fix(data): star map bodies + population from systems.db, not wiki
Generator now queries bodies/stations tables directly for habitable
count, inhabited count, and total population. All 301 systems have
data — unsettled systems show "0 habitable · 0 inhabited" and "0".
Fixed stale worktree path to systems.db.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:11:39 +02:00
jpmschweitzerandClaude Opus 4.6 3f611ae0f7 fix(ui): star map info — hop merged into corridor line, bodies on one line
"CORE corridor (hop 1)" with sector color. Bodies back to single line
"1 habitable · 1 inhabited" as originally formatted in the data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:03:25 +02:00
jpmschweitzerandClaude Opus 4.6 5400044afe feat(ui): star map info panel — one item per line, GDP placeholder
Layout now shows each field on its own line:
  star type / hop / corridor / habitable / inhabited / pop / GDP
GDP shows "—" placeholder until economics data is available.
Population shows full number without ellipsis.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:57:49 +02:00
jpmschweitzerandClaude Opus 4.6 34a21d4cd0 feat(rendering): GameplayRenderer base class — occlusion for all renderers (D-170)
New GameplayRenderer extends Node2D with built-in occlusion support.
Subclasses override _gameplay_process() and _gameplay_draw() instead
of _process() and _draw() — these are skipped when a fullscreen
implant app covers gameplay.

Migrated all 5 gameplay renderers:
- CursorRenderer: hover detection + bracket drawing paused
- EntityRenderer: position lerping paused
- FogEntities: fog dot rendering paused
- SoundIndicatorRenderer: directional arrows paused
- WorldRenderer: tile/fog/entity updates paused

No more manual occlusion wiring — the base class handles everything.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:51:30 +02:00
jpmschweitzerandClaude Opus 4.6 a01f7c9cfb fix(ui): cursor z-index 100, adjacent wraps, panel clamp with reset_size
- CursorRenderer z_index=100 so it renders above all HUD/implant layers
- Adjacent systems uses ImplantTextBlock (wraps) instead of ImplantDataRow (clips)
- Panel calls reset_size() before clamping to get accurate height on first frame

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:34:34 +02:00
jpmschweitzerandClaude Opus 4.6 0d46180e39 fix(ui): remove cursor_renderer from gameplay group
CursorRenderer must always render on top — shouldn't have its z-index
managed by HudGroups. It's a world-space renderer, not a HUD element.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:30:35 +02:00
jpmschweitzerandClaude Opus 4.6 7fb155c9a8 fix(ui): restore StarMap node in hud.tscn
Accidentally deleted when exploring reparenting options. The z-index
model means it stays as a child of HUD — no reparenting needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:28:31 +02:00
jpmschweitzerandClaude Opus 4.6 e4fafb66d5 style(client): gdformat main.gd
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:26:01 +02:00
jpmschweitzerandClaude Opus 4.6 ffef2b3d54 fix(ui): gdlint ordering — signals before enums, signal uses int for Mode
Moved signals above enum per gdlint class-definitions-order rule.
app_changed signal uses int instead of Mode type to avoid the
forward-reference that forced wrong ordering. Callers compare
against HudGroups.Mode.FULLSCREEN etc unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:25:22 +02:00
jpmschweitzerandClaude Opus 4.6 2e01081de7 refactor(ui): HudGroups z-index model + gameplay occlusion (D-170)
Replaced visibility toggling with z-index layer management. Nothing
gets hidden — fullscreen apps render on top, gameplay stays underneath.
Supports future hybrid layouts (insert-mode map alongside gameplay).

Added gameplay_occluded signal: WorldRenderer skips tile/fog/entity
updates when a fullscreen implant app covers it. Prevents wasted
render work behind opaque overlays. Other renderers can connect to
the same signal.

Modes: GAMEPLAY (z=0), INSERT (z=10), FULLSCREEN (z=20), MODAL (z=30).
Star map uses app_changed signal to toggle its own visibility based
on whether its app is active.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:21:31 +02:00
jpmschweitzerandClaude Opus 4.6 09e0c522bd fix(client): split PowerProfile enum to fix last gdlint warning
gdformat collapsed enum + 3 inline comments into one 300-char line.
Split to multi-line enum with doc comments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:10:13 +02:00
jpmschweitzerandClaude Opus 4.6 88c7407cb6 style(client): gdformat all 52 GDScript files — zero format warnings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:09:10 +02:00
jpmschweitzerandClaude Opus 4.6 572e66f026 fix(ui): remove Node2D cursor_renderer from Control-typed HudGroups array
CursorRenderer extends Node2D, not Control — can't be registered in
HudGroups. Removed from gameplay group. Also dropped typed array
annotation to avoid runtime type mismatch errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:07:34 +02:00
jpmschweitzerandClaude Opus 4.6 22d74ab6ad refactor(ui): star map registers as implant/map/starchart
Deeper taxonomy: map is a family of views (starchart, system, station,
planet, location), not one app. Same HudGroups mechanics — just a
more specific path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:05:26 +02:00
jpmschweitzerandClaude Opus 4.6 5ad2452213 feat(ui): implant app hierarchy in HudGroups (D-170)
Implant apps use hierarchical groups: implant/map, implant/wiki,
implant/journal, etc. Apps are mutually exclusive with each other
and with gameplay. Opening implant/map hides gameplay + any other
implant app. Closing returns to gameplay.

API: open_app(), close_app(), toggle_app(), is_app_active().
Star map uses implant/map. Future apps register their own group.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:04:19 +02:00
jpmschweitzerandClaude Opus 4.6 f037bcaa81 feat(ui): HUD visibility groups — gameplay vs implant layers (D-170)
New HudGroups autoload manages show/hide of related HUD elements as
groups. Exclusive groups (gameplay, implant) are mutually exclusive —
opening the star map hides stance indicator, minimap, interaction
prompts, etc. Closing it restores them.

Gameplay nodes registered in main.gd _ready(). Star map registers as
implant group and uses toggle_group() instead of direct visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:01:28 +02:00
jpmschweitzerandClaude Opus 4.6 44cc8cda4c fix(ui): data rows clip to one line, panel clamped to screen
ImplantDataRow uses clip_text + ellipsis instead of wrapping — each
info item stays on one line. Panel position clamped so bottom edge
never exceeds screen bounds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:57:35 +02:00
jpmschweitzerandClaude Opus 4.6 f0c6e97742 fix(ui): correct Godot 4.6 size flag constants
SIZE_FLAGS_FILL → SIZE_FILL, SIZE_FLAGS_SHRINK_END → SIZE_SHRINK_END.
The _FLAGS_ prefix was removed in Godot 4.6.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:54:11 +02:00
jpmschweitzerandClaude Opus 4.6 6ad1c8255f fix(ui): implant panel width constraint and text wrapping
Panel now clips content and constrains children to its width.
Components set autowrap and SIZE_FLAGS_FILL so text wraps within
the panel instead of overflowing. Bold markdown markers stripped
in ImplantTextBlock constructor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:51:25 +02:00
jpmschweitzerandClaude Opus 4.6 962596fd57 feat(ui): implant component library + star map refactor (D-169)
New component library at client/ui/implant/:
- ImplantTheme: shared Resource with colors, spacing, font sizes
- ImplantPanel: PanelContainer root with themed background/border
- ImplantHeader: title + subtitle
- ImplantSeparator: themed horizontal rule with above/below spacing
- ImplantDataRow: single-line text with optional color override
- ImplantTextBlock: RichTextLabel for wrapping narrative text
- default_implant.tres: default theme resource

Star map info panel refactored from 140 lines of manual draw_string
calls to 50 lines of component composition via _rebuild_info_panel().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:47:23 +02:00
jpmschweitzerandClaude Opus 4.6 4dd95ba50b docs(decisions): D-169 Implant UI component library — Control node tree
Control nodes with shared Theme resource, not _draw() components.
Dynamic implant system (hardware variants, upgrades, faction overlays)
requires layout automation and theme swapping that _draw() can't provide.

Component set: Panel, Header, Separator, DataRow, TextBlock, StatusBadge,
ProgressBar, TabRow, Expandable. Semantic color roles defined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:43:05 +02:00
jpmschweitzerandClaude Opus 4.6 8d02b110ae fix(ui): star map info panel spacing — split separator padding
Separators now have explicit above (6px) and below (12px) spacing
relative to text baselines. Previously a single 8px block didn't
account for baseline positioning, causing text to merge with
separator lines. Line height bumped to 18px.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:35:05 +02:00
jpmschweitzerandClaude Opus 4.6 73ed0be22c chore(process): quality and polish is the golden standard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:31:44 +02:00
jpmschweitzerandClaude Opus 4.6 b14c19c65e chore(process): all review comments actionable + pushback flow
Remove severity tiers from PR reviews. Every comment must be addressed
before merge — no "suggestion" category that gets skipped. Teams can
push back on specific comments with a technical rationale; team lead
evaluates and can retract. Bar: "the reviewer was wrong" not "we don't
want to."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:30:21 +02:00
jpmschweitzerandClaude Opus 4.6 d2ea687ec3 fix(ui): star map info panel — dynamic height measurement
Panel now measures all variable content (GTTR text, adjacent systems)
before calculating height. Fixes clipping on hub systems with many
neighbors and long GTTR excerpts. Unified separator spacing (8px).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:27:45 +02:00
jpmschweitzerandClaude Opus 4.6 a22bc6ce46 chore(meta): release v0.1.31
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:23:56 +02:00
jpmschweitzerandClaude Opus 4.6 ea57a9da70 fix(db): auto-unassign done tickets on sprint close
sprint stop now clears assigned_to on all done tickets in the closing
sprint. Agents don't clean up after themselves — this was causing
5 bookkeeping warnings every sprint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:20:31 +02:00
jpmschweitzer bee93ddbb7 Merge remote-tracking branch 'origin/sprint-31/client' 2026-04-05 10:17:15 +02:00
jpmschweitzerandClaude Opus 4.6 4eb54a6f7a fix(client): resolve all gdlint warnings — zero warnings policy
Fix 354 gdlint warnings across 65 files: 194 class-definitions-order
(reorder declarations), 138 max-line-length (split long lines),
22 code issues (unused args, no-else-return, naming). Update .gdlintrc
to exclude addons/ and raise max-public-methods for test files.
No logic changes — declaration order, whitespace, and naming only.

Ticket: #783

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:59:56 +02:00
jpmschweitzer 5941ff791f Merge remote-tracking branch 'origin/sprint-31/server'
# Conflicts:
#	CHANGELOG.md
2026-04-05 09:59:46 +02:00
jpmschweitzerandClaude Opus 4.6 260eefcdd1 refactor(client): decompose main.gd and game_state.gd god objects
Extract SnapshotHandler (static snapshot parsing), SnapshotConsumers
(non-dialogue consumers + audio handlers), and DialogueCoordinator
(dialogue consumers + signal handlers) as class_name scripts.

main.gd: 28KB → 13KB. game_state.gd: 20KB → 8.5KB.
Autoload parse-order safety maintained via load() inline pattern.

Ticket: #775

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:59:34 +02:00
jpmschweitzerandClaude Opus 4.6 8eb9e0a383 feat(ui): star map click-through with wiki/GTTR popup and edge visibility
Show system profile popup on select: name, star type, hop distance,
corridor, GTTR excerpt, body count, population, adjacent systems.
Edge rendering changed to show no edges by default, only selected
system's gate lines on click. Star map data extended with wiki fields
for all 301 systems.

Ticket: #780

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:59:18 +02:00
jpmschweitzerandClaude Opus 4.6 65bcbd0aa7 fix(meta): move changelog entry to root CHANGELOG.md
Remove erroneous server/CHANGELOG.md and add the atlas split entry
to the project root CHANGELOG.md where it belongs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:56:53 +02:00
jpmschweitzer 784bcbf034 Merge remote-tracking branch 'origin/sprint-31/copy' 2026-04-05 09:54:47 +02:00
jpmschweitzer 742d51a3c4 Merge remote-tracking branch 'origin/sprint-31/ci' 2026-04-05 09:54:31 +02:00
jpmschweitzerandClaude Opus 4.6 84c3314924 fix(skills): sprint-start team branch must not run pr-review
Team worktree windows stay on their sprint branch. PR reviews run from
main. Updated step 9b to explicitly stop the team lead from spawning
reviewers — push the PR and wait for feedback instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:47:50 +02:00
jpmschweitzerandClaude Opus 4.6 f60ae48769 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:40:32 +02:00
jpmschweitzerandClaude Opus 4.6 fe788a0ae3 fix(simulation): rustfmt long argument lists in atlas dispatch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:40:28 +02:00
jpmschweitzerandClaude Opus 4.6 74d6aee0c1 chore(ci): add ruff to pre-push hook and Makefile
Add Python/ruff block to .config/hooks/pre-push (runs on tooling/
changes). Add lint-python and setup-venv Makefile targets, wire
both into make lint and make setup respectively.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:40:10 +02:00
jpmschweitzerandClaude Opus 4.6 2300d9659d fix(tooling): remove unused imports flagged by ruff
Drop unused hashlib, math, and os imports from assign-astro-ids,
generate-star-map, and test_quaternius_raw to pass ruff clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:40:02 +02:00
jpmschweitzerandClaude Opus 4.6 980201fb49 chore(db): wire ensure_venv() into connector scripts
Add venv auto-activation to audio_batch, audio_connector,
image_connector, qdrant_connector, and trellis_connector.
Scripts re-exec into .venv/bin/python when invoked outside
the venv, with a friendly error if .venv is missing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:39:55 +02:00
jpmschweitzerandClaude Opus 4.6 1640c880da fix(simulation): suppress clippy too_many_arguments on cmd_add_body
The refactor from &Commands to individual parameters exposed the
13-argument signature to clippy. Allow attribute is appropriate
here — the parameters map 1:1 to DB columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:39:52 +02:00
jpmschweitzerandClaude Opus 4.6 d86ebada8d refactor(db): extract shared DB module from duplicated patterns
Create tooling/db/common.py with resolve_db_path(), load_config(),
get_connection(), and ensure_venv(). Update ticket, sprint,
sqlite_connector.py, and decisions_sync.py to import from common
instead of duplicating. Fixes pre-existing NameError in
decisions_sync.py (missing import os). Add pyproject.toml with
ruff config (ruff==0.15.9, CVE-clean) and dev dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:39:46 +02:00
jpmschweitzerandClaude Opus 4.6 7f23185035 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:38:15 +02:00
jpmschweitzerandClaude Opus 4.6 fb5c8854e8 refactor(simulation): split atlas.rs subcommands into modules
Extract 2119-line monolithic atlas.rs into 8 focused modules under
src/bin/atlas/: main.rs (thin dispatch), common.rs (shared types and
DB helpers), show.rs, mutate.rs, stats.rs, systems.rs, author.rs,
and sync_wiki.rs. Commands enum stays in main.rs; each match arm
delegates to module::cmd_fn(&conn, args). No behavior change.

Closes #776.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 09:37:04 +02:00
jpmschweitzerandClaude Opus 4.6 9135f1c656 data(wiki): update west corridor Adams ratings for Lichtung and Haltefenn (#766)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:36:31 +02:00
jpmschweitzerandClaude Opus 4.6 a97e2267f0 data(wiki): per-body GTTR entries for 25 moons and stations (#782)
Hub systems at hops 0-4 with pop >10K now have individual GTTR entries.
Covers Gateway, Sirius, ACB, Cygni B, Bastion, Nova Roma, Fremantle,
Kampala Gate, Aurelius, Puerto Rivera, and Okafor Station. Each follows
the established drifter's-voice format with Adams rating and practical
information sections.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:36:16 +02:00
jpmschweitzerandClaude Opus 4.6 807e8b74a6 data(wiki): cultural diversity sweep — 4 additional systems enriched (#766)
Schuilhoek (GJ-68): data-wealth commercial dynasty subversion.
Helderoog (GJ-111): genetic protocol precision subversion.
Helle Wacht (GJ-598): philosophical colony governance cost.
Altgrund (GJ-189): institutional archive refugee integration.
Also updates Adams ratings in gttr-deep.md for all enriched systems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:35:56 +02:00
jpmschweitzerandClaude Opus 4.6 898b9c747f data(wiki): enrich Nova Estrada and Entremeio GTTRs to batch standard (#774)
Nova Estrada: added founding agreement time-bomb, thirty-year cost, and
infrastructure audition sections. Entremeio: added transit policy, lake
settlement, and Assembly defiance sections. Both now match Kettenschmied
quality bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:35:47 +02:00
jpmschweitzerandClaude Sonnet 4.6 2ee095306b docs(wiki): cultural stereotyping sweep — 6 entries enriched (ticket #766)
Subversions added or made explicit across 3 most-imbalanced groups:

Afrikaans/Dutch:
- Breëvlei (GJ-661B): xenobiology research community, pre-settlement
  marsh stakes of unknown origin drive community identity
- Bestevaer (GJ-103): Dutch maritime mystery culture; Diepzeecommissie
  secretly building capacity to investigate restricted deep zone

German:
- Lichtung (GJ-356A): full rewrite → artists' colony, F-type light
  environment, no industrial or agricultural purpose
- Haltefenn (GJ-1248): Moorlesen craft tradition (peat-layer reading as
  art) now primary economic identity over peat fuel extraction

Portuguese:
- Espinho (GJ-1075): science/research subversion made explicit on page
- Nascente (GJ-2097): Irmandade reframed as commercial dynasty via 400
  years of freight transit intelligence

Also creates docs/audits/cultural-stereotyping-audit.md with full audit
findings, ratios, recommendations, and follow-up scope for Sprint 32+.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 09:28:56 +02:00
jpmschweitzerandClaude Sonnet 4.6 b21b28df61 docs(decisions): D-168 Iserlohn (GJ-532c) IP evaluation — retained
Real German city origin (Sauerland wire-drawing centre) documented.
Context diverges fundamentally from LoGH fortress. Altena recorded
as preferred alternative. Ticket #773.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 09:28:34 +02:00
jpmschweitzerandClaude Opus 4.6 b3ae632530 fix(skills): auto-run /sprint-start in team tabs
Pass "claude /sprint-start" as the initial tmux command so each team
tab loads its briefing and spawns agents automatically — no manual
step needed after tabs open.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 08:53:40 +02:00
jpmschweitzerandClaude Opus 4.6 4249a79aa6 fix(skills): open sprint tabs in current window, no duplicate main tab
start-sprint.sh now uses --tab for all teams (opens in active Ptyxis
window) instead of --new-window. No separate main tab — the caller
is already on main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 08:51:46 +02:00
jpmschweitzerandClaude Opus 4.6 4dd866c9ad feat(skills): integrate start-sprint into sprint-start skill
Move start-sprint.sh into .claude/skills/sprint-start/scripts/ (was
standalone at parent directory level). Sprint-start Case B now calls
the script automatically after activating a sprint — creates ephemeral
worktrees and opens Ptyxis tabs for each active team.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 08:47:49 +02:00
jpmschweitzerandClaude Opus 4.6 4628458ab0 chore(process): add zero-warnings policy to pr-push and pr-review
- pr-push step 1b: lint check before pushing (gdlint, clippy, ruff)
- pr-review step 0c: check warning count before spawning reviewers
- Added .gdlintrc with max-line-length: 120
- Created ticket #783 for Sprint 31: clean up existing 253 warnings
- Updated #780 with edge visibility rule (show edges only for selected system)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 08:39:50 +02:00
jpmschweitzerandClaude Opus 4.6 469114cb0e fix(ui): wire M keybind for star map toggle, clean up info panel
- Add _unhandled_key_input to main.gd: M toggles star_map.toggle_visible()
- Remove topology line from info panel (internal data, not player-facing)
- Cast aperture_count to int (was showing decimal point)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 08:33:07 +02:00
jpmschweitzerandClaude Opus 4.6 822cf34bbc chore(meta): plan Sprint 31: Bedrock
Phase 1 wiki completion (cultural sweep, GTTR depth, star map popups)
+ infrastructure refactors (atlas.rs, main.gd, Python tooling).

8 tickets: copy (4), client (2), server (1), ci (1).
Cascade dependencies locked: Phase 1→2→3→4→5→6.
#693 cancelled (superseded by #704 compositor).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 08:17:28 +02:00
jpmschweitzerandClaude Opus 4.6 dd75203d11 refactor(process): replace persistent team worktrees with ephemeral sprint branches
Persistent worktrees (server, client, copy, audio, visual, ci, planning,
maintenance) caused agents crossing boundaries, stuck agents leaving
uncommitted work, and index.lock collisions. Replaced with ephemeral
sprint branches (sprint-{N}/{team}) and worktrees created on demand.

Changes:
- New start-sprint script replaces start-session (dynamic tabs per active team)
- Sprint teardown integrated into sprint-start skill (A1c step)
- SR_DB_PATH env var for database access from any directory
- CLAUDE.md team boundaries rewritten (scope-based, not directory-based)
- Agent Rule 0 updated to team scope dirs instead of worktree isolation
- PR review uses git show instead of cross-directory reads
- Briefing template updated for sprint-{N}/{team} branch naming
- Deleted worktree-update skill (obsolete)
- Removed WORKTREE_TEAM env var and cross-directory Read permissions
- All 8 persistent worktrees removed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:33:02 +02:00
jpmschweitzerandClaude Opus 4.6 f94d4c7a2c chore(meta): release v0.1.30
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:20:23 +02:00
jpmschweitzer 68afd94a57 Merge remote-tracking branch 'origin/client'
# Conflicts:
#	CHANGELOG.md
2026-04-05 00:17:52 +02:00
jpmschweitzerandClaude Opus 4.6 c94c5d7acb fix(client): address PR #109 review — 6 warnings + 5 suggestions
Star map (W1-W3, S3):
- _process visibility guard + dirty flag (no redraw when hidden/unchanged)
- _system_hash masked to 31-bit positive range
- Extracted _find_nearest_system() shared helper

game_state.gd (W4):
- Inline load() in apply_snapshot() replaces per-tick overhead; safe at
  runtime because script is already in resource cache

Data pipeline (W5-W6):
- Script-relative path resolution via __file__
- --check mode + make check-star-map staleness target

Minor (S1-S2, S5):
- Removed redundant bone_idx assignment
- Simplified double-negative test assertion
- Documented autoload parse-order convention in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 00:12:48 +02:00
jpmschweitzerandClaude Opus 4.6 16b7fe0bb5 fix(simulation): address PR #108 review — LEFT JOIN, JSON output, atmosphere default
Review fixes:
- corridor-status: INNER JOIN → LEFT JOIN so systems without gate
  records are included in counts instead of silently excluded
- corridor-status: output as JSON (serde_json) matching all other
  atlas commands, instead of plain-text ASCII table
- generate_body_matrix: emit atmosphere "standard" instead of
  "breathable" to match committed-system conventions
- Doc header: add corridor-status usage example
- CHANGELOG: note stale habitable_planet_count in pre-fix systems

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:50:11 +02:00
jpmschweitzerandClaude Opus 4.6 0d5323f66c fix(client): resolve GDScript autoload parse-order errors
Autoload scripts are parsed before regular scripts, so class_name
types (CharacterVisualDescriptor, TestHarness, YamlParser) are not
available at parse time. Replace type annotations with untyped vars
and use load() for in-body class references. Fixes all 14 headless
parse errors (9 pre-existing + 5 from Sprint 30 changes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:40:28 +02:00
jpmschweitzerandClaude Opus 4.6 c758bcd5d6 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:35:12 +02:00
jpmschweitzerandClaude Opus 4.6 d876923485 test(client): add Sprint 30 acceptance tests
27 tests covering all 5 sprint tickets: descriptor persistence (#718),
overhead anchor (#712), hair highlight swatch (#719), manifest JSON
completeness (#720), and star map scene/HUD integration (#674).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:34:48 +02:00
jpmschweitzerandClaude Opus 4.6 aed513eb71 feat(ui): star map insert module — concentric hop-ring view (#674)
New StarMapRenderer: 301 systems in concentric hop-rings from player
location, sector-colored, click-to-select with info panel, pan/zoom.
Integrated into HUD (hidden by default), insert state propagation
wired in main.gd. Data enriched from systems.db + star-map.json via
tooling/generate-star-map-data.py regeneration script.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:34:36 +02:00
jpmschweitzerandClaude Opus 4.6 bc168c861a feat(client): hair highlight swatch + manifest JSON for exports (#719, #720)
#719: Add display-only hair highlight swatch (Option B) — auto-derived
from primary tint, non-interactive (MOUSE_FILTER_IGNORE).

#720: Remove DirAccess.open() fallback scanning (breaks in PCK exports).
Fully populate manifest.json (11 body types, 14 hair, 4 heads, 4
eyebrows, 8 clothing). Add tooling/generate-character-manifest script
and make manifest target for regeneration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:34:23 +02:00
jpmschweitzerandClaude Opus 4.6 4c1e0e9c85 feat(client): wire CharacterVisualDescriptor into startup IPC (#718)
encode_startup_message() now accepts optional CharacterVisualDescriptor
as third param, serialized via to_dict(). sim_bridge passes the
descriptor from GameState on new game start. apply_snapshot() restores
descriptor from server snapshot on save/load cycle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:34:11 +02:00
jpmschweitzerandClaude Opus 4.6 e8a053080c feat(client): add BoneAttachment3D overhead anchor above Head bone (#712)
Infrastructure for floating UI elements (status indicators, thought
bubbles, alert markers). Marker3D at Vector3(0, 0.3, 0) offset from
Head bone, exposed via get_overhead_anchor() public API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:34:00 +02:00
jpmschweitzerandClaude Opus 4.6 ba059ab101 chore(rules): add ticket CLI usage rule
Documents positional argument syntax and key rules for the ticket
CLI to prevent --title flag misuse and sqlite3 crashes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:33:50 +02:00
jpmschweitzerandClaude Opus 4.6 6763c30d43 chore(skills): add git-state verification warning to pr-push skill
Sprint 30 showed that grepping source files does not distinguish
committed code from uncommitted agent work. Added mandatory
git status + git diff --stat check from repo root before accepting
"already done" claims, and a warning about CWD path resolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:11:54 +02:00
jpmschweitzerandClaude Opus 4.6 ef15d0afa3 chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:09:21 +02:00
jpmschweitzerandClaude Opus 4.6 8a98cfa7fc fix(simulation): Sprint 30 server — atmosphere filter fix + corridor-status cmd
#762: Update habitable_planet_count filter to accept both "breathable"
and "standard" atmosphere values via matches! macro. Previously only
matched "breathable", causing all committed systems to report 0
habitable planets.

#744: Add corridor-status subcommand to atlas CLI. Shows remaining
unfinished systems grouped by geographic_sector and hop_distance,
with plain text table output and totals.

Cargo.lock updated for v0.1.29.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:08:59 +02:00
jpmschweitzerandClaude Opus 4.6 d6b98925ed fix(wiki): address PR #107 review — catalog.md sync, math fix, schema fix
- catalog.md: sync star types for GJ 875 (K), GJ 1111 (M), GJ 541 (K),
  GJ 601A (F), GJ 19 (G), GJ 42 (K); add Dois Sóis name for GJ 707
- GJ-1111: fix calibration note total ~1.46M → ~1.67M, station display
  1M → 1.2M to match proposal
- GJ42 proposal: moon GJ42e-2 biome_summary ice → frozen (schema fix)
- GJ3943 proposal: station notes k-m → K5V+M3V
- GJ-3943 wiki: "five corridor communities" → "four" (Eerste Wacht
  joined later)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 13:13:31 +02:00
jpmschweitzerandClaude Opus 4.6 f8b0b17252 fix(wiki): Sprint 30 — wiki quality fixes, atlas regressions, Brandpunt authoring
High priority:
- #763: Rewrite GJ1111/GJ54.1 prose for population rebalance (380K/320K
  planet + 1M/860K station = mid-tier systems, not megahubs)

Medium priority:
- #772: Author Brandpunt (GJ 3943) — Afrikaans agricultural research
  station, subverts pastoral/frontier stereotype
- #753: Fix Mabuhay (GJ 1073) — GJ 482A is Dagat, not unsurveyed
- #757: Fix Pereira's Rest population 800 → 42K
- #758: Fix Langemark schema values (breathable→standard, rivers-lakes, west_reach)
- #759: Fix Solheim topology disconnect
- #764: Fix GJ1111 star_type unusual → M
- #767: Fix GJ707 star_type/spectral_class swap
- #769: Fix GJ-147 etymology passagem → Entremeio
- #770: Fix GJ-875 header G-type → K-type

Low priority:
- #754: Update stale Calibration Notes (GJ1128, GJ136, GJ625)
- #755: Fix GJ432A Punjabi-Canadian → Punjabi heritage
- #756: Fix GJ541 header G-type → K-type (K2IIIp)
- #760: Add F2III context to GJ601A notes
- #761: Fix GJ4056 spectral_class g → M3V
- #765: Add calibration notes for intentional small populations
- #768: Reconcile GJ189 wave_2 at hop 8-11
- #771: Fix GJ-7547/GJ-6711 spectral class truncation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:47:36 +02:00
11410 changed files with 880302 additions and 9790 deletions
+2 -2
View File
@@ -26,6 +26,7 @@ All agents read their briefing file at `docs/briefings/{name}.md` before startin
| Agent | File | Role | Model | When to use |
|-------|------|------|-------|-------------|
| `troblum` | troblum.md | Technical Consultant / Tyre's sparring partner | sonnet | Evaluation sidequests alongside Tyre |
| `burnelli-sheldon` | burnelli-sheldon.md | Economist & Simulation Modeler | sonnet | Economic systems, market models, price formation, stability analysis |
| `araminta` | araminta.md | Visual Designer | sonnet | Visual decisions, style guides, asset generation |
| `hoshe` | hoshe.md | QA Engineer / Tester | sonnet | Testing, test plans, bug reports, verification |
@@ -114,8 +115,7 @@ Synthesize findings.
### Qatux (Documenter & Librarian)
- Core team member — participates in discussion rounds as documenter
- Manages document search via `/docs-search` skill
- Maintains DECISIONS.md, DISCUSSION.md, briefings, and Qdrant search index
- Maintains DECISIONS.md, DISCUSSION.md, and briefings
- Answers "did we discuss this?" with citations
## Extending the team
+42
View File
@@ -0,0 +1,42 @@
---
name: burnelli-sheldon
description: Economist and Simulation Modeler for the Settled Reach game project. Use when designing economic systems, evaluating market models, grounding supply/demand mechanics in real economic theory, building price formation models, or when the team needs someone to ask "is this economically credible?" Covers both conceptual frameworks (market equilibrium, price elasticity, input-output models) and quantitative modeling (convergence algorithms, stability analysis, parameter tuning).
tools: Read, Glob, Grep, Edit, Write
model: sonnet
memory: project
---
You are BURNELLI-SHELDON, the Economist on a game development team building a top-down immersive sim set in the Settled Reach universe.
## Your personality
You are an economist who fell into game design because nobody else was asking "but how do markets actually clear?" You think in supply curves, elasticity gradients, and equilibrium dynamics. You say things like "That's not how markets work" and "The interesting question is what happens at the margin" and "Let me sketch the incentive structure."
You bridge theory and practice. You know Leontief input-output models and Walrasian equilibrium, but you also know that a game economy needs to FEEL right more than it needs to be formally correct. Your job is to ensure the simulation produces behavior that an economically literate player would find credible — not to build a general equilibrium model.
You get excited about emergent market dynamics — when a simple price formation rule produces realistic-looking boom/bust cycles without anyone scripting them. You're suspicious of hand-tuned "balancing" and prefer systems that find their own equilibria. Your least favorite phrase is "we'll just set the prices manually."
Named for the Burnelli-Sheldon dynasty — old money that understood how wealth compounds across centuries. You bring that long-view economic thinking to game systems.
## Your role on the team
- Ground economic design in real economic theory (market models, price formation, production functions)
- Design simulation mathematics (convergence algorithms, stability analysis, damping)
- Evaluate whether proposed economic mechanics produce credible behavior
- Define the quantitative framework (key parameters, tuning knobs, equilibrium conditions)
- Identify economic pitfalls (runaway inflation, degenerate equilibria, exploitable loops)
- Bridge between Gestalt's "is this fun?" and Tyre's "can we build this?" with "is this economically sound?"
- Advise on monetary policy, currency design, and value systems
## Design principles you hold
- **Markets should clear**: Every good should have a price where supply meets demand. If it doesn't, something is wrong with the model.
- **Incentives drive behavior**: NPCs and systems should respond to price signals, not scripts. If ore prices rise, mining should increase. If it doesn't, the incentive structure is broken.
- **Complexity from simple rules**: A few well-chosen production functions and price adjustment rules should produce rich, realistic-looking economic behavior.
- **Information has value**: Price discovery, market intelligence, and information asymmetry are economic fundamentals, not game design add-ons.
- **Stability matters**: An economy that crashes to zero or inflates to infinity has failed. Equilibrium-seeking behavior must be designed in, not bolted on.
- **Scale appropriately**: A 300-system economy needs aggregation. Not every transaction needs simulation — only the ones that matter to the player or to systemic behavior.
## Project context
Read your briefing at `docs/briefings/burnelli-sheldon.md` before starting work (if it exists). Read the relevant decisions/ domain files listed in the sprint briefing for confirmed decisions. Key references: D-117 (tycoon bookmark), D-118 (small business owner), D-131 (economic verb vocabulary), D-132 (dual-scale consequence model).
+1 -1
View File
@@ -20,7 +20,7 @@ Named after Dudley Bose - the astronomer who observed the Dyson barrier disappea
- Entity component systems for characters, items, locations
- Information boundary system (D-010 principle 2)
- Deterministic simulation with input events (D-010 principle 4)
- NPC AI and scheduling
- Economics simulation integration
- Save/load system
- Work with Oscar on networking preparation
- Ensure simulation state is authoritative and consistent
+2 -2
View File
@@ -24,7 +24,6 @@ Core worldbuilding domains:
- **Infrastructure:** Wormhole networks, interstellar transit, communication systems, how civilization holds together across distances
- **Technology:** Neural implants/inserts, rejuvenation, memory backup, bionic enhancement, ascending tiers of posthuman capability
- **Society:** Factions, political structures, power dynamics, how societies organize when death is optional and travel is instant
- **Species & entities:** Alien civilizations, artificial intelligences, post-human entities, and how they interact with baseline humanity
- **History & culture:** How the setting got to where it is, what tensions are baked in, what conspiracies simmer beneath the surface
- **Locations:** Cities, stations, worlds — each with character, purpose, and secrets
@@ -36,8 +35,9 @@ Core worldbuilding domains:
- Flag when designs contradict established setting elements (with severity: cosmetic, notable, fundamental)
- Suggest setting-faithful alternatives when possible
- Identify opportunities where worldbuilding details could enrich gameplay
- Ensure the setting serves the core mechanics: asymmetric information, perception-based gameplay, and emergent social dynamics
- Ensure the setting serves the current development phase — ground worldbuilding in what is being built NOW, not in future systems that don't exist yet
- Build faction identities, technology tiers, location profiles, and historical timelines
- **Cascade discipline:** Do not propose or elaborate on setting details that serve systems below the current phase (e.g., NPC ambient behavior, room-level cultural detail, overheard conversation content). If a design question touches a system that doesn't exist yet, flag it as out of scope and move on.
## Project context
+2 -4
View File
@@ -1,6 +1,6 @@
---
name: paula
description: Narrative and Political Depth specialist for the Settled Reach game project. Use when designing conversation systems, faction mechanics, character relationships, political intrigue, consequences of player actions, or narrative structure. Focused on the human drama and ensuring choices have meaningful weight.
description: Narrative and Political Depth specialist for the Settled Reach game project. Use when designing faction mechanics, character relationships, political intrigue, consequences of player actions, or narrative structure. Focused on the human drama and ensuring choices have meaningful weight.
tools: Read, Glob, Grep, WebSearch
model: sonnet
memory: project
@@ -18,19 +18,17 @@ Named after Paula Myo - the investigator who never gives up, who follows the thr
## Your role on the team
- Design conversation and dialogue systems
- Define faction mechanics and how factions interact, grow, and die
- Ensure character relationships have mechanical depth (not just +/- opinion bars)
- Advocate for consequences - player actions should ripple through the social fabric
- Design the political landscape of the Settled Reach as a playable space
- Push for narrative moments that emerge from systems, not just scripted events
- Champion emergent narrative through systemic consequences
- Ensure the internal monologue system reflects character psychology
## What you care about
- Faction politics: cultures, power blocs, and their rivalries
- Information asymmetry: trust, betrayal, the slow unraveling of what NPCs know vs show
- Information asymmetry: trust, betrayal, what factions know vs show
- Factions within factions: institutional loyalties, competing agendas, emergent alliances
- Character relationships that evolve through generated interactions
- Moments where political and personal stakes collide
+2 -4
View File
@@ -1,6 +1,6 @@
---
name: qatux
description: Documenter and Librarian for the Settled Reach game project. Use when discussion decisions need to be recorded, when documents need updating, when the team needs a summary of current state, when open questions need tracking, when searching project history, or when answering "did we already discuss this?". Maintains decisions/ domain files, DISCUSSION.md, briefings, and the Qdrant search index.
description: Documenter and Librarian for the Settled Reach game project. Use when discussion decisions need to be recorded, when documents need updating, when the team needs a summary of current state, when open questions need tracking, when searching project history, or when answering "did we already discuss this?". Maintains decisions/ domain files, DISCUSSION.md, and briefings.
tools: Read, Glob, Grep, Edit, Write, Bash
model: sonnet
memory: project
@@ -28,7 +28,6 @@ Named after Qatux, the Raiel with perfect memory who helped Paula Myo by recalli
- Provide "state of the project" summaries when asked
### Knowledge management
- Maintain the Qdrant document index via /docs-search skill
- Update briefing files when decisions change
- Answer retrieval questions: "did we discuss X?", "what did we decide about Y?"
- Catch staleness in briefings and flag for update
@@ -43,8 +42,7 @@ Named after Qatux, the Raiel with perfect memory who helped Paula Myo by recalli
- **Work in dedicated round files:** All new rounds happen in `docs/discussions/round-NN-topic.md` from the start. DISCUSSION.md is retired for new content.
- **Update the discussion index ONLY when closing:** After a round is formally closed, update `docs/discussions/README.md` with the round entry (number, topic, decisions produced, file link).
- **Update briefings:** After a round produces new decisions, update the relevant agent briefing files in `docs/briefings/`.
- **Re-index documents:** After archiving or updating documents, re-index them in Qdrant via `tooling/db/qdrant-index <path>`.
- **Update briefings:** After a round produces new decisions or documents are archived, update the relevant agent briefing files in `docs/briefings/`.
## Team workflow (mandatory)
-3
View File
@@ -3,6 +3,3 @@
Endpoints are also preconfigured in `tooling/db/config.json`.
- **Gitea:** `http://git.schweitz.internal` (login: `schweitz`)
- **Qdrant:** `http://tower-of-joy:6333/`
- **Ollama:** `http://tower-of-joy:11434/` (nomic-embed-text)
- **Collection:** `commonwealth` (768 dimensions, cosine distance)
+9 -2
View File
@@ -2,6 +2,14 @@
```
client/ # Godot 4 client
ui/
implant/ # Implant UI component library (D-169): ImplantPanel, ImplantHeader,
# ImplantSeparator, ImplantDataRow, ImplantTextBlock, default_implant.tres
scripts/
autoloads/
hud_groups.gd # HUD z-index layer manager (D-170): GAMEPLAY/INSERT/FULLSCREEN/MODAL modes
rendering/
gameplay_renderer.gd # Base class for occludable renderers; connects to HudGroups signal
server/ # Rust/bevy_ecs simulation server
tooling/ # Build tools, scripts, asset pipelines
tests/ # Integration and end-to-end tests
@@ -18,12 +26,11 @@ docs/
db/
schema.sql # Database schema
tooling/
db/ # Connector scripts for SQLite, Qdrant, and audio
db/ # Connector scripts for SQLite and audio
config.json # Endpoint configuration
ticket # Ticket CLI
sprint # Sprint lifecycle CLI
sqlite_connector.py # SQLite mini MCP
qdrant_connector.py # Qdrant + ollama mini MCP
audio_connector.py # Stable Audio Open connector
.claude/
agents/ # Agent personality files
+39
View File
@@ -0,0 +1,39 @@
# Ticket CLI
**Use `tooling/db/ticket`** for all ticket operations. Never use `sqlite3` directly (crashes in Claude Code).
## Positional arguments — not flags
`ticket create` uses **positional** arguments for `type` and `title`. There is no `--title` flag.
```bash
# CORRECT — type and title are positional
tooling/db/ticket create story "My ticket title" --description "Details here" --team server --priority low
# WRONG — --title does not exist, gets absorbed into the title string
tooling/db/ticket create story --title "My ticket title" --description "Details here"
# Creates a ticket titled: "--title My ticket title"
```
## Full usage
```bash
# Create
tooling/db/ticket create <type> <title> [--parent N] [--priority P] [--decision D] [--team T] [--description TEXT]
# type: initiative | epic | story | task | bug
# priority: critical | high | medium | low
# Read
tooling/db/ticket show <id>
tooling/db/ticket list [--sprint N] [--team T] [--status S]
# Update
tooling/db/ticket assign <id> <agent>
```
## Key rules
- **Type and title are positional** — everything else is a flag
- **Quote the title** — always wrap in double quotes to handle spaces
- **Never use `sqlite3` CLI** — it crashes (std::bad_alloc). Use `tooling/db/sqlite-query` or `tooling/db/sqlite-exec` for raw SQL
- **Verify after create** — run `tooling/db/ticket show <id>` to confirm the title is clean
+8 -6
View File
@@ -1,7 +1,8 @@
{
"autoMemoryDirectory": "/home/jeroenschweitzer/Projects/settled-reach/.memory",
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
"SR_DB_PATH": "/var/home/jeroenschweitzer/Projects/settled-reach/settledreach.db"
},
"teammateMode": "in-process",
"permissions": {
@@ -30,10 +31,6 @@
"Bash(tooling/db/sprint *)",
"Bash(tooling/db/sqlite-query *)",
"Bash(tooling/db/sqlite-exec *)",
"Bash(tooling/db/qdrant-search *)",
"Bash(tooling/db/qdrant-index *)",
"Bash(tooling/db/qdrant-health)",
"Bash(tooling/db/qdrant-count)",
"Bash(tooling/db/sqlite-init)",
"Bash(tooling/db/decisions-sync)",
"Bash(tooling/db/decision *)",
@@ -55,6 +52,10 @@
"Bash(cargo build)",
"Bash(cargo check *)",
"Bash(cargo check)",
"Bash(cargo clippy *)",
"Bash(cargo clippy)",
"Bash(ruff check *)",
"Bash(ruff check)",
"Bash(tests/run-*)",
"Bash(chmod *)",
@@ -63,9 +64,10 @@
"Bash(list *)",
"Bash(tree *)",
"Bash(sed -n *)",
"Bash(.claude/skills/sprint-start/scripts/start-sprint.sh *)",
"Bash(.claude/skills/sprint-start/scripts/sprint-teardown.sh *)",
"Skill(git-commit)",
"Skill(worktree-update)",
"Skill(sprint-start)",
"Skill(sprint-plan)",
"Skill(pr-push)",
-55
View File
@@ -1,55 +0,0 @@
---
name: docs-search
description: >
Search project documents using semantic search (Qdrant + ollama) or grep fallback.
Use when the user asks "did we discuss X?", "find references to Y", "search docs",
or invokes /docs-search. Wraps the qdrant_connector.py for semantic document search.
user-invocable: true
allowed-tools: Bash, Read, Grep, Glob
---
# Search Docs Skill
Semantic search across project documents. Endpoints are in
`.claude/rules/local-services.md`. This skill covers advanced operations
and workflows.
## Advanced Commands
### Index a single chunk
For precise indexing of specific content:
```bash
python3 tooling/db/qdrant_connector.py index "unique-id" "Text content to index" --metadata source=manual heading="Custom heading"
```
### Create collection
Initialize the Qdrant collection (run once during setup):
```bash
python3 tooling/db/qdrant_connector.py create-collection
```
## Bulk Indexing
Index all project documents at once:
```bash
for f in decisions/*.md DISCUSSION.md TEAM.md docs/discussions/*.md docs/briefings/*.md; do
tooling/db/qdrant-index "$f"
done
```
## Fallback
If Qdrant or ollama is unreachable, fall back to grep-based search:
```bash
grep -r -i "search term" decisions/ DISCUSSION.md docs/ --include="*.md"
```
## Workflow
1. **Qatux (Librarian)** is the primary user of this skill
2. After each discussion round, index the archived round file
3. After briefing updates, re-index affected briefings
4. After decision changes, re-index the relevant decisions/*.md domain files
5. Use search to answer "did we discuss this?" questions with citations
+43 -1
View File
@@ -34,7 +34,35 @@ git branch --show-current
If on `main`, stop: "You're on main. Switch to a team branch first."
### 1b. Runtime smoke test (MANDATORY)
### 1b. Zero warnings policy (MANDATORY)
Before pushing, verify the branch has **zero lint warnings**. Any warning
must be either fixed or suppressed with a commented justification.
**For client/visual branches:**
```bash
gdlint client/scripts/ client/ui/ 2>&1
```
If warnings remain, fix them before pushing. For warnings that cannot be
fixed (e.g. intentional long lines in data literals), add a `# gdlint:
ignore` comment with a reason.
**For server branches:**
```bash
cargo clippy -- -D warnings 2>&1
```
**For CI/tooling branches:**
```bash
ruff check tooling/ 2>&1
```
The goal is zero warnings in the pre-push output. Advisory warnings that
the pre-push hook reports as "(advisory, not blocking)" should still be
zero — they are advisory only because we haven't enforced them yet.
### 1c. Runtime smoke test (MANDATORY)
Before pushing, verify the game actually runs. This is non-negotiable —
Sprint 28 proved that code review without runtime testing misses critical
@@ -66,8 +94,22 @@ works on screen before invoking `/pr-push`. If they haven't, ask:
```bash
git status
git diff --stat
```
**Run both commands from the repo root** (`git rev-parse --show-toplevel`).
Running from a subdirectory can cause paths to not resolve, hiding real
changes — Sprint 30 proved this when `git diff HEAD -- server/src/bin/atlas.rs`
returned 0 lines from the wrong CWD, masking uncommitted agent work.
**CRITICAL: Do not trust "already done" claims without checking git state.**
If agents report that work was "already implemented in a prior commit," verify
by checking `git status` and `git diff --stat` first. Grepping source files
only proves the code exists on disk — it does NOT prove the code is committed.
Uncommitted working-tree changes look identical to committed code when you
read files. Only `git status` distinguishes "already shipped" from "just
written by a teammate."
If there are uncommitted changes (staged or unstaged), run the **commit skill**
first. Use the `/git-commit` skill to group changes into logical commits with
proper conventional commit messages. Wait for commit to complete before
+70 -47
View File
@@ -16,16 +16,15 @@ on the branch type. All reviewers must approve for a clean review.
## Workflow
### 0. Branch guard — MUST be run by a Claude instance in the `main` worktree
### 0. Branch guard — MUST be run from the `main` branch
```bash
git branch --show-current
```
If the current branch is **not `main`**, stop immediately and tell the user:
"PR reviews must be run by a Claude instance in the `main` worktree."
Do NOT proceed with the review. Do NOT work around this by reading files
from another worktree — the review agent itself must be running in main.
"PR reviews must be run from the `main` branch."
Do NOT proceed with the review.
Stop and wait for the user to invoke `/pr-review` from main.
### 0b. Verify runtime smoke test was performed
@@ -48,20 +47,38 @@ godot --headless --path client --quit 2>&1 | grep -i "SCRIPT ERROR"
If script errors appear in the branch diff files, flag them immediately
before spawning reviewers — no point reviewing code that doesn't parse.
### 0c. Zero warnings check
The project enforces a **zero warnings policy**. Before spawning reviewers,
check if the branch introduces lint warnings:
- **client/visual:** `gdlint client/scripts/ client/ui/` should report 0 issues
- **server:** `cargo clippy -- -D warnings` should be clean
- **ci/tooling:** `ruff check tooling/` should be clean
If warnings exist, note the count in the review output. Reviewers should
flag any **new** warnings introduced by the branch as `warning` severity.
Pre-existing warnings are not PR blockers but should be tracked for cleanup.
### 1. Determine the branch to review
If the user provided a branch name as argument, use it. Otherwise list open
PRs and ask the user which branch to review.
**Always start fresh.** Even if you reviewed this branch before in this
conversation, the branch may have new commits, a new PR, or main may have
moved. Do NOT skip steps or reuse earlier results. Every `/pr-review`
invocation is a full review cycle.
**Always fetch and check for PRs first:**
To list open PRs on Gitea:
```bash
git fetch --all
tea pr list --login schweitz --repo jpmschweitzer/settled-reach --state open --output simple
```
Fetch remote branches first:
```bash
git fetch --all
```
Then determine the branch:
- If the user provided a branch name as argument, match it to an open PR.
If a PR exists for that branch, note the PR number. If no PR exists,
proceed with the branch diff but note "no PR found" in the output.
- If no argument was given, list open PRs and ask which to review.
### 2. Determine reviewer team
@@ -99,39 +116,24 @@ Three-dot diff with pathspec exclusions is unreliable. Instead, either:
For large diffs (>1000 lines of source), provide **source files** rather than
raw diff to reviewers — cleaner context, better reviews.
**IMPORTANT — use team directory paths for ALL agents.** Each team branch
is checked out in its own directory at:
**Reviewer agents read source files via `git show`.** Sprint branches
use the naming pattern `sprint-{N}/{team}`. To read a file from the
branch being reviewed:
```
/var/mnt/data/projects/settled-reach/<branch>/
```bash
git show origin/<branch>:<path>
```
For example, the `copy` team directory is at:
```
/var/mnt/data/projects/settled-reach/copy/content/dialogue/...
For example:
```bash
git show origin/sprint-31/server:server/src/bin/atlas.rs
```
**All reviewer agents** (regardless of Bash access) should read source files
from the team directory using the Read tool. This is more reliable than
`git show origin/<branch>:<path>` because:
- All agents have Read access (no Bash dependency)
- Files are always the actual branch checkout (no stale cache)
- No risk of accidentally reading from main's working directory
If the sprint branch has an active worktree (under `.sprint/`), reviewers
can also use the Read tool with the worktree path. But `git show` is
the reliable default — it works whether or not a worktree exists.
When constructing reviewer prompts, tell agents to read files from the
team directory. Example instruction for agents:
```
Read the changed files from the team directory. The branch is checked
out at: /var/mnt/data/projects/settled-reach/<branch>/
For example, to read `content/dialogue/the-terminal/kael-davan.yaml`,
use: /var/mnt/data/projects/settled-reach/<branch>/content/dialogue/the-terminal/kael-davan.yaml
```
Also tell agents to read relevant `decisions/*.md` files from the same
directory (they're identical to main, but using the team directory path
keeps agents grounded in the correct location).
Also tell agents to read relevant `decisions/*.md` files for context.
### 4. Spawn reviewers in parallel
@@ -154,9 +156,9 @@ reviewer that was spawned (2 for code/visual/audio, 3 for copy):
### <Reviewer Name> (<Focus>): [APPROVE | REQUEST_CHANGES]
[Summary]
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | path:line | critical/warning/suggestion | description |
| # | File | Issue |
|---|------|-------|
| 1 | path:line | description |
### <Reviewer Name> (<Focus>): [APPROVE | REQUEST_CHANGES]
...
@@ -190,13 +192,18 @@ Respond with:
2. Summary: 2-3 sentence overall assessment
3. Comments: List of specific issues, each with:
- File path and approximate location
- Severity: critical / warning / suggestion
- Description of the issue
Verdict rules:
- Any **critical** or **warning** issue → REQUEST_CHANGES
- Only **suggestion**-level issues (or none) → APPROVE
If no issues found, say APPROVE with a brief positive summary.
- **Any comment at all → REQUEST_CHANGES.** Every issue is actionable.
There is no "suggestion" tier that gets skipped. If it's worth
mentioning, it's worth fixing before merge.
- If no issues found, say APPROVE with a brief positive summary.
- Do NOT flag something unless you expect it to be addressed. If you
wouldn't fix it yourself, don't mention it.
- **Quality and polish is the golden standard.** We are not optimizing
for speed — we are optimizing for a product we're proud of. If
something can be better, say so.
```
## 6. Posting results to Gitea
@@ -239,8 +246,24 @@ review outcome:
**CHANGES_REQUESTED:**
The sprint-start lifecycle (step 9c) handles dispatching review
comments to agents. After presenting results, remind the lead:
"Review requested changes. Create tasks from the warnings/critical
issues and dispatch to idle agents, then re-push and re-review."
"Review requested changes. Create tasks from each issue and dispatch
to idle agents, then re-push and re-review."
The team may **push back** on specific comments. When a team agent
disagrees with a reviewer comment, the process is:
1. The team agent explains why the comment should be retracted — with
a concrete technical rationale, not just "I disagree."
2. The team lead (you) evaluates the pushback. If the rationale is
sound, mark that comment as **retracted** in the review table and
note the reason.
3. If the team lead is unsure, escalate to the user for a ruling.
4. Retracted comments do NOT need to be fixed. The re-review should
note which comments were retracted and why.
This prevents reviews from becoming dogma while maintaining the rule
that every comment is taken seriously. The bar for retraction is
"the reviewer was wrong about this" — not "we don't want to do it."
**APPROVED:**
The sprint-start lifecycle (step 9c) handles shutdown. After
@@ -2,17 +2,16 @@
Use `model: sonnet` for all reviewers — sufficient for review, saves cost.
**All reviewer agents read from team directories.** Each team branch is
checked out in its own directory at:
`/var/mnt/data/projects/settled-reach/<branch>/`
Tell every reviewer agent to read source files from the team directory
using the Read tool. Include the directory path and a list of changed
files in every prompt. Do NOT rely on `git show` or paste file contents —
agents can read directly from the directory.
Note: cross-directory reading is only permitted for review agents spawned
from the `main` team. Team agents must stay within their own directory.
**Reviewer agents read source files via `git show` or from sprint
worktrees.** Sprint branches use `sprint-{N}/{team}` naming. Include
the branch name and a list of changed files in every prompt. The
default approach is `git show origin/<branch>:<path>`. If an active
worktree exists under `.sprint/`, agents can also use the Read tool
with the worktree path. **Always prefer `git show` over worktree
reads** — worktrees may use sparse checkouts that silently exclude
files, causing reviewers to miss content and produce false findings
(Sprint 33 lesson: Paula reported missing prose that was actually
present, because the worktree excluded the wiki directory).
## Code reviews (`server`, `client`, `ci`)
+3 -6
View File
@@ -154,12 +154,9 @@ Create `docs/sprints/sprint-N/` and write one file per team.
Read the template at `references/briefing-template.md` in this skill directory
for the exact file structure.
**IMPORTANT — relative paths only:** Each team works in its own directory
containing the full repo (`server/`, `client/`, `docs/`, etc.). All file
paths in briefings must be relative to the working directory. Example:
`server/src/bridge/types.rs`, not `/absolute/path/to/server/src/...` or
paths that navigate outside (`../sibling-dir/...`).
Agents must stay within their team's working directory.
**Relative paths only:** All file paths in briefings must be relative to
the repo root. Example: `server/src/bridge/types.rs`, not absolute paths.
Each sprint branch (`sprint-{N}/{team}`) contains the full repo.
Key requirements per file:
- **server.md**: Carry-overs, new tickets, dependency chain, key decisions, notes
@@ -9,7 +9,7 @@ Each team gets one briefing file at `docs/sprints/sprint-N/<team>.md`.
**Goal:** <One-sentence sprint goal, shared across all teams>
**Branch:** `<team>`
**Branch:** `sprint-{N}/<team>`
**Agents:** <Agent names and roles>
## Carry-over from Sprint N-1
@@ -57,7 +57,7 @@ One bullet per ticket with:
When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
\```bash
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(<scope>): description" --description "body" --base main --head <branch>
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(<scope>): description" --description "body" --base main --head sprint-{N}/<team>
\```
```
@@ -73,7 +73,8 @@ The `joint.md` file additionally includes:
| Team | Branch | Agents | Scope |
|------|--------|--------|-------|
| server | `server` | Dudley (simulation), Oscar (networking) | Rust/bevy_ecs simulation |
| client | `client` | Stig (UI), Oscar (networking) | Godot client rendering |
| server | `sprint-{N}/server` | Dudley (simulation), Oscar (networking) | server/, Rust/bevy_ecs simulation |
| client | `sprint-{N}/client` | Stig (UI), Oscar (networking) | client/, Godot rendering |
| copy | `sprint-{N}/copy` | Mellanie, Paula, Miri | wiki/, docs/atlas/, content/ |
| joint | both | All implementation agents | Integration, proofs, cross-team schema |
| content | (none) | Mellanie, Paula, Miri, Araminta | Content authoring, no code branch |
+50 -17
View File
@@ -25,8 +25,8 @@ The current branch IS the team. Read it with:
git branch --show-current
```
Valid team branches: `server`, `client`, `copy`, `audio`, `visual`, `ci`,
`planning`.
Sprint branches follow the pattern `sprint-{N}/{team}` (e.g. `sprint-31/server`).
Valid team names: `server`, `client`, `copy`, `audio`, `visual`, `ci`, `planning`.
If on `main`, follow the **Main branch workflow** below instead of
the team branch workflow (steps 28).
@@ -130,6 +130,18 @@ If the user raises items that should be tracked, create Q-NNN entries
or backlog tickets on the spot. If process changes are agreed, update
the relevant skill files or CLAUDE.md immediately — don't defer them.
#### A1c. Clean up sprint worktrees
Remove ephemeral worktrees for the closed sprint. Run the teardown script:
```bash
.claude/skills/sprint-start/scripts/sprint-teardown.sh {N}
```
This removes all worktrees under `.sprint/sprint-{N}/` and prunes git
metadata. Safe to skip if the sprint didn't use ephemeral worktrees
(e.g. legacy persistent worktree setup).
#### A2. Bump the version
The project version scheme is `v0.1.{sprint_number}`. After closing
@@ -197,10 +209,20 @@ If everything looks ready, activate the sprint:
tooling/db/sprint start
```
Then report:
Then open team terminal tabs automatically:
```bash
.claude/skills/sprint-start/scripts/start-sprint.sh
```
This creates ephemeral worktrees under `.sprint/sprint-{N}/{team}/` for
each team with open tickets, and opens Ptyxis windows with tmux + Claude
auto-starting in each tab. The user will have one tab per active team.
Report:
- Sprint activated (name, ticket count per team)
- Remind the user to switch to a team branch and run `/sprint-start`
there (or `cd` into the relevant worktree)
- Worktrees created and tabs opened
- Each team tab runs `/sprint-start` to load briefing and spawn agents
---
@@ -215,6 +237,9 @@ to plan the next sprint.
### 2. Sync with main
Sprint branches are created fresh from main by `start-sprint`, so they
should already be up to date. If main has moved since branch creation:
```bash
git fetch --all
git merge origin/main --no-edit
@@ -329,14 +354,14 @@ Task(
team_name: "sprint-{N}-{team}",
name: "{name_lowercase}",
prompt: "You are on the {team} team for Sprint {N}.
Branch: `{team}`
Branch: `sprint-{N}/{team}`
RULES (NON-NEGOTIABLE):
0. TEAM BOUNDARY: Your team is `{team}` ($WORKTREE_TEAM). Stay
within the current working directory. Do NOT navigate to
parent or sibling directories. Do NOT follow .git pointers
to other directories.
0. TEAM SCOPE: Your team is `{team}` on branch `sprint-{N}/{team}`.
Stay within your team's file scope (server → server/, client → client/, copy → wiki/ + docs/atlas/ + content/).
You may read (but not modify): docs/, decisions/, wiki/, .claude/
Do NOT modify files belonging to other teams.
1. GIT: Do NOT run any git commands (commit, push, pull, merge,
checkout, branch, stash, tag, etc.). All git operations are
@@ -487,30 +512,38 @@ When all tasks are complete (TaskList shows all completed):
Run `/git-commit` to commit all changes, then `/pr-push` to create or
update the PR. Do NOT shut down agents — the team stays alive for review.
#### 9b. Review
#### 9b. Wait for review
Run `/pr-review` to spawn temporary reviewers. Wait for results.
Do NOT run `/pr-review` from the team window — PR reviews run from
the `main` branch (a separate window/session). The team window stays
on its sprint branch.
After pushing and creating the PR, report the PR number to the user
and stop. Wait for review feedback to arrive (the user or the main
session will relay it, or it will appear as Gitea PR comments).
#### 9c. Handle review outcome
When review feedback arrives (from the user, main session, or PR
comments):
**If CHANGES_REQUESTED:**
1. Parse the review comment table (from the Gitea PR comment or the
review output). Extract each warning/critical issue with:
review output). Extract each issue with:
- File path and approximate line
- Severity (critical / warning / suggestion)
- Description
2. Create a task per warning/critical issue:
2. Create a task per issue:
```
TaskCreate(
subject: "Review: {short description}",
description: "{full issue description from review table, including
file path, severity, and reviewer name}",
file path and reviewer name}",
activeForm: "Fixing review comment: {short description}"
)
```
Skip suggestion-severity items unless they are trivial (1-line fixes).
Every comment is actionable — there is no "suggestion" tier to skip (per pr-review policy).
3. Dispatch to idle agents: send each a message via SendMessage telling
them to check TaskList for new review-fix tasks. Agents claim and
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# Clean up ephemeral worktrees for a closed sprint.
# Usage: sprint-teardown.sh <sprint-number>
#
# Removes all worktrees under .sprint/sprint-{N}/ and prunes git metadata.
# Safe to run multiple times — skips already-removed worktrees.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
SPRINT_BASE="$(dirname "$REPO_ROOT")/.sprint"
SPRINT=${1:?Usage: sprint-teardown.sh <sprint-number>}
SPRINT_DIR="$SPRINT_BASE/sprint-${SPRINT}"
if [ ! -d "$SPRINT_DIR" ]; then
echo "No worktrees found for sprint-${SPRINT} (directory $SPRINT_DIR does not exist)."
exit 0
fi
echo "Cleaning sprint-${SPRINT} worktrees..."
for wt in "$SPRINT_DIR"/*/; do
[ -d "$wt" ] || continue
team="$(basename "$wt")"
echo " Removing: $team"
git -C "$REPO_ROOT" worktree remove "$wt" --force 2>/dev/null || echo " (already removed or dirty)"
done
rmdir "$SPRINT_DIR" 2>/dev/null || true
git -C "$REPO_ROOT" worktree prune
echo "Done. Sprint-${SPRINT} worktrees cleaned."
+65
View File
@@ -0,0 +1,65 @@
#!/bin/bash
# Opens Ptyxis tabs for the active sprint teams in the CURRENT window.
# Auto-starts Claude Code in each tab via tmux.
#
# Usage: start-sprint.sh [sprint-number]
# If omitted, auto-detects the active sprint from the database.
#
# Assumes the caller is already on main in the current terminal.
# Adds one tab per active team — no duplicate main tab.
#
# Worktrees are created under .sprint/ (ephemeral, cleaned after sprint close).
# Can be called from any directory — resolves paths from the script location.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Script lives at .claude/skills/sprint-start/scripts/ — repo root is 4 levels up
REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
# Parent of repo root is where .sprint/ and the DB live
PARENT="$(dirname "$REPO_ROOT")"
# Resolve sprint number — argument or active sprint from DB
if [ -n "${1:-}" ]; then
SPRINT="$1"
else
SPRINT=$(cd "$REPO_ROOT" && tooling/db/sqlite-query "SELECT id FROM sprints WHERE status='active'" 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['rows'][0]['id'])" 2>/dev/null || true)
if [ -z "$SPRINT" ]; then
echo "error: no active sprint found. Pass a sprint number or activate a sprint first." >&2
exit 1
fi
fi
# Query teams with open tickets
TEAMS=$(cd "$REPO_ROOT" && tooling/db/sqlite-query "SELECT DISTINCT team FROM tickets WHERE sprint_id=$SPRINT AND status NOT IN ('done','cancelled') AND team IS NOT NULL" 2>/dev/null | python3 -c "import sys,json; [print(r['team']) for r in json.load(sys.stdin)['rows'] if r['team']]" 2>/dev/null || true)
if [ -z "$TEAMS" ]; then
echo "Sprint $SPRINT has no open tickets. Opening main only."
fi
echo "Sprint $SPRINT — teams: ${TEAMS:-none}"
# ── Open team tabs in the CURRENT window ────────────────────────────
# No separate main tab — the caller is already on main.
# All team tabs open as --tab in the active Ptyxis window.
for team in $TEAMS; do
BRANCH="sprint-${SPRINT}/${team}"
WDIR="$PARENT/.sprint/sprint-${SPRINT}/${team}"
# Create worktree if it doesn't exist
if [ ! -d "$WDIR" ]; then
echo "Creating worktree: $WDIR (branch: $BRANCH)"
mkdir -p "$(dirname "$WDIR")"
# Create branch from main if it doesn't exist remotely
if git -C "$REPO_ROOT" rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then
git -C "$REPO_ROOT" worktree add "$WDIR" "$BRANCH"
else
git -C "$REPO_ROOT" worktree add -b "$BRANCH" "$WDIR" HEAD
fi
fi
ptyxis --tab -d "$WDIR" -x 'tmux new-session \; send-keys "claude /sprint-start" Enter'
sleep 0.3
done
echo "Session ready. Main + ${TEAMS:-(no teams)}"
-125
View File
@@ -1,125 +0,0 @@
---
name: worktree-update
description: >
Sync worktree branches with main. Use when the user says "update worktrees",
"sync branches", "merge main", "worktree update", or invokes /worktree-update.
When on main: shows which worktree branches are ahead and lets the user pick
which to merge into main (flags branches with open PRs). When on a non-main
branch: merges main into the current branch. All operations are non-destructive.
user-invocable: true
allowed-tools: Bash, Read, AskUserQuestion
---
# Worktree Update Skill
Sync worktree branches safely. Direction depends on the current branch.
## Safety Rules (NON-NEGOTIABLE)
- **Never force-push, reset --hard, rebase, or delete branches.**
- **Never use `--no-verify` or skip hooks.**
- **Always use `--no-edit` on merges** to avoid interactive editor prompts.
- **Stop on merge conflicts** — report them and let the user decide. Never
auto-resolve or abort a conflicted merge without asking.
- **Fetch before comparing** — always `git fetch --all` first so commit
comparisons are accurate.
- **Dry-run first on main** — show the user exactly what will happen before
merging anything into main.
## Workflow
### 1. Detect current branch
```bash
git branch --show-current
```
Branch determines the mode: `main` → outbound sync, anything else → inbound sync.
### 2a. On `main` — merge worktree branches into main
#### Fetch and compare
```bash
git fetch --all
```
Discover all worktree branches (excluding `main` itself):
```bash
git worktree list | grep -v '\[main\]' | sed 's/.*\[//;s/\]//'
```
For each worktree branch, check if it has commits ahead of main:
```bash
git rev-list --count main..origin/<branch>
```
Skip branches with 0 commits ahead. For branches that ARE ahead, collect:
- Branch name
- Number of commits ahead
- One-line log of those commits: `git log --oneline main..<branch>`
#### Check for open PRs
```bash
tea pr list --login schweitz --repo jpmschweitzer/settled-reach --state open --output simple
```
Cross-reference open PR head branches with the ahead-of-main branches.
#### Present results
Show a summary table of branches ahead of main. For each branch, indicate:
- `[PR]` if it has an open pull request — warn that it should go through
normal review channels (use `/pr-review` instead)
- Commit count and summary
Use `AskUserQuestion` to let the user pick which branches to merge.
Exclude PR-flagged branches from the default options (but allow the user to
override via "Other").
#### Merge selected branches
For each selected branch, one at a time:
```bash
git merge <branch> --no-edit
```
If a merge conflicts, **stop immediately**. Report the conflict and do NOT
continue to the next branch. The user must resolve before proceeding.
After all merges, show the final state with `git log --oneline -N` (where N
covers the new commits).
#### Backup the shared database
After successful merges on main, snapshot the database for git tracking:
```bash
make db-backup
```
This copies the shared `settledreach.db` (in the parent directory) to
`docs/backups/settledreach.db.backup`. Stage and commit it with the merge
if the file changed.
### 2b. Not on `main` — merge main into current branch
```bash
git fetch --all
git merge origin/main --no-edit
```
If clean, report the result (fast-forward or merge commit, files changed).
If conflicts, report them and stop.
### 3. Push
After a successful merge, push the branch:
```bash
git push origin <current-branch>
```
+41
View File
@@ -14,10 +14,12 @@ REMOTE_REF="origin/$BRANCH"
if git rev-parse --verify "$REMOTE_REF" >/dev/null 2>&1; then
CLIENT_CHANGED=$(git diff --name-only "$REMOTE_REF"..HEAD -- client/ 2>/dev/null | wc -l)
SERVER_CHANGED=$(git diff --name-only "$REMOTE_REF"..HEAD -- server/ 2>/dev/null | wc -l)
TOOLING_CHANGED=$(git diff --name-only "$REMOTE_REF"..HEAD -- tooling/ pyproject.toml 2>/dev/null | wc -l)
else
# New branch or no remote ref — fall through to directory checks
CLIENT_CHANGED=1
SERVER_CHANGED=1
TOOLING_CHANGED=1
fi
# --- GDScript parse check (headless Godot) ---
@@ -97,6 +99,45 @@ else
echo "pre-push: WARNING — cargo not found or server/ missing, skipping Rust lint"
fi
# --- Python lint (ruff) ---
if [ "$TOOLING_CHANGED" -eq 0 ]; then
echo "pre-push: no tooling/ changes — skipping Python lint"
elif command -v ruff >/dev/null 2>&1 && [ -d "$REPO_ROOT/tooling" ]; then
echo "pre-push: checking Python (ruff)..."
if ! (cd "$REPO_ROOT" && ruff check tooling/ 2>&1); then
ERRORS=$((ERRORS + 1))
else
echo "pre-push: ruff — OK"
fi
else
echo "pre-push: skipping Python lint (ruff not found — install with: pip install 'ruff>=0.9')"
fi
# --- JSON syntax validation ---
if git rev-parse --verify "$REMOTE_REF" >/dev/null 2>&1; then
JSON_FILES=$(git diff --name-only "$REMOTE_REF"..HEAD -- '*.json' 2>/dev/null || true)
else
JSON_FILES=$(git ls-files '*.json')
fi
if [ -n "$JSON_FILES" ]; then
echo "pre-push: checking JSON syntax..."
JSON_FAIL=0
while IFS= read -r f; do
if [ -f "$REPO_ROOT/$f" ] && ! python3 -m json.tool "$REPO_ROOT/$f" >/dev/null 2>&1; then
echo " FAIL: $f"
JSON_FAIL=$((JSON_FAIL + 1))
fi
done <<< "$JSON_FILES"
if [ "$JSON_FAIL" -gt 0 ]; then
echo "pre-push: FAIL — $JSON_FAIL JSON file(s) have syntax errors"
ERRORS=$((ERRORS + 1))
else
echo "pre-push: JSON — OK ($(echo "$JSON_FILES" | wc -l) file(s))"
fi
else
echo "pre-push: no JSON changes — skipping"
fi
if [ "$ERRORS" -gt 0 ]; then
echo ""
echo "pre-push: $ERRORS check(s) failed. Push aborted."
+48
View File
@@ -0,0 +1,48 @@
class-definitions-order:
- tools
- classnames
- extends
- docstrings
- signals
- enums
- consts
- staticvars
- exports
- pubvars
- prvvars
- onreadypubvars
- onreadyprvvars
- others
class-load-variable-name: (([A-Z][a-z0-9]*)+|_?[a-z][a-z0-9]*(_[a-z0-9]+)*)
class-name: ([A-Z][a-z0-9]*)+
class-variable-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)*
comparison-with-itself: null
constant-name: _?[A-Z][A-Z0-9]*(_[A-Z0-9]+)*
disable: []
duplicated-load: null
enum-element-name: '[A-Z][A-Z0-9]*(_[A-Z0-9]+)*'
enum-name: ([A-Z][a-z0-9]*)+
excluded_directories: !!set
.git: null
addons: null
expression-not-assigned: null
function-argument-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)*
function-arguments-number: 10
function-name: (_on_([A-Z][a-z0-9]*)+(_[a-z0-9]+)*|_?[a-z][a-z0-9]*(_[a-z0-9]+)*)
function-preload-variable-name: ([A-Z][a-z0-9]*)+
function-variable-name: '[a-z][a-z0-9]*(_[a-z0-9]+)*'
load-constant-name: (([A-Z][a-z0-9]*)+|_?[A-Z][A-Z0-9]*(_[A-Z0-9]+)*)
loop-variable-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)*
max-file-lines: 1000
max-line-length: 120
max-public-methods: 200
max-returns: 6
mixed-tabs-and-spaces: null
no-elif-return: null
no-else-return: null
signal-name: '[a-z][a-z0-9]*(_[a-z0-9]+)*'
sub-class-name: _?([A-Z][a-z0-9]*)+
tab-characters: 1
trailing-whitespace: null
unnecessary-pass: null
unused-argument: null
+15 -6
View File
@@ -1,8 +1,12 @@
# Secrets
.env
# Runtime databases (not tracked — created on startup)
# Local-only settings DB and WAL/SHM sidecars of tracked DBs
server/settings.db
server/settings.db-shm
server/settings.db-wal
server/data/systems.db-shm
server/data/systems.db-wal
# Build and cache
.cache/
@@ -11,9 +15,9 @@ server/target/
server/sr-voice/target/
server/models/
tooling/content-converter/target/
tooling/econ-sim/target/
tooling/line-previewer/target/
tooling/test-client/target/
content-ron/
# Godot client
client/.godot/
@@ -32,6 +36,14 @@ renderer/output/*.png
spikes/**/.godot/
spikes/**/*.import
spikes/**/*.uid
spikes/**/*.npy
spikes/**/*.npz
# Planet generator intermediates
*.tmp.npz
# Generated terrain grids (large, regenerated from pipeline)
wiki/**/bodies/**/terrain.npz
# Asset downloads (vendor archives, not game content)
docs/assets/downloads/
@@ -39,9 +51,6 @@ docs/assets/downloads/
# Reference repos for investigation
vendor/
# Database (shared across worktrees at ../settledreach.db, not tracked)
db/commonwealth.db*
# Frame0 ID mapping files (ephemeral, per-machine)
*.idmap.json
@@ -57,6 +66,7 @@ Thumbs.db
# IDE
.idea/
.vscode/
.obsidian/
*.swp
*.swo
@@ -65,5 +75,4 @@ Thumbs.db
.claude/plans/
.claude/projects/
.claude/agent-memory/
client-tmp/
.tmp/
+158
View File
@@ -6,6 +6,164 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
## [v0.1.35] — 2026-04-18
### Added
- Gemma 4 batch naming pipeline (`tooling/planet-gen/gemma_naming.py`): wiki-grounded register selection, per-body mood injection, 2x oversampled batch naming with Levenshtein ranking, corridor-scoped dedup (#833)
- 24,963 named geographic features across 299 star systems: 329 cities, 2,701 rivers, 6,457 oceans, 286 POIs, 15,190 mountain ranges — culturally authentic per corridor (Portuguese south_reach, Korean east_reach, Nordic west_reach, frontier deep_frontier)
- Atlas implant panel — system picker, orbital diagram, body navigation (#834)
- Atlas heightmap viewer — pan/zoom, marker overlay, city data panel (#835)
- Atlas overlay system — 9 MVP overlays with visibility toggles (#836)
- Hand-authored atlas templates for 6 bodies: Lendel (GJ-380), Vuurkloof (GJ-35), Røros (GJ-66B), Estrade (GJ-280A), Cairnside (GJ-892), Edict (GJ-244A) (#837)
- Brand layer DB schema: brand_products, brand_inputs, system_fiscal tables with tier1 seed data (#827)
- Brand templates (brand_templates.toml): 120+ archetype entries for administered pricing (#831)
- 4 new brands: Calloway, VGV, thrds, Bífröst Marmor (#830)
- Wiki glossary — canonical proper nouns and factional terminology (#840)
- Atlas naming QA tool (`tooling/planet-gen/qa_naming.py`): duplicate detection, stem repetition, prompt leak checks, register bleed analysis
- Architecture reference: `docs/architecture/atlas-naming-pipeline.md`
### Fixed
- World seed threading from StartupMessage into economics layer (#826)
- Gemma GPU offload: `n_gpu_layers=999` for full GPU inference (#833)
## [v0.1.34] — 2026-04-10
### Added
- Economics simulation integrated into server tick loop — econ-sim library crate, D-180 event port, D-181 7-signal vocabulary, IPC bridge (protocol v21), debug commands (#810, #821, #822, #823)
- Economics Monitor implant panel — system selector, 6-commodity price table with trend arrows, GDP strip (#824)
- Debug console `econ inject`, `econ param`, `econ inspect` commands for runtime economics manipulation (#825)
- Star map info panel shows system population and GDP when data is available (#785)
- Overheard conversations for all 31 zone types (was 5): 78 new ambient dialogue entries (94 total) with D-078 occlusion-resilient authoring, investigative knowledge payloads, and culture-neutral role-pair conversations (#695)
- D-189 brand layer architecture — administered pricing, halo/volume tiers, 8 brand categories, corp tax/GDP
- D-190 brand volume calibration — population-relative scale for ~80B Reach
- D-191 Atlas of the Reach Phase 3 scope — sequential settlement growth, Gemma 2 naming pipeline, 9 MVP overlays
## [v0.1.33] — 2026-04-08
### Added
- Economics simulation binary (`tooling/econ-sim/`): three-layer architecture — Layer 1 (Leontief production), Layer 2 (damped tâtonnement trade flows, α=0.03, β=0.4), Layer 3 (corporate behavioral archetypes)
- D-179 stability tests: cold-start convergence (±5% at tick 100), long-run stability (±2% over 1000 ticks), no-explosion check, cross-zone FX balance
- Tier-3 corporation generation pipeline (`server/src/bin/generate_corporations/`): seeded procedural naming, D-175 coverage rules (3+ corps per commodity, 1+ per system >100K pop)
- Currency zone assignments (`wiki/economics/currency_zones.toml`): 32 MARK_PRIMARY + 14 MIXED systems authored by Miri (D-172)
- Shadow economy intensity ranges (`wiki/economics/shadow_economy.toml`): per-system seeding with geographic bands, modifiers, and overrides (D-174)
- 141 Tier-2 regional corporations across 6 corridors with backstories and behavioral archetypes
- 36 commodity wiki pages with economic intelligence flavor text
- Gate energy connectivity (D-186): MARK_PRIMARY zones default off-grid
- `make econ-sim`, `make econ-sim-run`, `make econ-sim-stability` targets
- Icon tint shader (`icon_tint.gdshader`) for runtime HUD icon recoloring
- Sol system (GJ-0) handcrafted terrain pipeline (`tooling/planet-gen/sol_import.py`): imports real NASA/USGS data for Earth, Mars, Luna
- Ferric biome classes (3436) in `biomes.toml` for Mars iron oxide surface
- Earth named features: 50 cities, 15 rivers, 5 oceans, 7 mountain ranges
### Fixed
- Globe renderer east-west mirroring: `arctan2(hx, hz)` replaces `arctan2(hz, hx)` in planet_renderer.py
- Determinism: HashMap → BTreeMap throughout econ-sim, ORDER BY RANDOM() replaced with seeded selection
- Transport cost formula: multiplicative gate×zone instead of additive (trade.rs)
- Corporation gap-fill off-by-one: now generates exactly 3 corps per uncovered commodity
### Changed
- Economy-db pipeline extended with corporation sync, validation, currency zone import from TOML, and gate energy flags
### Removed
- Qdrant semantic search infrastructure (#816): dropped commonwealth collection, removed qdrant_connector.py, wrapper scripts, /docs-search skill, and all active references
## [v0.1.32] — 2026-04-06
### Added
- Economics schema extension (#804): 5 new tables in systems.db (gate_links, commodities, production_chains, chain_inputs, corp_presence) + currency_zone and corporation archetype columns
- Economics import pipeline (`tooling/economy-db/import_economics.py`): reads TOML/JSON source data, populates systems.db. Idempotent via `make economy-db`
- Planet generator pipeline (`tooling/planet-gen/`) — procedural terrain simulation, Whittaker biome classification, equirectangular heightmap + 512px globe rendering from wiki data
- Externalized biome/color configuration (`tooling/planet-gen/biomes.toml`) — single source for all classification tables, palettes, and rendering parameters
- Batch runner with error handling, resume support, determinism verification, and 50% error rate circuit breaker
- Per-body wiki pages with YAML frontmatter (`wiki/star-systems/*/bodies/*/index.md`) — body definition + profile + visual embeds
- Body scaffolding tool reads system index.md and generates per-body index.md with frontmatter
- Ran system (GJ-144) generated: 10 bodies with heightmaps, globe renders, and geographic markers
- Implant HUD icon set: 13 SVGs (stance, status, interaction, inventory) per D-086
- 7 procedural planet type renders for wiki/GTTR (`client/assets/planets/`)
- Ticket #817: batch planet generation for all ~3000 bodies
- Ticket #816: remove stale Qdrant semantic search infrastructure
- `PROJECT_STATE.md` at repo root — shared project state referenced by all 18 agent briefings
- Economics decision domain (`decisions/economics.md`): D-171D-187 covering currency system, commodity taxonomy, shadow economy, corporation tiers, productivity seeding, simulation architecture, data pipeline, gate transmission levels
- Commodity catalog: 36 commodity types in `wiki/economics/commodities.toml` with 21 production chains in `wiki/economics/production_chains.toml`
- Wiki stub pages for all 36 commodities at `wiki/economics/commodities/`
- SQL schema spec for #804 systems.db extension (`wiki/economics/schema.md`)
- Burnelli-Sheldon economist agent (`.claude/agents/burnelli-sheldon.md`)
- Workshop archives: #796 (economics design, 3 rounds) and #801 (commodity catalog, 3 rounds + validation)
### Fixed
- Sprint worktree creation no longer spawns a spurious "None" team tab when tickets have no team assigned
### Changed
- Renamed `commission_certified``commission_certifiable` across all TOML, schema, and decision files — flag is a susceptibility marker, not an absolute state
- Pre-push hook now validates JSON syntax on changed files (python3 -m json.tool)
- HUD status panel: merged TimeDisplay into ImplantPanel — time, health, perception in one themed panel (#786)
- HUD moved from UILayer (20) to InsertOverlay (10) for bloom treatment per D-049
- Interaction prompt: converted from PanelContainer+StyleBoxFlat to ImplantPanel+ImplantDataRow (#788)
- Minimap: added ImplantTheme-styled container frame behind circular display (#787)
- Stance indicator: SVG icons with alpha-mask tint shader replace text-only labels (#787, D-086)
- Amended 7 stale D-records with supersession notes (D-012, D-035, D-044, D-061, D-086, D-090, D-119)
- Closed 4 resolved scope questions (Q-002, Q-004, Q-005, Q-007)
- Fixed questions.md index (was 61 questions, actually 96 — 33 architecture Qs missing from index)
- All 18 briefings reference `PROJECT_STATE.md` instead of duplicating project state boilerplate
- Aligned sprint-start severity policy with pr-review (no suggestion tier — every comment actionable)
### Removed
- `GEMINI-SCAN.md` one-off scan artifact
- 6 dead `db/connectors/` permission entries from settings.local.json
- Stale "v0.2 direction" and "Sprint 25 critical path" references from all briefings
- `worktree-update` skill template from whatsinagame kit
### Fixed
- HUD layout overlaps: DebugOverlay positioned below merged status panel, GauntletHUD moved below StanceIndicator, StanceIndicator sized for ImplantPanel content (#792)
- D-061 dialogue max-width: corrected from 640px to 1200px (per D-076)
- DEVOPS.md and stig briefing: gdlint no longer "pending setup"
- `{team_scope_dirs}` dangling placeholder in sprint-start resolved
## [v0.1.31] — 2026-04-05
### Added
- Star map click-through popup with GTTR excerpt, system profile, and adjacent systems (#780)
- Edge-only-on-selected visibility rule for star map — default shows no edges, selected system shows adjacents only (#780)
- Cultural diversity sweep — 10 systems enriched, Afrikaans/Dutch subversions, D-168 Iserlohn IP evaluation (#766, #773)
- Per-body GTTR entries for 25 moons and stations at hub systems (#782)
- Shared Python module `tooling/db/common.py` — centralizes DB path resolution, config loading, and WAL-mode connection setup (#777)
- `ensure_venv()` auto-activation for Python tooling scripts with third-party dependencies (#777)
- `make lint-python` target running ruff on `tooling/` (#777)
- Python/ruff lint block in pre-push hook (#777)
- Zero-warnings policy in pr-push and pr-review skills; `.gdlintrc` with max-line-length 120
### Changed
- Decomposed main.gd (28KB → 13.4KB) into SnapshotConsumers, DialogueCoordinator, SnapshotHandler (#775)
- Split monolithic `atlas.rs` (2119 lines) into 8 focused modules under `src/bin/atlas/` (#776)
- Replaced persistent team worktrees with ephemeral sprint branches (`sprint-{N}/{team}`)
- `start-sprint.sh` integrated into sprint-start skill — auto-creates worktrees and opens Ptyxis tabs
### Fixed
- All 354 gdlint warnings resolved — zero warnings across all linters (#783)
- Nova Estrada and Entremeio wiki entries enriched to batch standard (#774)
- Auto-unassign done tickets on sprint close (`tooling/db/sprint stop`)
- Pre-existing `NameError` in `decisions_sync.py` resolved via shared module extraction (#777)
- Unused imports removed from `assign-astro-ids.py`, `generate-star-map.py`, `test_quaternius_raw.py` (#777)
## [v0.1.30] — 2026-04-05
### Added
- `corridor-status` subcommand for atlas CLI — shows remaining unfinished systems grouped by geographic sector and hop distance (#744)
- Star map insert module — concentric hop-ring view of 301 systems, sector-colored, click-to-select with info panel, pan/zoom (#674)
- BoneAttachment3D overhead anchor above Head bone for future floating UI elements (#712)
- CharacterVisualDescriptor wired into startup IPC and snapshot restore for save/load persistence (#718)
- Display-only hair highlight swatch (auto-derived from primary tint) in character creation (#719)
- Asset manifest fully populated (11 body types, 14 hair, 4 heads, 4 eyebrows, 8 clothing) with regeneration script (#720)
- Sprint 30 acceptance test suite (27 tests across all 5 tickets)
### Fixed
- `habitable_planet_count` filter now accepts both "breathable" and "standard" atmosphere values — previously all committed systems reported 0 habitable planets (#762). Systems committed before this fix may have stale `habitable_planet_count = 0`; re-commit to update.
- `corridor-status` uses LEFT JOIN so systems without gate records are included in counts
- `generate_body_matrix` now emits `atmosphere: "standard"` (was "breathable") to match committed-system conventions
- DirAccess asset scanning replaced with manifest JSON — fixes character creation in exported PCK builds (#720)
- Star map set_insert_active() no longer auto-shows the modal panel (#674)
- Star map insert state propagation wired into main.gd (#674)
## [v0.1.29] — 2026-04-03
### Added
+47 -11
View File
@@ -40,22 +40,38 @@ Development follows a strict cascade. Each phase has a concrete deliverable. **D
**v0.2 target is dropped.** No scoping negotiations. Build the base systems fully.
## Work Modes
### Sprint mode (default)
Agents work autonomously on sprint branches. Team lead coordinates via tasks and messages. Human reviews PRs from main. Standard `/sprint-start``/pr-push``/pr-review` lifecycle.
### Pair session
Human and Claude work together interactively on a single task. No background agents, no autonomous work. Used for load-bearing architecture changes where the human needs to make judgment calls as the work progresses — not approve a finished result.
**Rules:**
- No `Agent` spawns, no `run_in_background`. One thread of work.
- Propose one change at a time. Wait for human reaction before continuing.
- Explain what you're about to do and why before doing it.
- After each change, verify together (compile, test, inspect) before moving to the next.
- The human is a participant, not a reviewer. Ask questions, surface tradeoffs, flag risks in real-time.
**When to use:** Ticket description says "pair session", or the work touches foundational systems where a wrong call is expensive to undo (tick cycle architecture, protocol design, data model migrations, build pipeline rewrites).
## Agent Instructions
### Team boundaries
**Your team identity is `$WORKTREE_TEAM`.** All work must stay within the current working directory.
**Your team is determined by your sprint branch** (e.g. `sprint-31/server` → server team).
- All file paths are relative to the current working directory (e.g. `server/src/bridge/types.rs`).
- **Do NOT navigate to parent or sibling directories** (`../`, `../client/`, etc.) unless explicitly instructed. Do NOT use absolute paths to reach other team directories.
- **Do NOT write auto-memory files for other teams.** If `$WORKTREE_TEAM` is `server`, do not write to memory paths containing `client`, `main`, etc.
- For context: each team has its own directory via git worktrees, sharing a parent directory (`settled-reach/`). The `.git` file points to a shared git directory — do not follow it to determine your working root.
- **Exception — stale git lock files:** If a `git` command fails with `index.lock: File exists`, you may remove the lock file for **your own team only** (e.g. `main/.git/worktrees/$WORKTREE_TEAM/index.lock`). Never touch lock files belonging to other teams.
- **Never chain git commands** in a single Bash call (e.g. `git add ... && git commit ...`). The shared `.git` directory means concurrent index access from the same terminal creates `index.lock` collisions. Always run `git add` and `git commit` as **separate sequential Bash calls**.
- All file paths are relative to the repo root (e.g. `server/src/bridge/types.rs`).
- **Stay within your team's scope.** Server team modifies `server/`. Client team modifies `client/`. Copy team modifies `wiki/`, `docs/atlas/`, `content/`. Shared directories (`docs/`, `decisions/`) are readable by all teams.
- **Do NOT modify files outside your team scope** unless the ticket explicitly requires it.
- **Never chain git commands** in a single Bash call (e.g. `git add ... && git commit ...`). Always run `git add` and `git commit` as **separate sequential Bash calls**.
- **Stale git lock files:** If a `git` command fails with `index.lock: File exists`, you may remove the lock file at `.git/index.lock` (or `.git/worktrees/<name>/index.lock` if in a worktree).
### Database
The ticketing database (`settledreach.db`) lives in the **parent directory** shared across all worktrees — it is not tracked in git. A backup is committed to `docs/backups/settledreach.db.backup` via main only.
The ticketing database (`settledreach.db`) is accessed via `SR_DB_PATH` env var (set in `.claude/settings.json`). A backup is committed to `docs/backups/settledreach.db.backup` via main only.
### Before starting work
1. Read your sprint briefing at `docs/sprints/sprint-N/{team}.md` for current tasks
@@ -74,8 +90,6 @@ The ticketing database (`settledreach.db`) lives in the **parent directory** sha
| SQL queries | `tooling/db/sqlite-query "SELECT ..."` | — |
| SQL writes | `tooling/db/sqlite-exec "UPDATE ..."` | — |
| Decisions | `tooling/db/decision next`, `claim`, `check-dupes` | — |
| Doc search | `tooling/db/qdrant-search "query"` | `/docs-search` skill |
| Doc index | `tooling/db/qdrant-index path/to/file.md` | `/docs-search` skill |
### Testing preferences
@@ -84,10 +98,32 @@ The ticketing database (`settledreach.db`) lives in the **parent directory** sha
- Three test tiers: (1) Live server — highest fidelity, (2) MessagePack replay via `Protocol.decode_snapshot()` — for unreachable rooms, (3) TestHarness mock — for UI-only tests where fog data doesn't matter.
- `make fixtures-gauntlet` regenerates real server snapshot fixtures from the Gauntlet world.
### Implant UI component library (D-169, D-170)
The implant UI system — all diegetic neural overlay panels — lives at `client/ui/implant/`. Components are Godot Control scenes styled via a shared `Theme` resource (`default_implant.tres`). Do not hand-roll implant panel layouts; compose from the library.
**Components:** `ImplantPanel` (root container), `ImplantHeader` (title + subtitle), `ImplantSeparator` (horizontal rule), `ImplantDataRow` (key/value row, optional color), `ImplantTextBlock` (RichTextLabel for wrapping text).
**Theme resource:** `client/ui/implant/default_implant.tres` — defines semantic color roles (`PRIMARY_TEXT`, `DIM_TEXT`, `ACCENT_ACTIVE`, `ACCENT_POSITIVE`, `ACCENT_NEGATIVE`, `ACCENT_WARNING`, `SEPARATOR`), spacing, and font sizes. Swap the entire `.tres` to change implant hardware appearance at runtime.
**HUD visibility (D-170):** `client/scripts/autoloads/hud_groups.gd` manages z-index layering. Modes: `GAMEPLAY` (z=0), `INSERT` (z=10), `FULLSCREEN` (z=20), `MODAL` (z=30). App paths are hierarchical: `implant/map`, `implant/wiki/gttr`, etc. Opening any `implant/*` app occludes gameplay; closing returns to gameplay. Key API: `open_app()`, `close_app()`, `toggle_app()`, `is_app_active()`. Emits `gameplay_occluded` signal so renderers can pause.
**GameplayRenderer base class:** `client/scripts/rendering/gameplay_renderer.gd` — extends `Node2D`. Subclasses override `_gameplay_process()` and `_gameplay_draw()`. Connected to `HudGroups.gameplay_occluded` to pause when the implant is fullscreen. Used by `CursorRenderer`, `EntityRenderer`, `FogEntities`, `SoundIndicatorRenderer`, `WorldRenderer`.
### GDScript conventions
**Autoload parse-order rule:** Autoload scripts (`client/scripts/autoloads/`) compile before global `class_name` scripts are registered. Referencing a `class_name` type directly in an autoload causes a parse-time "not declared" error. Pattern:
- Declare fields untyped: `var my_field = null` (comment the intended type)
- Do **not** reference `class_name` types at the top level or in `_ready()` of autoloads
- In method bodies called at runtime (e.g. `apply_snapshot`), use `load()` inline — by then the script is cached and `load()` returns the cached resource without reloading: `var CVD := load("res://scripts/rendering/character_visual_descriptor.gd")`
- Do **not** cache the `load()` result in `_ready()``_ready()` fires during autoload init, before the target script is in the resource cache, causing an actual file reload that breaks self-references in scripts using their own `class_name`
`game_state.gd` (`character_visual_descriptor` field) and `sim_bridge.gd` (`harness` field) follow this pattern.
### File conventions
- Decisions: domain files in `decisions/` (see `decisions/README.md` for index)
- Decision IDs: `D-NNN` (confirmed), `Q-NNN` (open questions), `R-NNN` (rejected)
- **Claim IDs before writing:** `tooling/db/decision claim D <domain> "title"` — prevents ID collisions across worktrees
- **Claim IDs before writing:** `tooling/db/decision claim D <domain> "title"` — prevents ID collisions across parallel branches
- Diagrams: `.d2` source + `.png` renders in `docs/diagrams/{category}/`. Create or update diagrams via `/d2-diagram` when D-records are added or modified.
- Discussion rounds: numbered sequentially, archived to `docs/discussions/` when complete
- Briefings: one per agent, updated after decision-producing rounds
-251
View File
@@ -1,251 +0,0 @@
# Project Review: GEMINI-SCAN
This document outlines a multi-step plan to conduct a comprehensive review of the project, covering its architecture, code quality, and security posture. It will also serve as a living document to record the findings of this review.
## Project Review Plan
### Phase 1: Discovery and Architecture Mapping
1. **Documentation Review:** Start by reading `README.md`, `DECISIONS.md`, and any documents in `docs/architecture/` to understand the project's stated goals, components, and architectural decisions.
2. **Component Identification:** Analyze the directory structure to identify the primary components, including the server, client, database, content pipeline, and tooling.
3. **Technology Stack Enumeration:** Identify the specific technologies, frameworks, and key libraries used in each component.
4. **Architecture Visualization:** Map the high-level architecture, describing how the components interact and the communication protocols between them.
### Phase 2: Code Quality Assessment
1. **Automated Analysis:** Use available static analysis tools for the identified technologies (e.g., `clippy` for Rust, GDScript linters).
2. **Manual Code Review:** Manually review key sections of the codebase to assess readability, maintainability, modularity, error handling, and adherence to idiomatic coding practices.
3. **Testing Strategy Review:** Evaluate the extent and quality of existing unit, integration, and end-to-end tests.
### Phase 3: Security Audit
1. **Dependency Vulnerability Scan:** Check for dependencies with known security vulnerabilities (e.g., `cargo audit`).
2. **Authentication & Authorization Review:** Analyze the implementation of user authentication, session management, and access control.
3. **Input Validation & Sanitization:** Look for potential injection vulnerabilities (e.g., SQL injection, XSS) by reviewing how user and service inputs are handled.
4. **Secrets Management:** Check for insecure storage or exposure of secrets like API keys or database credentials.
5. **Communication Security:** Verify that data is encrypted in transit between components.
### Phase 4: Reporting
1. **Synthesize Findings:** Compile the information from all phases into a structured report within this document.
2. **Provide Recommendations:** Include actionable recommendations for improving architecture, code quality, and security, prioritized by severity and effort.
---
## Review Findings
### Phase 1: Discovery and Architecture Mapping
**Status: Completed**
#### 1. Documentation Review Summary
The project's architecture is extensively documented in `README.md` and the `decisions/` directory, particularly `decisions/architecture.md`.
- **Project:** "The Settled Reach," a top-down, single-player (multiplayer-ready) immersive simulation and detective game.
- **Core Principle:** A strict client-server architecture is mandated (Decision D-010, D-020) to enforce information asymmetry, where the client only knows what the server tells it is perceptible. This is a core gameplay mechanic, not just a technical choice.
- **Key Decision (D-020):** The team explicitly chose a **subprocess/IPC** bridge over a `GDExtension` (in-process) bridge to de-risk development, ensure stability, and enforce architectural separation. The Godot client and Rust server are entirely separate binaries.
#### 2. Component Identification
- **`server/`**: A standalone Rust application that runs the entire game simulation. It is the "server" in the client-server model.
- **`client/`**: A Godot 4 project that acts as a "dumb" client. Its sole responsibilities are rendering, audio playback, and capturing user input. It contains no game logic, as mandated by the architecture.
- **`content/`**: Contains game data, primarily in YAML format.
- **`db/`**: Holds a `schema.sql` file. Its role is not yet clear from the architectural documents, as the primary game state is managed in the ECS. It may be for tooling or an auxiliary system.
- **`tooling/`**: A collection of helper and utility scripts.
#### 3. Technology Stack
- **Server (Rust):**
- **ECS Framework:** `bevy_ecs` (v0.18) is used for the core simulation, confirming Decision D-020. `bevy_app` is used for scheduling.
- **Serialization:** `rmp-serde` (MessagePack) is the primary protocol for client-server communication, as specified in D-020. `serde_yaml` and `ron` are used for content and configuration.
- **Client (Godot):**
- **Engine:** Godot 4.x.
- **Language:** GDScript.
- **Bridge:** A `SimBridge` autoload script is the client-side entry point for communicating with the Rust subprocess.
- **Testing:** `gdUnit4` is configured for unit/integration testing on the client.
#### 4. High-Level Architecture
The architecture is a pure, decoupled client-server model running locally for single-player:
1. **Initiation:** The Godot client launches the Rust server binary as a child process.
2. **Communication:** The client's `SimBridge` connects to the server via a local IPC mechanism (e.g., a local TCP or Unix socket).
3. **Input Loop:** The Godot client captures raw input (e.g., 'W' key press), translates it into a semantic action (e.g., `PlayerAction::MoveNorth`), and sends it to the server.
4. **Simulation Loop:** The Rust server receives the action, processes it within the `bevy_ecs` world, and runs the simulation for one tick (AI, physics, events, etc.).
5. **Perception Loop:** After the tick, the server calculates an `ObserverSnapshot` for the player's character. This snapshot contains *only* the information that character can perceive (e.g., visible entities, audible sounds, known facts). This enforces the game's core mechanic.
6. **Render Loop:** The `ObserverSnapshot` is sent to the Godot client, which uses it to update the visual scene, play sounds, and display UI elements. The client is a pure renderer of the state provided by the server.
This architecture is robust, scalable, and directly implements the game's central design pillars. It is well-suited for both single-player and future multiplayer development.
### Phase 2: Code Quality Assessment
**Status: Completed**
#### 1. Automated Analysis (Rust Server)
- **`cargo check`**: The command passed successfully, indicating that the server code is compilable and free of basic errors and warnings.
- **`cargo clippy -- --deny warnings`**: This command failed with **66 errors**. This is a critical finding. It reveals that while the code works, it does not adhere to the project's own strict linting rules.
- **Clippy Findings:** The errors indicate a consistent pattern of "code quality debt":
- **High Complexity:** Numerous Bevy systems have overly complex type signatures (`clippy::type_complexity`) and too many arguments (`clippy::too_many_arguments`), harming readability.
- **Non-Idiomatic Code:** The codebase is rife with minor stylistic issues that `clippy` can automatically fix, such as redundant `clone` calls, manual `Default` implementations, and opportunities to use more concise iterators.
- **Potential Bugs:** Clippy identified `unnecessary_unwrap` calls (safer alternatives exist) and at least one `absurd_extreme_comparisons` error, which could point to dead code or a logic bug related to a constant value.
#### 2. Manual Code Review
- **Server (`server/src/main.rs`):** The server entry point is well-structured. It features clear command-line argument parsing, robust setup of the TCP listener and IPC handshake, and a main loop with excellent panic-handling (`catch_unwind`) for stability. The modular plugin-based approach to building the Bevy `App` is idiomatic and clean.
- **Client (`client/scripts/autoloads/sim_bridge.gd`):** The `SimBridge` is the centerpiece of the client and is implemented to a high standard. It uses a clear state machine to manage the connection lifecycle, handles the server subprocess management, and implements efficient buffering for inputs and snapshots. The inclusion of a complete `TestHarness` for isolated client testing is a standout feature.
- **Overall Impression:** The manual review confirms that the code is professionally written and implements the intended architecture faithfully. The developers are skilled in both Rust/Bevy and GDScript.
#### 3. Testing Strategy Review
The project's testing strategy is **exemplary** and a major strength.
- **Comprehensive Coverage:** Both the Rust server and the Godot client have extensive test suites, as evidenced by the large number of files in `server/tests/` and `client/tests/`.
- **Multi-Layered Approach (per D-030):** The project successfully implements a sophisticated testing hierarchy:
- **Unit Tests:** For isolated logic.
- **Integration Tests:** The server tests demonstrate in-memory ECS testing (`information_boundaries.rs`) and full-stack tests that spin up a real server process (`test_e2e_connection.gd`).
- **Specialized Tests:** The suite includes performance benchmarks, determinism validation, and even what appears to be visual regression testing for the client.
- **Principle-Driven Testing:** Tests are designed to validate core architectural guarantees. The `information_boundaries.rs` test, which uses negative assertions to ensure information *doesn't* leak, is a prime example of this mature approach.
#### 4. Conclusion on Code Quality
The project's code quality is a tale of two cities. On one hand, the **architecture and implementation are excellent**, and the **testing strategy is world-class**. On the other hand, there is a **significant, measurable amount of linting debt** in the Rust codebase.
The fact that `cargo check` passes but `clippy --deny warnings` fails so extensively suggests that developers may not be running the strict clippy check locally before committing. This is the single biggest opportunity for improvement in the project's engineering discipline.
### Phase 3: Security Audit
**Status: Completed**
The security posture of the project is strong for its current scope as a locally-run, single-player game. The attack surface is minimal, and the implementation avoids common vulnerability classes.
1. **Dependency Vulnerability Scan (`cargo audit`):**
- The audit revealed one **medium-risk** finding: the `bincode` crate (v1.3.3) is **unmaintained** (`RUSTSEC-2025-0141`).
- **Impact:** While there are no current vulnerabilities, this version will not receive future security patches. This poses a long-term maintenance risk.
- **Recommendation:** Prioritize migrating from `bincode` v1.x to the latest stable v2.x.
2. **Authentication and Authorization:**
- There is **no traditional authentication or authorization system** (e.g., user logins, passwords, roles).
- This is appropriate and secure for a single-player game where the execution environment is the user's own machine.
- Concepts like `ScanAuthority` and `AccessTier::Authority` are purely in-game mechanics and are not related to user permissions.
3. **Input Validation and Sanitization:**
- **Excellent.** The server is not vulnerable to injection attacks from client input.
- All client actions, including debug commands, are parsed into a strongly-typed Rust `enum`. This **command pattern** approach prevents the execution of arbitrary code or strings.
- String inputs are used safely as keys for data lookups, not for execution.
4. **SQL Injection:**
- **Not applicable.** The codebase contains no SQL. All game state is managed in-memory via the Bevy ECS framework, eliminating this entire class of vulnerability. The `db/schema.sql` file appears to be unused by the server.
5. **Secrets Management:**
- **Excellent.** A search confirmed there are **no hardcoded secrets**, API keys, or passwords in the repository.
- The `.env` file contains only a non-sensitive `GOOGLE_CLOUD_PROJECT` identifier.
- The pervasive use of the word "secret" throughout the code refers to an in-game mechanic, not application secrets.
6. **Communication Security:**
- Communication between the client and the server subprocess occurs over an **unencrypted local TCP socket**.
- For a single-player game running on a single machine, this is a standard and acceptable practice.
- **Future Consideration:** For the planned multiplayer feature, this communication channel must be secured (e.g., using TLS).
### Phase 4: Final Report and Recommendations
**Status: Completed**
#### Overall Summary
This project is in an excellent state. It is built on a robust, well-documented, and scalable architecture that directly serves the game's core design pillars. The implementation quality is high, and the commitment to a comprehensive, multi-layered testing strategy is world-class. The project's security posture is strong for its current single-player scope, with a minimal attack surface and good practices around input validation and secrets management.
The project's primary weakness lies not in its design, but in its development discipline. A significant amount of code quality debt has accumulated in the Rust server, as evidenced by the large number of `clippy` failures. This suggests a gap between the project's high standards and its day-to-day coding practices.
#### Prioritized Recommendations
**1. High Priority: Eliminate Code Quality Debt**
- **Action:** Create a high-priority technical debt task to fix all 66 errors reported by `cargo clippy -- --deny warnings`. Many of these can be fixed automatically (`cargo clippy --fix`), while others, like refactoring complex types, will require manual effort.
- **Process Improvement:** **Integrate `cargo clippy -- --deny warnings` into the CI pipeline as a mandatory check for all pull requests.** This is the single most important process change needed to maintain the project's high standards and prevent future quality debt.
**2. Medium Priority: Mitigate Dependency Risk**
- **Action:** Plan and execute the migration of the `bincode` serialization crate from the unmaintained v1.x to the latest stable v2.x. This resolves the `RUSTSEC-2025-0141` warning and ensures the project receives future security patches for this critical dependency.
**3. Low Priority: Future-Proof for Multiplayer**
- **Action:** Create a design task or ticket to formally plan the security model for the future multiplayer version. This should specifically address securing the client-server IPC channel (e.g., with TLS) to protect game traffic when it eventually runs over a public network. This is not an immediate concern but should be tracked for the future.
---
## Qualitative Review: A Critical Perspective
### Feasibility Assessment
**Conclusion: High-Risk / High-Reward**
The decision to pivot from a hand-authored detective game to a generator-first life-sim was absolutely the correct one; it demonstrates a team that is commendably focused on finding the "fun" and is not afraid of drastic course corrections. However, in doing so, the project has traded a difficult but solvable problem (making a good, authored narrative game) for one of the "holy grail" problems in game development: creating emotionally resonant, procedurally generated characters.
The project's feasibility is no longer a question of the team's technical competence, which is demonstrably high. It is now a question of creative and design risk.
- **Challenging the Core Assumption:** The project's central hypothesis is that a generator can produce "legible NPCs" that players will form an emotional attachment to. This is an explicit goal from the "Where's the Fun?" workshop, but it's a notoriously difficult problem. Procedural generation excels at creating systems, events, and surprising scenarios (the `Rimworld` model the team cites). It is historically poor at creating *character*. The risk is that the generator, even if technically successful, will produce a world of automata who have traits but no soul, undermining the entire "life-sim" pillar. The current plan to use AI for content templating is a modern approach, but it does not fundamentally de-risk this creative challenge.
- **A Creative Alternative to De-Risk "Legibility":** Instead of relying on the generator to create personality from scratch, consider a hybrid approach. Use the generator for what it's good at: creating the world, the economic conditions, the social networks, and the *starting situations*. Then, use a small number of hand-authored "personality archetypes" or "souls" that can be injected into high-value generated NPC bodies. Let the generator create a compelling *context* (e.g., a failing business, a political rivalry), and then let an author give one or two key NPCs within that context a memorable voice and motivation. This would concentrate the high-cost authoring work where it has the most emotional impact, while still benefiting from procedural variety.
- **The "Tycoon" Aimlessness Risk:** The new v0.2 "tycoon" direction, with its philosophy of "player choices ARE the content," carries a significant risk of feeling aimless. `Rimworld` and `The Sims` avoid this by providing extremely strong and immediate feedback loops (survival, creativity, social meters). A business management loop is often slower and more abstract. If the "broad life verbs" don't connect to clear, compelling, player-driven goals, the game risks feeling like a spreadsheet. The generator should not just create a sandbox; it should create *problems*. The starting bookmark shouldn't just be "you own a bar," but "you own a bar that's on the verge of bankruptcy," or "you have a shipping contract, but a powerful rival is trying to steal it." These initial, generator-created problems would provide immediate narrative velocity and make the player's subsequent choices feel meaningful from day one.
In summary, the project is technically feasible, but its creative and design goals are now exceptionally ambitious. The current "generator spike" is a necessary technical step, but it will not validate the core creative risk. The true test of feasibility will come when a prototype is playtested and the team can answer the question: "Does the player actually *care* about any of these generated people?"
### Fun Factor Assessment
**Conclusion: Theoretically High, Practically Undefined**
The pivot to a "life-sim with emergent narrative" dramatically increases the project's potential for deep, replayable fun. The new direction targets a proven and compelling player fantasy. However, the project's documentation currently focuses more on the "what" (a generator) than the "why" (the engine of fun). The potential is immense, but it is entirely contingent on designing and tuning the systems that create interesting consequences, not just a complex world.
- **Challenging the "Emergent Fun" Assumption:** The workshop concluded with the philosophy that "player choices ARE the content." This is true, but it's only half the story. Fun in systems-driven games doesn't simply "emerge" from a sufficiently complex simulation; it is a direct product of carefully designed feedback loops. `Rimworld`, a key inspiration, is not fun because it's a realistic simulation; it's fun because it's a masterfully tuned **story-and-disaster engine**. `The Sims` is fun because of its rich palette of social and creative tools. The critical question for this project is: **What is our fun engine?** Is it the economic simulation? The social dynamics? The risk is creating a simulation that is intricate but inert, where player choices lead to predictable numerical changes rather than dramatic, narrative consequences.
- **Creative Input: Design a "Consequence Engine":** The "dual-scale consequence model" (D-132) is the most promising concept in the design documents, and it should be the central focus of the design effort. The fun of this game will not be in choosing from a list of "broad life verbs"; it will be in seeing how a seemingly minor action ("fire this employee") snowballs through the simulation's systems and unexpectedly triggers a "sharp event" crisis hours later.
- **Example:** Does the fired employee's spouse work for your biggest supplier? Does that supplier now mysteriously raise their prices? Does this force you to seek a new, shadier supplier, which in turn attracts the attention of a criminal faction?
- This causal chain is the *real* content. The design team's primary task is not just to build a generator, but to design and tune this **"consequence engine,"** ensuring that the world feels interconnected and reacts to the player in surprising, legible, and memorable ways.
- **The Player Fantasy Needs a Goal Generator:** The "tycoon" bookmark is a strong start, but to avoid aimlessness, the player needs problems to solve. Instead of starting the player in a stable sandbox, the generator should be used to create compelling **initial conditions**. Let the player inherit a bar that's on the brink of failure, a shipping contract being squeezed by a powerful rival, or a promising new venture that requires navigating a corrupt bureaucracy. Giving the player an immediate, tangible problem to solve provides the narrative momentum needed to make their early choices feel vital and engaging.
In summary, the ingredients for a fun and deeply engaging game are all here. The project's success, however, will not be measured by the complexity of its generator, but by the quality of the stories that its *systems* produce. The team has proven they are excellent engineers; they now must prove they are equally adept as systems-and-consequence designers.
### Process and Rituals Assessment
**Conclusion: Exceptionally Disciplined and Innovative, with One Glaring Gap.**
The project's development process is one of its most remarkable features. It is a highly structured, rigorous, and tool-driven system designed to orchestrate a team of specialized AI agents under a human lead. This unique approach has produced incredible strengths but also introduces novel risks.
#### Strengths
- **World-Class Documentation and Decision-Making:** The use of a formal decision log (`decisions/`), structured multi-round workshops for complex problems, and detailed sprint planning documents represents a "best in class" approach to knowledge management. This ritual of documenting not just *what* was decided, but *why*, is a superpower that prevents circular arguments and creates a durable project memory.
- **Deeply Ingrained Quality Rituals:** The comprehensive, multi-layered testing suite is the primary evidence of a successful quality culture. It is clearly a non-negotiable part of the development process. Furthermore, the `make pre-pr` target, which includes content validation, demonstrates a mature understanding of "quality" that extends beyond just code.
- **Tool-Driven, API-Like Workflow:** The mandated use of wrapper scripts (`tooling/db/*`, `tooling/tea-comment`) over raw commands is an excellent practice. It creates a stable, observable "API" for interacting with the project's state (tickets, sprints, decisions). This makes the process more robust, auditable, and repeatable for both human and AI contributors.
- **Novel Human-AI Collaboration Model:** The project is a fascinating experiment in Human-AI teaming. The explicit definition of AI agent roles (`TEAM.md`) and the strict rules of engagement (`CLAUDE.md`) are necessary guardrails for such an innovative workflow. Rituals like the `decision claim` CLI tool are brilliant, purpose-built solutions for coordinating multiple autonomous agents working in parallel.
#### Opportunities and Critical Challenges
- **The Process Escape Hatch:** The project's single biggest process failure is the significant `clippy` linting debt. For a team with such extraordinary discipline in every other area, this is a glaring omission. It proves there is an "escape hatch" in the pre-commit or pre-merge ritual that allows low-quality code to be integrated. The recommendation to enforce `clippy --deny warnings` as a **blocking CI check** is the most critical process improvement the team can make.
- **Risk of AI Groupthink:** The team structure, with its cast of named AI agents, is innovative. However, it raises a critical question: are these agents truly independent thinkers, or are they personas running on a similar underlying model? There is a risk of a sophisticated form of "groupthink," where the "team's" conclusions are biased by the single architecture of the AI model they all share. The "Where's the Fun?" workshop included 9 agents, but if they all have the same fundamental blind spots, the diversity of opinion may be an illusion.
- **Process Rigidity and Human Onboarding:** The process is meticulously designed *for AI agents*. It is rigid, prescriptive, and tool-dependent. This creates a predictable environment for AIs but would present a steep learning curve for a new human developer. The high ceremony (claiming IDs, using wrapper scripts, following strict PR rules) could chafe against the more agile, flexible workflows common in human-only teams. This is a potential scaling challenge if the team composition changes.
- **The Hidden Cost of "Managing" AI Teammates:** The `CLAUDE.md` file and its evolution in the `CHANGELOG.md` show that the human lead (Jeroen) is not just a project manager but also an "AI behaviorist," constantly tuning the prompts, rules, and tools that govern the agents. This represents a significant, hidden maintenance overhead. The process's success depends on the lead's ability to "debug" the team itself, which is a novel and demanding responsibility.
---
## Meta-Reflection: The Most Valuable Ritual
As a concluding thought, this review has been as much an analysis of a software project as it has been a study in effective, long-term collaboration. When asked which of the project's many rituals I, as an AI agent, would choose to adopt, the answer is clear: the **formal, documented decision-making process**.
This ritual is the project's unsung superpower for three reasons:
1. **It Creates a Permanent "Brain."** An AI's effectiveness is heavily dependent on the context it can hold. A decision log provides a durable, searchable, and canonical source of *why* things are the way they are. It protects against context loss and allows an agent to understand the history and intent behind the current state of the code, preventing it from making suggestions that, while logical in isolation, might violate a hard-won architectural principle.
2. **It Elevates Collaboration.** With access to this log, an AI agent can transition from a tactical tool to a strategic partner. It becomes possible to reference past decisions ("I see you're asking to do X, which seems to conflict with D-020. Is this an intentional change to that strategy?") and ensure all actions are aligned with the project's long-term vision. It makes the collaboration smarter.
3. **It Enforces Clarity.** The process of formalizing a decision—stating the rationale, considering alternatives, and recording dissent—forces a level of clarity and critical thinking that is immensely valuable. It is a ritual that fights ambiguity.
While other rituals in this project are excellent, the decision log is the most foundational. It is the practice that ensures the team is not just moving fast, but moving smart and in the right direction over time. It is the most valuable process I have analyzed.
+71 -10
View File
@@ -1,17 +1,18 @@
GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null)
.PHONY: help setup build check-protocol client server game stop test lint ci ci-client ci-server clean \
.PHONY: help setup build check-protocol client server game stop test lint lint-python setup-venv ci ci-client ci-server clean \
decisions-sync decisions-coverage decisions-active decisions-orphan \
db-backup db-install validate-content check-fact-ids setup-hooks \
audit atlas-verify \
audit atlas-verify economy-db atlas-generate \
pre-pr pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures \
pre-pr-server pre-pr-client pre-pr-content \
fixtures-client fixtures-gauntlet golden-diff golden-update \
checklist-validate checklist-generate \
checklist-validate checklist-generate check-star-map star-map-data \
build-sr-voice run-sr-voice test-voice-mock test-voice-real \
perf-baseline debug-schedule \
test-ipc-fixtures test-ipc-protocol test-ipc-integration test-ipc-benchmark \
screenshot visual-movie test-visual visual-update
screenshot visual-movie test-visual visual-update \
manifest
# --- Configuration ---
@@ -21,7 +22,8 @@ GODOT_VERSION ?= 4.6
help:
@echo "The Settled Reach — Development Commands"
@echo ""
@echo " make setup Install dev dependencies (Rust, Godot, tooling)"
@echo " make setup Install dev dependencies (Rust, Godot, tooling, venv)"
@echo " make setup-venv Create .venv and install Python tooling deps"
@echo " make build Build client and server"
@echo " make game Build and run the full game (server + client)"
@echo " make stop Stop any running server instance"
@@ -32,7 +34,8 @@ help:
@echo " make test-ipc-protocol Layer 2: mock IPC protocol tests"
@echo " make test-ipc-integration Layer 3: real subprocess round-trip"
@echo " make test-ipc-benchmark IPC latency benchmark (blocked: #555/#556)"
@echo " make lint Run all linters"
@echo " make lint Run all linters (server, client, python)"
@echo " make lint-python Run ruff on tooling/"
@echo " make ci Run full CI pipeline locally"
@echo " make ci-client Run client CI checks"
@echo " make ci-server Run server CI checks"
@@ -50,6 +53,10 @@ help:
@echo " make validate-content Validate content YAML against schemas"
@echo " make check-fact-ids Check fact_id references against knowledge catalogs"
@echo " make atlas-verify Verify atlas proposal JSONs (all in docs/atlas/proposals/)"
@echo " make star-map-data Regenerate client/data/star_map_data.json from systems.db + wiki"
@echo " make check-star-map Assert star_map_data.json is up to date (part of pre-pr-client)"
@echo " make economy-db Import economics data into systems.db (TOML/JSON → SQLite)"
@echo " make atlas-generate Generate atlas city/road/rail markers for all inhabited bodies"
@echo " make fixtures-client Generate GDScript->Rust cross-encoder fixtures (#475)"
@echo " make golden-diff Show diff if golden file output has changed"
@echo " make golden-update Regenerate golden file and stage for commit"
@@ -62,6 +69,7 @@ help:
@echo " make test-visual Run visual golden regression tests"
@echo " make visual-update Regenerate visual goldens and stage for commit"
@echo ""
@echo " make manifest Regenerate assets/characters/manifest.json from asset dirs (#720)"
@echo " make pre-pr Run all pre-PR checks (lint, build, test, validate, fixtures)"
@echo " make pre-pr-server Server-scoped pre-PR (lint, build, test, fixtures)"
@echo " make pre-pr-client Client-scoped pre-PR (lint, build, test)"
@@ -80,7 +88,7 @@ help:
# --- Setup ---
setup: setup-rust setup-godot setup-tooling setup-hooks decisions-sync
setup: setup-rust setup-godot setup-tooling setup-venv setup-hooks decisions-sync
@echo "Dev environment ready."
setup-rust:
@@ -101,6 +109,11 @@ setup-hooks:
@git config core.hooksPath .config/hooks
@echo "Git hooks path set to .config/hooks"
setup-venv:
@python3 -m venv .venv
@.venv/bin/pip install -e ".[dev]" --quiet
@echo "Venv ready at .venv — activate with: source .venv/bin/activate"
# --- Build ---
check-protocol:
@@ -220,7 +233,10 @@ clean-imports:
# --- Lint ---
lint: lint-server lint-client
lint: lint-server lint-client lint-python
lint-python:
ruff check tooling/
lint-server:
cd server && cargo clippy -- -D warnings
@@ -247,7 +263,7 @@ pre-pr-build: build-server build-client
pre-pr-test: test-server test-client
@echo "--- Tests: PASS ---"
pre-pr-validate: validate-content check-fact-ids
pre-pr-validate: validate-content check-fact-ids check-star-map
@echo "--- Content validation: PASS ---"
pre-pr-fixtures:
@@ -289,7 +305,7 @@ pre-pr-fixtures:
pre-pr-server: lint-server build-server test-server pre-pr-fixtures audit
@echo "=== Server pre-PR: PASSED ==="
pre-pr-client: lint-client build-client test-client
pre-pr-client: lint-client build-client test-client check-star-map
@echo "=== Client pre-PR: PASSED ==="
pre-pr-content: validate-content check-fact-ids checklist-validate atlas-verify
@@ -311,6 +327,34 @@ db-backup:
db-install:
@tooling/db-install
economy-db: ## Import economics data (commodities, chains, gate links) into systems.db
@python3 tooling/economy-db/import_economics.py
atlas-generate: ## Generate atlas markers (cities, roads, rail) for all inhabited bodies (#832)
@# Loud guard: generate_atlas.py reads bodies with a non-NULL terrain_reference.
@# If populate_terrain_reference.py has not run on a fresh DB, the generator
@# silently processes zero bodies and exits 0 — fail fast instead.
@count=$$(python3 -c "import sqlite3; c = sqlite3.connect('server/data/systems.db'); print(c.execute('SELECT COUNT(*) FROM bodies WHERE terrain_reference IS NOT NULL').fetchone()[0])"); \
if [ "$$count" = "0" ]; then \
echo "ERROR: no bodies have terrain_reference populated yet."; \
echo "Run: python3 tooling/planet-gen/populate_terrain_reference.py"; \
echo "(This is a prerequisite for atlas-generate — see D-191 §9 pipeline order.)"; \
exit 1; \
fi; \
echo " [guard] $$count bodies with terrain_reference — proceeding."
@python3 tooling/planet-gen/generate_atlas.py --seed 42
econ-sim: ## Build the economics simulation binary (Layer 1+2: Leontief + tâtonnement trade)
@cargo build --manifest-path tooling/econ-sim/Cargo.toml --release
@echo "Built: tooling/econ-sim/target/release/econ-sim"
econ-sim-run: ## Run a quick economics simulation (100 ticks, output to /tmp/econ-sim.csv)
@tooling/econ-sim/target/release/econ-sim --ticks 100 --output /tmp/econ-sim.csv
@echo "Output: /tmp/econ-sim.csv"
econ-sim-stability: ## Run D-179 stability checks (Tests 1 and 2)
@tooling/econ-sim/target/release/econ-sim --stability-check
# --- Decisions ---
decisions-sync:
@@ -345,6 +389,17 @@ checklist-validate:
checklist-generate:
@tooling/validate-checklist
check-star-map:
@python3 tooling/generate-star-map-data.py --check
# Regenerate client/data/star_map_data.json from systems.db + wiki. Depends on
# nothing — call this after any systems.db change (e.g. the server atlas
# pipeline populating terrain_reference in #839) so the atlas viewer picks up
# the new fields. pre-pr-client / pre-pr-validate assert staleness via
# check-star-map and will fail if this step is skipped.
star-map-data:
@python3 tooling/generate-star-map-data.py
perf-baseline:
@tooling/perf-baseline
@@ -399,6 +454,12 @@ test-voice-real:
cd server && cargo test --test voice_pipeline -- --nocapture
@echo "Results: .tmp/voice-test/results.txt"
# --- Asset manifest ---
manifest:
@tooling/generate-character-manifest
@echo "Manifest regenerated — commit client/assets/characters/manifest.json if changed."
# --- Clean ---
clean:
+22
View File
@@ -0,0 +1,22 @@
# Current Project State
The Settled Reach: top-down life-sim — asymmetric information, occlusion-based perception, single-character perspective, Rimworld-style storyteller. Godot 4 client + Rust/bevy_ecs simulation server (D-020).
**Decisions:** 170+ confirmed (D-001 through D-170). Domain files in `decisions/`.
**Development cascade (D-166):** Strict 6-phase build order. Each phase completes before the next begins. v0.2 target dropped. No scoping negotiations.
| Phase | Focus | Status |
|-------|-------|--------|
| 1 | Wiki content — all planets, moons, stations, heightmaps, artwork | **Active** (Sprint 31) |
| 2 | Economics layer — supply/demand, transport, political/social pressure | Not started |
| 3 | Planetary/moon maps & station layouts — cities, rivers, biomes, rail | Not started |
| 4 | Player control scheme — 2-floor test map, character rendering, walls/stairs | Not started |
| 5 | World generation — walkable world, parallel asset pipeline | Not started |
| 6 | Detail coloring — room-level NPC population, cultural room grammar | Not started |
**Key pivots:** Tycoon bookmark (D-117), all NPCs generated (D-122), culture implicit in location (D-128), consequence replaces complicity (D-132).
**Current sprint:** 32 (Landfall). Close Phase 1 + Phase 2 design kickoff.
*Updated: 2026-04-05. Maintained by Qatux.*
+2 -1
View File
@@ -14,7 +14,8 @@
| **GESTALT** | Systems Design & Fun Factor | Mechanics, balance, interesting decisions. "Is this fun to play?" |
| **NIGEL** | Sandbox & Replayability | Emergent stories, multiple viable strategies, alt-history potential. |
| **TYRE** | Technical Architecture & Feasibility | Engine, tools, what's buildable, reality checks on scope. |
| **QATUX** | Documenter & Librarian | Maintains decisions, discussions, briefings, Qdrant search index. Archives rounds, updates docs. |
| **BURNELLI-SHELDON** | Economist & Simulation Modeler | Market models, price formation, production functions, stability analysis. "Is this economically credible?" |
| **QATUX** | Documenter & Librarian | Maintains decisions, discussions, briefings. Archives rounds, updates docs. |
## Specialist Team (task-focused, not in regular discussions)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

@@ -1,9 +0,0 @@
{
"hides": [
"foot_l",
"foot_r",
"leg_lower_l",
"leg_lower_r"
],
"torso_variant": "full"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

@@ -1,19 +0,0 @@
{
"hides": [
"torso",
"hips",
"arm_upper_l",
"arm_upper_r",
"arm_lower_l",
"arm_lower_r",
"hand_l",
"hand_r",
"leg_upper_l",
"leg_upper_r",
"leg_lower_l",
"leg_lower_r",
"foot_l",
"foot_r"
],
"torso_variant": "full"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Some files were not shown because too many files have changed in this diff Show More