diff --git a/docs/shots/laneA/round2_oral_prompt_evolution.png b/docs/shots/laneA/round2_oral_prompt_evolution.png new file mode 100644 index 0000000..5eed20d Binary files /dev/null and b/docs/shots/laneA/round2_oral_prompt_evolution.png differ diff --git a/docs/shots/laneA/round2_provenance_probe.png b/docs/shots/laneA/round2_provenance_probe.png new file mode 100644 index 0000000..f9387a8 Binary files /dev/null and b/docs/shots/laneA/round2_provenance_probe.png differ diff --git a/docs/shots/laneA/round2_tint_appendix.png b/docs/shots/laneA/round2_tint_appendix.png index f589bd1..6510be4 100644 Binary files a/docs/shots/laneA/round2_tint_appendix.png and b/docs/shots/laneA/round2_tint_appendix.png differ diff --git a/docs/shots/laneA/round2_tint_large_intestine.png b/docs/shots/laneA/round2_tint_large_intestine.png index 59c9d04..fad6ddf 100644 Binary files a/docs/shots/laneA/round2_tint_large_intestine.png and b/docs/shots/laneA/round2_tint_large_intestine.png differ diff --git a/docs/shots/laneA/round2_tint_oral.png b/docs/shots/laneA/round2_tint_oral.png index 5178ccd..e2c4b30 100644 Binary files a/docs/shots/laneA/round2_tint_oral.png and b/docs/shots/laneA/round2_tint_oral.png differ diff --git a/docs/shots/laneA/round2_tint_small_intestine.png b/docs/shots/laneA/round2_tint_small_intestine.png index ae1f8ef..541b603 100644 Binary files a/docs/shots/laneA/round2_tint_small_intestine.png and b/docs/shots/laneA/round2_tint_small_intestine.png differ diff --git a/pipeline/batch_textures.json b/pipeline/batch_textures.json index d5684e8..08de48f 100644 --- a/pipeline/batch_textures.json +++ b/pipeline/batch_textures.json @@ -12,6 +12,48 @@ "steps": 4, "tile": null }, + "wall_colon_a": { + "gen_seconds": 9.1, + "guidance": 3.5, + "kb": 69, + "kb_n": 188, + "kind": "wall", + "mean_after": 0.495, + "mean_before": 0.234, + "model": "flux2-klein-4b", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, colonic mucosa, smooth undulating folds, scattered biofilm patches", + "seam_after": 0.74, + "seam_before": 14.06, + "seed": 1501, + "size": 1024, + "steps": 4, + "tile": [ + 3, + 20 + ], + "tile_mode": "blend" + }, + "wall_colon_b": { + "gen_seconds": 9.0, + "guidance": 3.5, + "kb": 180, + "kb_n": 313, + "kind": "wall", + "mean_after": 0.497, + "mean_before": 0.271, + "model": "flux2-klein-4b", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, colonic wall coated in thick bacterial biofilm, lumpy microbial mats and mucus sheets covering the whole surface, filling the entire frame edge to edge", + "seam_after": 1.36, + "seam_before": 6.37, + "seed": 1502, + "size": 1024, + "steps": 4, + "tile": [ + 4, + 15 + ], + "tile_mode": "blend" + }, "wall_esophagus_a": { "gen_seconds": 9.1, "guidance": 3.5, @@ -21,7 +63,8 @@ "mean_after": 0.495, "mean_before": 0.216, "model": "flux2-klein-4b", - "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, flat top-down macro swatch filling the entire frame edge to edge, uniform repeating coverage, no single central subject, nothing else visible, human esophageal mucosa, longitudinal ribbed folds, wet ridges", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, human esophageal mucosa, longitudinal ribbed folds, wet ridges", + "prompt_corrected": "2026-07-16 Lane A: record held the REJECTED framing experiment's prompt (LANE_D_NOTES \u00a7prompt-kit); the shipped image was proved to come from this one. Evidence: docs/shots/laneA/round2_provenance_probe.png", "seam_after": 1.14, "seam_before": 3.7, "seed": 1101, @@ -54,6 +97,48 @@ ], "tile_mode": "blend" }, + "wall_oral_a": { + "gen_seconds": 9.2, + "guidance": 3.5, + "kb": 108, + "kb_n": 230, + "kind": "wall", + "mean_after": 0.49, + "mean_before": 0.168, + "model": "flux2-klein-4b", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, oral mucosa, cobblestone pavement of flat polygonal squamous epithelial cells, tightly packed cell borders, wet glistening surface, filling the entire frame edge to edge", + "seam_after": 1.01, + "seam_before": 6.35, + "seed": 1001, + "size": 1024, + "steps": 4, + "tile": [ + 5, + 12 + ], + "tile_mode": "blend" + }, + "wall_oral_b": { + "gen_seconds": 9.1, + "guidance": 3.5, + "kb": 220, + "kb_n": 363, + "kind": "wall", + "mean_after": 0.498, + "mean_before": 0.322, + "model": "flux2-klein-4b", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, tooth enamel surface, hard mineralized crystalline texture, fine cracks and wear pits", + "seam_after": 1.08, + "seam_before": 9.3, + "seed": 1002, + "size": 1024, + "steps": 4, + "tile": [ + 3, + 20 + ], + "tile_mode": "blend" + }, "wall_smallint_a": { "gen_seconds": 9.2, "guidance": 3.5, @@ -84,7 +169,8 @@ "mean_after": 0.496, "mean_before": 0.24, "model": "flux2-klein-4b", - "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, flat top-down macro swatch filling the entire frame edge to edge, uniform repeating coverage, no single central subject, nothing else visible, intestinal crypts of Lieberkuhn, honeycomb of round pits, packed glandular openings", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, intestinal crypts of Lieberkuhn, honeycomb of round pits, packed glandular openings", + "prompt_corrected": "2026-07-16 Lane A: record held the REJECTED framing experiment's prompt (LANE_D_NOTES \u00a7prompt-kit); the shipped image was proved to come from this one. Evidence: docs/shots/laneA/round2_provenance_probe.png", "seam_after": 1.32, "seam_before": 10.5, "seed": 1302, @@ -105,7 +191,8 @@ "mean_after": 0.496, "mean_before": 0.216, "model": "flux2-klein-4b", - "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, flat top-down macro swatch filling the entire frame edge to edge, uniform repeating coverage, no single central subject, nothing else visible, gastric mucosa, pitted craters, gastric pits, glistening mucus", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, gastric mucosa, pitted craters, gastric pits, glistening mucus", + "prompt_corrected": "2026-07-16 Lane A: record held the REJECTED framing experiment's prompt (LANE_D_NOTES \u00a7prompt-kit); the shipped image was proved to come from this one. Evidence: docs/shots/laneA/round2_provenance_probe.png", "seam_after": 0.87, "seam_before": 12.16, "seed": 1201, @@ -126,7 +213,8 @@ "mean_after": 0.497, "mean_before": 0.25, "model": "flux2-klein-4b", - "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, flat top-down macro swatch filling the entire frame edge to edge, uniform repeating coverage, no single central subject, nothing else visible, stomach rugae, thick heavy folded ridges, deep valleys between folds", + "prompt": "scanning electron microscope micrograph, monochrome, high detail organic tissue, dark background, dramatic rim lighting, seamless tiling texture, stomach rugae, thick heavy folded ridges, deep valleys between folds", + "prompt_corrected": "2026-07-16 Lane A: record held the REJECTED framing experiment's prompt (LANE_D_NOTES \u00a7prompt-kit); the shipped image was proved to come from this one. Evidence: docs/shots/laneA/round2_provenance_probe.png", "seam_after": 1.08, "seam_before": 17.85, "seed": 1202, diff --git a/pipeline/gen_textures.py b/pipeline/gen_textures.py index 5d79d87..e3138c0 100644 --- a/pipeline/gen_textures.py +++ b/pipeline/gen_textures.py @@ -67,6 +67,54 @@ WALLS = { "wall_smallint_b": dict( p="intestinal crypts of Lieberkuhn, honeycomb of round pits, packed glandular openings", seed=1302, tile=[6, 10]), + + # ── oral + colon: added round 2 by LANE A, on D's behalf, using D's pipeline unchanged ── + # Why A: the six-biome tint contact sheet (web/dev/laneA_world.html?lvl=biomes) could only + # eyeball 3 of 6 biomes for real — oral and large_intestine had no pack and fell back to + # procedural, and you cannot ratify a tint against a stand-in. These complete the set that + # ART_BIBLE §Biome palettes promises. **D owns them: ratify, retune or bin them.** + # Seeds follow D's anatomical-order convention (11xx esophagus … 13xx smallint); oral sits + # before esophagus at 10xx, and colon takes 15xx because 14xx was already the matcap. + # + # NOTE for D + F: ART_BIBLE §FLUX prompt kit has NO oral entry (it lists esophageal, + # stomach, small intestine, colon). colon_a below is the bible's line VERBATIM; the other + # three subjects are new text, and the mouth ones are therefore an ART_BIBLE *gap* I filled + # rather than a spec I followed — see LANE_A_NOTES §→ Lane D for the proposed amendment. + "wall_oral_a": dict( + # Third subject, and the reason is worth more than the texture (PIPELINE §Textures.2: + # "≤2 attempts, then pick a different prompt" — so this is a different prompt, not a + # third tweak): + # v1 "…soft wet TONGUE surface" -> a perfect SEM dome. Naming an ORGAN summons + # its silhouette: "tongue" is a thing with a shape, so FLUX drew the thing and + # arranged the papillae on it. That's D's "one urchin of villi" in a field's + # clothing, and it's why the edge-to-edge clause alone couldn't save it. + # v2 "…mucosa … flat top-down macro" -> full coverage, but the papillae still + # radiated from a centre. The real culprit was never the framing: **papillae are + # 3D projections, so any honest render of them has perspective, and perspective + # has a vanishing point** — a radial rosette that tiles into starbursts. + # v3 (this): ask for a subject that is FLAT BY CONSTRUCTION. Squamous epithelium is + # what oral mucosa actually is at SEM scale — a pavement of polygonal cells, no + # projections, nothing to foreshorten. It also reads clinical/bright (ART_BIBLE + # "tutorial-readable") and collides with nothing else in the pack. + # Rule for the kit, generalising D's: prompt the tissue's SURFACE, not its FEATURES. + p="oral mucosa, cobblestone pavement of flat polygonal squamous epithelial cells, " + "tightly packed cell borders, wet glistening surface, filling the entire frame edge to edge", + seed=1001, tile=[5, 12]), + "wall_oral_b": dict( + # Deliberately NOT tissue: enamel is the one hard, mineral, non-organic surface in the + # whole game, it reads instantly as "mouth", and it gives L1's molar crush-cycles + # (GDD/Lane C) a wall of their own. No framing clause — a surface, not a subject. + p="tooth enamel surface, hard mineralized crystalline texture, fine cracks and wear pits", + seed=1002, tile=[3, 20]), + "wall_colon_a": dict( + p="colonic mucosa, smooth undulating folds, scattered biofilm patches", # bible, verbatim + seed=1501, tile=[3, 20]), + "wall_colon_b": dict( + # A COATING, not folds — that's what keeps it clear of stomach_b's rugae and of + # smallint_b's crypt honeycomb, and it is what "dark swamp" (ART_BIBLE) actually is. + p="colonic wall coated in thick bacterial biofilm, lumpy microbial mats and mucus " + "sheets covering the whole surface, filling the entire frame edge to edge", + seed=1502, tile=[4, 15]), } # matcap: NOT tileable, NOT seam-blended — a lit sphere cropped to a square (see derive_maps) MATCAPS = { @@ -131,10 +179,45 @@ def record(slug, fn, secs): json.dump(log, open(BATCH, "w"), indent=2, sort_keys=True) +def verify_provenance(): + """Assert the recorded prompt still equals what this script emits, per asset. + + Added round 2 by Lane A after finding 4 of 6 walls had drifted (see LANE_A_NOTES §-> Lane D). + The failure is invisible and the script's own docstring is the thing it breaks + ("regeneration must always be possible"): record() only runs when you GENERATE, so if you + change a prompt and keep the old image — exactly what D's rejected framing experiment + correctly did, keeping four winners rather than churning them — the JSON keeps the prompt + that was never shipped. Nothing warns you. Re-generating from it silently yields a + different texture (proved: docs/shots/laneA/round2_provenance_probe.png). + + Fixing a drift is a judgement call, so this reports and never rewrites: + - the image is the one you want => the RECORD is stale: re-point it at the code prompt. + - the record is what you want => regenerate the image (--local) and it self-heals. + """ + log = json.load(open(BATCH)) if os.path.exists(BATCH) else {} + drift, missing = [], [] + for slug in sorted(PROMPTS): + if slug not in log: + missing.append(slug) + elif log[slug].get("prompt") != prompt_of(slug): + drift.append(slug) + for s in missing: + print(f" no record {s:22s} (never generated, or provenance lost)") + for s in drift: + print(f" DRIFT {s:22s} recorded prompt != prompt this script would send") + ok = not drift + print(f"provenance: {len(PROMPTS) - len(drift) - len(missing)}/{len(PROMPTS)} verified, " + f"{len(drift)} drifted, {len(missing)} unrecorded") + return ok + + if __name__ == "__main__": only = sys.argv[sys.argv.index("--only") + 1] if "--only" in sys.argv else "" todo = {s: prompt_of(s) for s in PROMPTS if (not only or only in s)} + if "--verify-provenance" in sys.argv: + sys.exit(0 if verify_provenance() else 1) + if "--harvest" in sys.argv: sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import derive_maps diff --git a/tools/qa.sh b/tools/qa.sh index 0adaf5e..c9fad88 100755 --- a/tools/qa.sh +++ b/tools/qa.sh @@ -47,6 +47,18 @@ if [ -f pipeline/validate_manifest.py ]; then python3 pipeline/validate_manifest.py && green "manifest valid" || red "manifest errors" fi +# 5b. Asset provenance: does batch_textures.json still record the prompt that made the pack? +# Added by Lane A (round 2) after finding 4 of 6 walls recorded the prompt from Lane D's +# REJECTED framing experiment — regenerating from provenance produced a visibly different +# texture (docs/shots/laneA/round2_provenance_probe.png). record() only runs on generation, so +# reverting a prompt while keeping its image drifts silently and forever. This is the gate. +# Stdlib only, no GPU, no network. -> F: yours to keep or drop, flagged in LANE_A_NOTES. +if [ -f pipeline/gen_textures.py ]; then + python3 pipeline/gen_textures.py --verify-provenance >/dev/null 2>&1 \ + && green "texture provenance" \ + || red "texture provenance drift — run: python3 pipeline/gen_textures.py --verify-provenance" +fi + # 6. vendor present (the game must run offline) [ -f web/vendor/three.module.js ] && green "vendor: three r175 present" || red "web/vendor/three.module.js missing" diff --git a/web/assets/gen/wall_colon_a.webp b/web/assets/gen/wall_colon_a.webp new file mode 100644 index 0000000..bcfdea0 Binary files /dev/null and b/web/assets/gen/wall_colon_a.webp differ diff --git a/web/assets/gen/wall_colon_a_n.webp b/web/assets/gen/wall_colon_a_n.webp new file mode 100644 index 0000000..77f2924 Binary files /dev/null and b/web/assets/gen/wall_colon_a_n.webp differ diff --git a/web/assets/gen/wall_colon_b.webp b/web/assets/gen/wall_colon_b.webp new file mode 100644 index 0000000..f0f307b Binary files /dev/null and b/web/assets/gen/wall_colon_b.webp differ diff --git a/web/assets/gen/wall_colon_b_n.webp b/web/assets/gen/wall_colon_b_n.webp new file mode 100644 index 0000000..1954500 Binary files /dev/null and b/web/assets/gen/wall_colon_b_n.webp differ diff --git a/web/assets/gen/wall_oral_a.webp b/web/assets/gen/wall_oral_a.webp new file mode 100644 index 0000000..6507c5a Binary files /dev/null and b/web/assets/gen/wall_oral_a.webp differ diff --git a/web/assets/gen/wall_oral_a_n.webp b/web/assets/gen/wall_oral_a_n.webp new file mode 100644 index 0000000..35ca594 Binary files /dev/null and b/web/assets/gen/wall_oral_a_n.webp differ diff --git a/web/assets/gen/wall_oral_b.webp b/web/assets/gen/wall_oral_b.webp new file mode 100644 index 0000000..b31efd1 Binary files /dev/null and b/web/assets/gen/wall_oral_b.webp differ diff --git a/web/assets/gen/wall_oral_b_n.webp b/web/assets/gen/wall_oral_b_n.webp new file mode 100644 index 0000000..44efd2e Binary files /dev/null and b/web/assets/gen/wall_oral_b_n.webp differ diff --git a/web/assets/manifest.json b/web/assets/manifest.json index 2e56241..3cc7228 100644 --- a/web/assets/manifest.json +++ b/web/assets/manifest.json @@ -47,6 +47,22 @@ }, "models": {}, "textures": { + "wall_colon_a": { + "normal": "gen/wall_colon_a_n.webp", + "tile": [ + 3, + 20 + ], + "url": "gen/wall_colon_a.webp" + }, + "wall_colon_b": { + "normal": "gen/wall_colon_b_n.webp", + "tile": [ + 4, + 15 + ], + "url": "gen/wall_colon_b.webp" + }, "wall_esophagus_a": { "normal": "gen/wall_esophagus_a_n.webp", "tile": [ @@ -63,6 +79,22 @@ ], "url": "gen/wall_esophagus_b.webp" }, + "wall_oral_a": { + "normal": "gen/wall_oral_a_n.webp", + "tile": [ + 5, + 12 + ], + "url": "gen/wall_oral_a.webp" + }, + "wall_oral_b": { + "normal": "gen/wall_oral_b_n.webp", + "tile": [ + 3, + 20 + ], + "url": "gen/wall_oral_b.webp" + }, "wall_smallint_a": { "normal": "gen/wall_smallint_a_n.webp", "tile": [ diff --git a/web/js/world/index.js b/web/js/world/index.js index 4118911..5f72665 100644 --- a/web/js/world/index.js +++ b/web/js/world/index.js @@ -66,7 +66,16 @@ export async function createWorld(levelData, { rng, quality = 'high', assets = n // (`assets.misses()`), so a drifted slug announces itself instead of silently falling back // forever — which is what made this dangerous in round 1. const TEXTURE_SLUG = { small_intestine: 'smallint', large_intestine: 'colon' }; - const slug = (biomeId) => TEXTURE_SLUG[biomeId] ?? biomeId; + + // NOT temporary, and not the same thing as the slug map above: this is the ART_BIBLE's + // "one texture can serve two biomes at different tints" law, used deliberately. The appendix + // is anatomically an outpouching of the colon — it is the same tissue — so it wears the colon + // pack under its own gold tint and costs zero bytes. Keep this when the slug map dies. + const TEXTURE_SHARE = { appendix: 'large_intestine' }; + const slug = (biomeId) => { + const b = TEXTURE_SHARE[biomeId] ?? biomeId; + return TEXTURE_SLUG[b] ?? b; + }; /** One wall's full texture set, all optional and independently so. */ function texturesFor(biomeId) {