Generated on MODELBEAST flux_local, on-device, $0.00. Lane D's pipeline used
unchanged; **D owns these — ratify, retune or bin them.**
Why A did D's job: the six-biome tint contact sheet could only judge 3 of 6
biomes for real (oral/large_intestine/appendix had no pack and fell back to
procedural), and you cannot ratify a tint against a stand-in. All six are now
textured — the set ART_BIBLE §Biome palettes promises.
- wall_oral_a/b, wall_colon_a/b + normals. Seams 6.35/9.30/14.06/6.37 -> 1.01/
1.08/0.74/1.36 (D's "indistinguishable" band; the shipped pack's weakest is
2.39). Means normalised to ~0.50 by D's derive_maps, untouched.
- appendix wears the colon pack at its own gold tint (TEXTURE_SHARE in
world/index.js) — anatomically it IS colon tissue, and it's ART_BIBLE's
"one texture, two biomes" law for zero bytes. Distinct from the temporary
slug map; keep it when that dies.
- oral_a took three subjects. v1 named an ORGAN ("tongue") and FLUX drew the
organ's silhouette — D's "one urchin of villi" in a field's clothing. v2 named
the tissue but papillae are 3D projections, so perspective gave it a vanishing
point that tiles into starbursts. v3 asks for squamous cell pavement: flat by
construction. Rule for the kit: prompt the SURFACE, not the FEATURE.
Evidence: docs/shots/laneA/round2_oral_prompt_evolution.png
PROVENANCE DEFECT FOUND AND FIXED (-> Lane D):
batch_textures.json recorded, for 4 of 6 walls, the prompt from the framing
experiment D tried and rejected — not the prompt that made the shipped pack.
record() only runs on generation, so reverting a prompt while keeping its image
(exactly what D correctly did: "four winners kept untouched rather than churned")
drifts the record silently. Proved by regenerating esophagus_a at seed 1101 under
both prompts: the code's prompt reproduces the shipped wall, the recorded one
gives the bland wood-grain D described rejecting. Evidence:
docs/shots/laneA/round2_provenance_probe.png. Records re-pointed at the code's
prompt (images untouched — they were never wrong).
New: gen_textures.py --verify-provenance + qa.sh gate 5b, so it cannot rot again.
qa GREEN incl. the new gate; 11/11 provenance verified; all six biomes eyeballed
in engine (docs/shots/laneA/round2_tint_*.png).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
67 lines
3.1 KiB
Bash
Executable File
67 lines
3.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# tools/qa.sh (Lane F) — the gate. Run before you finish a session; leave it GREEN.
|
|
# Dependency-free: bash + node + python3. Gates accumulate each round.
|
|
set -u
|
|
cd "$(dirname "$0")/.."
|
|
FAIL=0
|
|
red() { printf '\033[31m✗ %s\033[0m\n' "$1"; FAIL=1; }
|
|
green() { printf '\033[32m✓ %s\033[0m\n' "$1"; }
|
|
|
|
# 1. every JS module parses.
|
|
# NB: plain `node --check` exits 0 on ANY file containing import/export (ESM detection
|
|
# no-op — found by Lane B round 1, verified on node v26). Must force module parsing.
|
|
JSBAD=0
|
|
while IFS= read -r -d '' f; do
|
|
node --check --input-type=module < "$f" 2>/dev/null || { echo "SYNTAX: $f"; node --check --input-type=module < "$f"; JSBAD=1; }
|
|
done < <(find web/js -name '*.js' -print0)
|
|
[ "$JSBAD" = 0 ] && green "node --check (ESM): all web/js modules parse" || red "JS syntax errors"
|
|
|
|
# 2. determinism law: Math.random only in core/rng.js
|
|
RAND=$(grep -rn 'Math\.random' web/js --include='*.js' | grep -v 'core/rng.js' || true)
|
|
if [ -n "$RAND" ]; then red "Math.random outside core/rng.js:"; echo "$RAND"; else green "determinism: no stray Math.random"; fi
|
|
|
|
# 3. level JSONs parse + minimal keys
|
|
LVLBAD=0
|
|
for f in web/js/levels/*.json; do
|
|
[ -e "$f" ] || continue
|
|
python3 - "$f" <<'PY' || LVLBAD=1
|
|
import json, sys
|
|
d = json.load(open(sys.argv[1]))
|
|
assert d.get("id") and isinstance(d.get("seed"), int) and d.get("segments"), "missing id/seed/segments"
|
|
PY
|
|
done
|
|
[ "$LVLBAD" = 0 ] && green "level JSONs valid" || red "level JSON errors"
|
|
|
|
# 4. Lane C selfcheck (when it exists)
|
|
if [ -f web/js/levels/index.js ]; then
|
|
node web/js/levels/index.js --selfcheck && green "levels selfcheck" || red "levels selfcheck failed"
|
|
fi
|
|
|
|
# 4b. Lane A world math (headless; no three.js needed)
|
|
if [ -f web/js/world/spline.js ]; then
|
|
node web/js/world/spline.js --selfcheck >/dev/null 2>&1 && green "world selfcheck" || red "world selfcheck failed"
|
|
fi
|
|
|
|
# 5. Lane D manifest validation (when it exists)
|
|
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"
|
|
|
|
[ "$FAIL" = 0 ] && printf '\033[32mQA GREEN\033[0m\n' || printf '\033[31mQA RED\033[0m\n'
|
|
exit $FAIL
|