Files
settled-reach/docs/gemma-architecture-flavor-methodology.md
T
jpmschweitzerandClaude Fable 5 a6c2ff4740 docs(assets): Gemma architecture-flavor methodology — bounded corpus distillation (T-992)
Stage-1 register selection over the current wiki tree (Q-107 assumption
stated), stage-2 TEXT-FIELDS-ONLY structured proposals (tag, label,
cultural_description, candidate allow/block tags) — humans own all numeric
eligibility/weights (catalog-wide CI guardrails can't be satisfied
per-template); Araminta owns visual_bundle. No era-bands (Q-106 dissolved);
trauma_decay_rate stale-schema note. Mirrors docs/gemma-naming-methodology.md
incl. the sr-voice-tooling serving pattern. Worked example: GJ-4053 granite
dry-stack accept; GJ-1248/GJ-285/GJ-588 as correct stage-1 rejections.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:15:56 +02:00

331 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gemma Architecture-Flavor Methodology (D-232, T-992)
**Status:** design methodology, not yet implemented. Analogous in structure and
intent to [`docs/gemma-naming-methodology.md`](gemma-naming-methodology.md)
(D-223) — the retired pipeline that named atlas features from wiki prose — but
the two are solving different problems. Naming filled thousands of empty
`name` fields at generation time. This pass proposes a **small number of new
rows in an authored, frozen catalog**, once, with a human in the loop before
anything ships. There is no runtime Gemma call anywhere in the architecture-
flavor draw (D-232 phase 1-3): `trait_selection` is a deterministic
`SeedChain`-seeded pick from the catalog. Gemma's only role is helping grow the
catalog file between authoring passes.
---
## 1. Scope — bounded corpus-distillation, not per-body generation
D-232 round 3 retired the round-2 per-body Gemma-array model in favor of a
shared trait-template catalog. The catalog's **content** is split into two
tracks (T-977 scope note, 2026-05-26):
- **Core hand-authored set** (T-1005, shipped) — Miri (cultural meaning +
eligibility) and Araminta (visual_bundle) authored **28 templates** directly:
10 `cross_corridor`, 8 `baseline`, 10 `heritage` (see the corridor-pool
breakdown in `wiki/economics/architecture_trait_catalog.toml`). This sits
within D-232's stated floor (~25-35) but below its target (40-45).
- **This pass** — a *bounded* Gemma corpus-distillation run: **one read of all
wiki prose → propose candidate templates → human-gated review**, growing the
catalog from 28 toward the 40-45 target. It runs **after** the core set
exists and reads it as context (see §4.1).
**What this explicitly is not:**
- Not per-body open-vocabulary generation. There is no Gemma call in the
skeleton-generation or chunk-fill path. The round-2 model (a Gemma call per
body, producing a bespoke culture) is the thing D-232 retired.
- Not a recurring or CI-triggered job. It runs manually, whenever the catalog
is judged to need growth (new corridor, new archetype, a corpus content
addition worth distilling) — not on every wiki edit.
- Not a source of any numeric field. See §5.
**Superseded stage-2 schema note:** the original T-977/T-992 ticket text (round
2) described a stage-2 output of `{tag, allow, block, weight,
trauma_decay_rate}`. Neither `weight` nor `trauma_decay_rate` survive to D-232
round 3 — there is no per-template decay rate anywhere in the shipped schema
or `traits.py`, and Gemma never emits a weight (§5-§6 explain why). Treat any
reference to `trauma_decay_rate` outside this note as stale round-2 prose.
---
## 2. Model & runtime — reuse the naming pipeline's binary split
`docs/architecture/atlas-naming-pipeline.md` §2 documents the split the retired
naming pipeline used, and the same reasoning applies here, more strongly:
- **Two binaries exist.** The in-repo `sr-voice serve --model <path> --stdio`
(`server/sr-voice/`) is the **game runtime** — Gemma 2, small context
(`--ctx-size`, historically 1024 tokens for naming), tuned for low-latency
in-game NPC dialogue, no network port (Gemma 2 T&C compliance). The naming
pipeline instead used **`sr-voice-tooling`** — a separate binary built
against Gemma 4 (GGUF), compiled for ROCm/HIP (`gfx1201`), living outside the
repo (`~/Projects/settled-reach/binaries/sr-voice-tooling`) and run inside
the `reach-build` distrobox. The split exists because tooling work trades
latency for richer output and a bigger context window — exactly the
trade this pass wants.
- **Recommendation: reuse `sr-voice-tooling` / Gemma 4, not the in-game
runtime.** This pass needs to read a full `index.md` page (several hundred
to ~1000+ words — see §3), not the naming pipeline's hand-compressed 30-45
word GTTR hook. Gemma 2's ~1024-token context was already tight for the
*compressed* naming case; reading full system pages needs the larger
context the tooling binary provides.
- **Protocol: identical stdio JSONL, unchanged.** Both binaries speak the same
newline-delimited JSON contract (`server/sr-voice/src/stdio.rs`,
`docs/architecture/atlas-naming-pipeline.md` §2): one request per line,
`{"prompt": "...", "seed": N}`, one response per line, `{"text": "..."}` or
`{"error": "..."}`. No new protocol work is needed to point a new script at
either binary.
- **Call volume is much smaller, so KV-cache bleed matters less — but the fix
is the same if it shows up.** The naming pipeline processed ~2,400 bodies ×
5 feature types and needed `--refresh`-driven subprocess restarts to stop
a long-lived model echoing earlier completions. This pass makes on the
order of ~300 stage-1 calls (one per system) plus one stage-2 call per
surfaced candidate (expected: a small fraction of 300, since most systems
should say "no distinct tradition" — see §4.1). If output diversity
degrades across a long run, apply the same lesson: restart the subprocess
periodically (e.g. every system boundary) to flush context.
- **Few-shot over instructions carries over unchanged.** The naming
pipeline's model-fit lesson — *Gemma is far better at pattern completion
than instruction-following* — applies to both stages here. Prompts should
be worked examples ("system prose → verdict + name", "flagged prose →
structured JSON"), not bare instructions.
---
## 3. Source material — the wiki tree, and the Q-107 assumption
**Stage 1 reads `wiki/star-systems/*/index.md`** — the system-level wiki page
(System Profile, cultural/economic prose, Faction Notes, Silence Topic,
Narrative Hook). All 302 systems have one.
> **This is an explicit assumption, not a permanent contract.** Q-107 (open —
> wiki → Atlas content-set consolidation) proposes moving all wiki content
> (pages, heightmaps, markers, architecture-flavor source) into one
> consolidated, first-class content tree. If Q-107 resolves and the content
> moves, **only stage 1's file glob needs updating** — the extraction prompt,
> the structured-output schema, and the human-review gate are all
> location-invariant, per D-232's own note that "the generator-facing tables
> are invariant to [source location]."
Companion `gttr.md` / per-body `gttr-<slug>.md` files (present for ~301 of 302
systems) carry additional narrative color and were the naming pipeline's
secondary source for register selection. **This pass scopes stage 1 to
`index.md` only** — per the T-992 refinement. Folding `gttr*.md` in as a
secondary source is a plausible future expansion, not required for the first
pass.
Q-107's audit note (2026-06-12) already flags the kind of prose this pass
exists to consume — **distinctive material/craft traditions that currently
exist only as prose and have no catalog counterpart**:
| System | index.md location | Tradition |
|---|---|---|
| GJ-4053 (Dzimbahwe) | lines 22-26 | granite dry-stack, mortarless masonry |
| GJ-1248 | line 30 | peat-wood carving |
| GJ-285 | lines 28-38 | basalt crater farms |
| GJ-588 | lines 30-34 | structural timber |
None of these four appear as a distinct template in the shipped 28 (see the
worked example, §8).
---
## 4. Two-stage flow
### 4.1 Stage 1 — candidate surfacing (per-system read → shortlist)
This differs from the naming pipeline's stage 1 (pick one of N predefined
cultural registers). Here Gemma performs **signal detection**, not
classification: for each system, it reads the `index.md` prose plus a compact
context block listing the **existing catalog's tag + label +
cultural_description** (28 entries — enough to ground "already covered" but
short enough to fit context), and is asked one question — *does this system's
prose describe a coherent, distinctive architectural/material tradition not
already represented in the catalog, and if so, name it and quote the
grounding passage.*
The expected answer for most systems is **no** — most systems should draw from
an existing corridor baseline or heritage template, not spawn their own. A
system earning a "yes" needs a real, holistic, catalog-worthy signal (the
Dzimbahwe stone tradition — not "this system likes blue paint").
Output: a shortlist of `(system_id, candidate_name, grounding_quote)` tuples,
much smaller than the 302-system corpus.
### 4.2 Stage 2 — structured proposal (shortlist → schema)
For each stage-1 hit, a second call turns the flagged prose into the
structured proposal (§5). Candidate `allow_tags`/`block_tags` are drawn **only
from the catalog's existing shared material palette** (the `ObjectTag`
vocabulary enumerated in the catalog file's header comment — `walls`, `roofs`,
`facade`, `street` lists) so proposals stay pre-constrained to compatible
vocabulary. Minting a genuinely new `ObjectTag` is a separate, human-only
vocabulary-authoring step (T-977(d), Miri+Araminta co-maintained) — Gemma
proposes combinations of existing tags, never new tags.
---
## 5. Structured output schema — text fields only
```json
{
"tag": "granite_drystack_vernacular",
"label": "Granite Dry-Stack Vernacular",
"cultural_description": "...",
"candidate_allow_tags": ["stone_wall", "..."],
"candidate_block_tags": ["glass_curtain_wall", "..."],
"source_system": "GJ-4053",
"grounding_quote": "..."
}
```
Gemma **never** sets a numeric or eligibility field. This is not a scoping
choice made for simplicity — it's forced by the shape of the guardrails: the
catalog-wide CI checks (`V-TT-01`: ≥5 eligible templates per `BulkClass`
post-gate; `V-TT-02`: no template >60% of its eligible pool's weight,
`tooling/economy-db/economy_import/traits.py`) are properties of the **whole
catalog**, not of any one template in isolation. A per-template extraction
pass, looking at one system's prose, has no way to know whether adding this
candidate at `base_weight = 9000` would push some `BulkClass`'s weighted pool
over the 60% line — only a human with the whole catalog in view can safely
assign eligibility gates and weights. This is the same channel-separation
principle D-232/D-233 already establish for economics vs. culture (economics
hard-gates *what* a building is; the catalog only *weights* how it's
characterized), extended one level further:
| Field(s) | Set by | Why |
|---|---|---|
| `tag`, `label`, `cultural_description` | Gemma, human-reviewed | text extraction is the point of this pass |
| candidate `allow_tags` / `block_tags` | Gemma, human-corrected | pre-constrained to the existing `ObjectTag` palette |
| `corridor_pool`, `geographic_sector` | Miri | needs whole-pool view (baseline vs. heritage; D-237 heritage-value alignment) |
| `bulk_class_gate`, `production_ubiquity_gate`, `min_prosperity_bps` | Miri | hard-gate eligibility — guardrail-sensitive |
| `base_weight`, `weight_mods` | Miri (Nigel spot-checks V-TT-02) | pool-weight math the guardrails enforce |
| `zone_affinity` | Miri | district fit, informed by `cultural_description` |
| `era_scope` | Miri | maintenance/wear scope (D-217), not a tech ladder |
| `visual_bundle` | Araminta | D-235 fallback-hierarchy authoring |
**Field-naming note:** D-232's decision text names these fields `allow` /
`block`; the shipped catalog TOML and `traits.py` use `allow_tags` /
`block_tags` (T-1005 implementation). This doc uses the shipped names
throughout, since that's the schema any accepted proposal has to match — the
decision record's prose is the one out of step here and could use a tidy-up
note pointing at the implemented names.
---
## 6. The human-review gate
**Reviewers:** Miri (cultural meaning, eligibility, weights — same role as
T-1005), Araminta (`visual_bundle`), Nigel (spot-checks CI guardrail math on
request; not required per-proposal since the importer re-validates
automatically at bake time — see §7).
**Acceptance criteria**, applied to every candidate before it becomes a
`[templates.*]` stanza:
1. **Genuine distinctiveness** — `cultural_description` reads as a coherent,
holistic relationship between wall/roof/facade/street (never an
axis-scramble), and isn't a restatement of an existing template under a
new name.
2. **Dedup against the existing catalog** — check both the `tag` (mechanical,
unique key — `traits.py` will already reject a collision) and the
`label`/`cultural_description` (semantic — a near-duplicate cultural voice
under a different label doesn't earn a second template; fold the
distinguishing detail into an existing template's `allow_tags` instead, or
reject outright).
3. **Vocabulary compatibility** — candidate `allow_tags`/`block_tags`
reference only tags already in the shared `ObjectTag` palette; a genuinely
new material/facade/roof tag routes to the separate vocabulary-authoring
step, not this pass.
4. **Corridor fit** — Miri assigns `corridor_pool` + `geographic_sector`;
"corridors are tendencies, not borders" (D-232) — a candidate doesn't need
to be exclusive to one corridor to be accepted as a heritage sub-pool
entry.
5. **CI guardrails as a post-authoring gate, not a pass/fail on Gemma's
output.** Once Miri has assigned `base_weight`/`weight_mods`/gates,
`make regen-db` re-runs `populate_trait_templates()`
(`tooling/economy-db/economy_import/traits.py`), which re-validates
`V-TT-01` and `V-TT-02` across the **whole** catalog, not just the new
entry. A proposal can clear 1-4 and still get rebalanced or rejected at
bake time if it happens to push a `BulkClass` pool over the 60% line —
that's expected, not a process failure, and it's exactly why Gemma never
sets these numbers itself (§5).
Nothing is committed to `architecture_trait_catalog.toml` until it clears 1-4
**and** a `make regen-db` / `make check-systems-db` dry run comes back clean.
---
## 7. Flow into the catalog
1. Stage 1 + 2 produce N candidate proposals (one JSON object per stage-1 hit
that survived stage 2).
2. Miri reviews against §6's criteria 1-4; rejects, merges, or accepts each.
3. Accepted proposals become new `[templates.<tag>]` TOML stanzas, hand-added
to `wiki/economics/architecture_trait_catalog.toml` in the matching section
(the file's existing sections: **A** cross-corridor pool, **B**
per-corridor baseline, **C** heritage sub-pools) with Miri's numeric
fields filled in.
4. Araminta adds the matching `visual_bundle`, including the D-235 fallback
parent for every new specific tag (a new template ships with a working
generic-parent fallback from day one, same as the shipped 28).
5. `make regen-db` bakes the change via `import_economics.py` →
`traits.py:populate_trait_templates()`; `V-TT-01`/`V-TT-02` re-run
automatically (§6.5). `make check-systems-db` confirms the meta stamp.
6. Commit the TOML change and the regenerated `systems.db` together — the
ordinary asset-pipeline rule (`.claude/rules/asset-pipeline.md`): edit
sources, regen, commit both.
No path lets this pass write to `systems.db` directly. The golden rule is
unchanged: edit sources, never the DB.
---
## 8. Worked example — GJ-4053 (Dzimbahwe)
- **Stage 1** flags GJ-4053: the prose describes a 400-year granite dry-stack
tradition ("great house of stone... dry-stacked without mortar") and a
distinct sculptural tradition, neither represented by an existing template.
The closest existing templates — `civic_monumental` (`stone_wall,
vaulted_roof, colonnade`, institutional/ceremonial) and the south_reach
heritage pool (`iberian_hacienda`, `atlantic_creole_maritime` — Iberian and
Afro-Atlantic, not southern-African stone) — don't fit.
- **Stage 2** produces a proposal along these lines: `tag:
south_african_stone_vernacular`, `label: "Southern African Stone
Vernacular"`, `cultural_description` drawn from the "great house of stone"
language, `candidate_allow_tags: [stone_wall, ...]`, `candidate_block_tags:
[glass_curtain_wall, ...]`, `source_system: GJ-4053`, plus the grounding
quote.
- **Miri reviews:** distinct from the existing south_reach heritage pool
(criterion 2 passes) — accepts as a new south_reach heritage sub-pool entry,
assigns `min_prosperity_bps`, `weight_mods` (e.g. `founding_age.ancient`),
`zone_affinity` (Residential-leaning, matching the domestic/sculptural
framing).
- **Araminta** authors the `visual_bundle` (wall = `stone_wall`, roof/facade
choices, fallback chain to `generic_wall`/`generic_roof`).
The other three systems Q-107's audit note flags are useful for calibrating
**stage 1's expected "no" rate**, not as three more automatic templates: GJ-285
(basalt crater farms) and GJ-1248 (peat-wood carving / Moorlesen) are
agricultural and craft-object traditions, not building-architecture ones — a
correctly-run stage 1 would surface them as signal for the *materials/crafts*
side of Q-107's consolidation (feeding `atlas_body_trait_bias` hero-pin
authoring against an **existing** template), not propose a new
`[templates.*]` stanza. GJ-588 (structural timber) is architecture-adjacent,
but its `outer north_reach` setting already has a timber-forward baseline
(`north_anglo_frontier`: `timber_wall`, `pitched_roof`, `regular_facade`) — the
expected stage-1 outcome there is "no distinct tradition, corridor baseline
already covers it," which is the **correct, common** result, not a missed
opportunity. Only GJ-4053's stone tradition clears the bar because no existing
template — baseline or heritage, any corridor — matches it.
---
## 9. Open follow-ups
- **Q-107** (open) — source-location consolidation. If it resolves, update
§3's file glob; nothing else in this doc changes.
- **Filing the actual pass** — this doc defines the methodology only (T-992's
scope). No ticket yet schedules an execution run; file one under T-977 when
the catalog is judged to need growth toward the 40-45 target.