feat(composer): PROMOTION -- the corpus grows itself, and it did not collapse

Tier 5. §3.3's answer to Jahni's "I'd like the system to not rely at all on
the handmade ones": promotion does not REMOVE the authored seed, it dilutes
it. Every invented strate that measures well rejoins the corpus.

Five simulated seasons, 24 candidates each:

  season  corpus  project/default/promoted  survival  spread
    1       15          4/8/3               20.8%     0.869
    2       20          4/8/8               54.2%     0.849
    3       20          4/8/8               54.2%     0.849
    4       21          4/8/9               45.8%     0.916
    5       21          4/8/9               50.0%     0.916

Corpus 12 -> 21, of which 9 are discovered. Survival roughly doubles.

⭐ AND THE COLLAPSE RISK DID NOT MATERIALISE, which was the thing worth
measuring rather than assuming. Promotion selects survivors, and blending
survivors repeatedly can converge the corpus onto a narrow safe region --
the composer would get better at passing and worse at surprising. Spread
went 0.869 -> 0.916. It dipped and plateaued mid-run, but did not narrow.

Recorded honestly: seasons 3 and 5 promoted ZERO. The near-duplicate gate
(normalised metric distance < 0.20 over air, largest share, walkable,
feature scale, clearance) is doing its job, but it also means the corpus
saturates at this scale. Five seasons of 24 demonstrates the MECHANISM, not
§3.3's "season 5 blends 200 discovered ones" -- that needs the overnight
budget, not a test.

Safety: a promoted record is RE-MEASURED on load, never trusted. Verified by
corrupting a stored metric -- the loader discarded it and re-derived the
geometry. A stale or tampered record therefore cannot introduce an
unescapable strate into a future season, which is the one failure that would
matter. Provenance (project/default/promoted) is tracked and logged.

Codex's recommendation, and I agree: keep a small explicit OUTLIER QUOTA
among candidates that pass every hard gate. This run showed no collapse, but
survivor-only promotion keeps the long-term risk, and a quota costs nothing.

PART A: the fine ROI now centres on LargestComponentPoint instead of (0,0).
Blank fine renders 6/42 -> 3/42. The label now reads "largest open space",
not "strate centre" -- the image moved, so the caption had to.

Build clean, 27 succeeded / 0 failed, verified by me. WorldRadiusVoxels=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-05 01:46:38 +02:00
1 parent 43eaf1ef0e
commit 0b3ec18b43
10 files changed
+2614 -53

No files matched your search

+86 -10
View File
@@ -253,6 +253,50 @@ small commandlet walking the struct by reflection. Clamp coverage is partial. A
tunables at all (`StrateTopWorldZ` / `StrateBottomWorldZ` are runtime Z bounds) and need an explicit
exclusion list. Bools cannot be blended — roll them by probability or inherit from the dominant parent.
### ✅ Tier 5 status — promotion is built and measured (2026-09-05)
Promotion is now a real corpus input, not a claim about a future approval UI. A surviving editor
candidate becomes a `FVoxelStratePromotableRecord`: complete native parameter vector (all six family
blocks), recipe, archetype tag, measured metric summary, season/seed/candidate identity, input corpus
hash, and the three gate results. `VF_SaveStratePromotedRecords` writes a sorted, pretty JSON store
(`schema_version=2`) beside one JSON season manifest per run. `FVoxelStrateCorpus` loads those records
alongside project assets and C++ defaults, tags each member `project`, `default`, or `promoted`, and
prints the provenance counts in the membership report. The stored metric is evidence for review only:
the world-specific verifier rebuilds the recipe and remeasures it on every load, and a failed fresh
non-vacuous, largest-component, or primordial arrival→departure gate cannot enter the corpus.
The provisional policy is deliberately explicit. It accepts only records that pass all three gates,
requires a normalized measured-metric distance of **at least 0.20** from every usable existing member
and already accepted record, and caps a season at **6** promotions. Distance is Euclidean over
`(air fraction, largest-component share, walkable fraction, median feature scale / 256 voxels,
median vertical clearance / 64 voxels)`. The 0.20 threshold is a review-sized novelty floor: it is
roughly a 20-point move in one bounded fraction, or a smaller combined move across dimensions, while
the physical normalizers make feature/clearance changes comparable. It is provisional, not the
owner's definition of good. One authored asset was measured as a valid but all-air window in this
fixture (`DA_Strate2`), so it is retained as measured-but-vacuous and uses conservative exact-parameter
identity fallback rather than a false metric distance; the other **11/12** base members participate
in the measured novelty space.
The five-season simulation used 24 structure candidates per season, the fixed sample window
(`step=8`, `radius=256`, `WorldRadiusVoxels=0`), and the same fresh verifier on every reload:
| Season | Input corpus hash | Corpus after | project/default/promoted | survivors / 24 | survival | measured spread | promoted | rejected near-dup / hash / cap |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| 1 | `9dbcea85` | 15 | 4 / 8 / 3 | 5 | 20.8% | 0.869214 | 3 | 2 / 0 / 0 |
| 2 | `8b624d80` | 20 | 4 / 8 / 8 | 13 | 54.2% | 0.849043 | 5 | 8 / 0 / 0 |
| 3 | `cfbd74ec` | 20 | 4 / 8 / 8 | 13 | 54.2% | 0.849043 | 0 | 13 / 0 / 0 |
| 4 | `cfbd74ec` | 21 | 4 / 8 / 9 | 11 | 45.8% | 0.916302 | 1 | 10 / 0 / 0 |
| 5 | `c108a276` | 21 | 4 / 8 / 9 | 12 | 50.0% | 0.916302 | 0 | 12 / 0 / 0 |
The curve does **not** show a monotonic survival improvement: `20.8%, 54.2%, 54.2%, 45.8%,
50.0%`. Measured spread also dips and plateaus, but ends wider: **0.869214 → 0.916302
(+0.047089)**. This five-season run therefore found no collapse toward a mean, but it is not evidence
that collapse cannot happen at a different seed or policy. The current recommendation is to keep the
novelty floor and add a small, explicitly reviewable outlier quota in a later policy revision—promote
some distant survivors even when their quality score is merely acceptable—rather than tune this run
to look smoother. The owner still needs the preview before defining “good”; promotion is the dilution
mechanism, not that missing judgment.
### ✅ Tier 4a status — parameter roll built (2026-09-04)
`Public/VoxelStrateComposer.h` + `Private/VoxelStrateComposer.cpp` now implement the corpus and
@@ -654,6 +698,13 @@ cap, or the writer can be called for an explicitly selected list. Fine filenames
card's text include the sample step and exact resolved XY/Z window, so they cannot be mistaken for the
coarse render. A refused fine render is shown as a refusal with its reason rather than silently omitted.
The fine ROI is now centred on each survivor's deterministic `LargestComponentPoint` from the coarse
air flood fill and is labelled **“largest open space”** on the page. It is not the middle of the
strate and not the world origin. In the final 42-survivor run, the origin-centred baseline had **6/42**
uniform fine plan projections (and 6/42 complete two-view cards); the largest-open-space pass had
**3/42** in both counts. The reduction is real but not total: a largest open space can itself be
larger than a radius-64 ROI, so a uniform card is not proof that the strate is empty.
The vertical image is an XZ section through the sampled cell nearest the strate-window centre Y. The
plan image scans every sampled Z layer, keeps layers containing both air and solid, and chooses the
one with the largest number of XY solid/air boundary transitions; ties prefer more mixed cells, more
@@ -667,11 +718,11 @@ orange walkable cells, a turquoise scalar contour, and a one-chunk footer scale
resampling. It also carries the required caveat: a 2D apparent join is not a 3D connectivity proof;
the separate arrival→departure verdict is the connectivity check.
The focused run measured **0.456 s** for the coarse render, **70.271 s** for the 42 fine scalar samples,
and **0.573 s** for their filled/contour rendering. Peak fine `Air` + `Density` capture allocation was
**9,338,880 bytes** and the peak single RGBA raster was **75,776 bytes**. The full StructureRoll test
completed in **184.659 s**, with 64 coarse filled pairs, 64 coarse contour pairs, 42 fine filled pairs,
42 fine contour pairs, and 0 refusals.
The final run measured **0.300 s** for the coarse render, **133.272 s** for the origin-plus-centred
fine scalar samples, and **1.113 s** for their filled/contour rendering. Peak fine `Air` + `Density`
capture allocation was **9,338,880 bytes** and the peak single RGBA raster was **75,776 bytes**. The
full StructureRoll test completed in **243.649 s**, with 64 coarse filled pairs, 64 coarse contour
pairs, 42 fine filled pairs, 42 fine contour pairs, and 0 refusals.
### 3.5 Validation in three layers, and how "good" is ever judged
@@ -696,7 +747,10 @@ But *good* never has to be defined, only **collected**:
⚠️ **Known risk: convergence to a comfortable middle.** Promote only what you already liked, keep
blending near the corpus, and the system slowly stops surprising you. Same failure mode as the
build-vote pool, same counter: keep extrapolating past the corpus, and keep some unrated weird
candidates in circulation.
candidates in circulation. The first five-season promotion measurement did **not** collapse: mean
normalized spread ended 0.047089 wider than it started, but survival was not monotonic. That is one
seed and one provisional policy, not a dispensation from the risk; the next policy should reserve a
reviewable outlier quota instead of assuming survivor promotion preserves surprise.
---
@@ -981,13 +1035,21 @@ roughness and should stay late in the stack.
- ✅ **Structure roll (§3.2, Tier 4b)** — serialisable recipes, root polarity, declaration-derived
resource-safe modifier legality, mandatory posts, 64-candidate measurement, and direct box-verdict
brute force are built; the bootstrap follow-up is 42/64 survival with 0 invalid recipes and 0
violations, plus 64 rendered preview pairs and an index.html contact sheet.
violations, plus 64 rendered preview pairs and an index.html contact sheet. The fine survivor ROI
is centred on `LargestComponentPoint` and labelled largest open space; the final before/after
blank-plan count is 6/42 → 3/42.
- ⬜ Reject-and-resample driven by Tier 2 (§3.5) · the **offline season pipeline** with Jahni's review
and veto (§3.1).
### Tier 5 — the long game
**Promotion** (good strates rejoin the corpus) · theme and tag draws for materials, creatures and audio ·
eventually the model. All optional, all compounding.
- ✅ **Promotion** (good strates rejoin the corpus) — JSON promotable records and season manifests,
project/default/promoted provenance, fresh-load re-verification of the three gates, measured
near-duplicate rejection, per-season cap, and a deterministic five-season simulation. The run ended
at 21 corpus members (4 project, 8 default, 9 promoted); spread widened overall but survival was
non-monotonic. The owner still needs to define “good” from the preview; promotion does not invent
that judgment.
- ⬜ Theme and tag draws for materials, creatures, and audio · eventually the model. All optional,
all compounding.
---
@@ -1025,7 +1087,10 @@ Game-design questions live in **[GDD.md](GDD.md) §16**. These are the ones that
3. **How theme coherence is drawn** — operators, materials, decorations, creatures and audio pulled as a
compatible set. Jahni expects his first big job to be creating themes and tagging content with
multiple fitting tags.
4. **Where the corpus lives** and how promotion is recorded between seasons.
4. ✅ **Where the corpus lives** and how promotion is recorded between seasons: the cumulative store is
`Saved/ComposerPromotion/seed_0_step_8_radius_256/promoted_strates.json` in the simulation, with
`season_01_manifest.json` … `season_05_manifest.json` alongside it; the default runtime/editor
location is `Saved/VoxelForge/StrateCorpus/promoted_strates.json`.
5. **Whether op discovery** (promoting good sub-stacks into named reusable units) is ever worth it.
Parked: the existing parameter space is already vastly larger than a hundred seasons could explore.
**The scarce resource is judgment, not vocabulary.**
@@ -1071,6 +1136,17 @@ them is *wrong* — a false verdict leaves no geometry and no collision behind i
---
**2026-09-05 — Tier 5 + fine-ROI validation.** The specified UE 5.7 `VoxelMEditor Win64
Development` build succeeded. The full headless `VoxelForge` namespace reported **27 succeeded,
0 failed, 0 not run, 0 succeeded-with-warnings** in **613.277 s**. The focused promotion test was
green in **63.556 s** and the focused StructureRoll test was green in **243.649 s**. Promotion ended
with 9 cumulative records, fresh-load stale-metric replacement, `WorldRadiusVoxels=0`, and the table
in §3.3. The StructureRoll preview measured fine blank plan/card counts **6/42 → 3/42** and rendered
42 fine filled pairs plus 42 contour pairs with 0 refusals. No existing generation/equivalence test
failed.
---
## 11. Tier 1 status — passages aim at real open space (bounded full-point query; build not run)
**Updated 2026-08-29.** `VF_SuggestLandingPoint` (in `VoxelCaveMorphology`) is a **pure free function**