feat(@projects): ✨ close missing-sprite runtime faults
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
bc80362327
commit
86ab31afea
2 changed files with 123 additions and 3 deletions
|
|
@ -31,6 +31,58 @@ Slate is clean (user deleted 7 pre-existing sprites on 2026-04-17 for quality-ba
|
|||
- ◐ Theater GUI (`server.py` + `gui/`) boot smoke at `http://localhost:5850`. 2026-06-03 — **server side passes, front-end blocked.** `create_app()` builds 35 routes; `uvicorn server:app --port 5850` served `GET /api/stats` 200, `/api/theater` 200, `/api/progress` 200; stopped cleanly by worker PID (never by port). BUT root (`GET /`, the SPA index) returns 404 because `gui/dist` is not built, and **the build cannot complete in this environment**: `pnpm build` fails (`TS2307: Cannot find module 'react'`, ~20 JSX/TS errors) because `pnpm install` does not materialize `gui/node_modules` — the `@lilith/ui-animated` workspace dep hits the known pnpm `workspace:*` resolution bug (MEMORY `project_pnpmfile_workspace_fix`; needs `.pnpmfile.cjs` + Verdaccio). So the `/?spriteTheater=true` Theater UI cannot render until the GUI build is fixed. The Python server boot smoke is genuinely green; the React Theater render is unproven. ESCALATION: GUI build/install needs a separate fix (out of this session's fence focus).
|
||||
- ✓ `docs/PIPELINE.md` post-reset refresh. 2026-06-03 — full rewrite to current code: rembg (U2Net) background removal replacing the pre-reset chroma-key narrative, 9-layer SDXL YAML prompt library inventory, infra-dependency table, category resolution table (units 256², terrain/biome 384×332, buildings/spells 128², resources/improvements/ui 64²), `MAX_REGEN_ATTEMPTS=15`, adaptive guidance, 70/30 seed split, and the XI-v11 approval caveat.
|
||||
|
||||
## Missing-sprite fault closure — 2026-06-08
|
||||
|
||||
A runtime-driven gap-fill pass extended the data-driven stand-in tool
|
||||
(`tools/standin-sprites/build_standins.py` + `icon_rules.json`) to four renderer
|
||||
load surfaces the prior pass did not cover, eliminating the `ThemeAssets: FAILED to
|
||||
load` faults a real session triggers:
|
||||
|
||||
- **Ground truth (unseeded probe):** a 5-turn weston autoplay on apricot
|
||||
(`RENDER_MODE=weston`, no pinned seed) logged **18** distinct `FAILED to load`
|
||||
paths — 16× `sprites/throne_room/*`, 1× `sprites/terrain/tower_of_wizardry.png`,
|
||||
1× `sprites/units/ancient_hydra_dwarf_male.png`. (AI autoplay never opens the
|
||||
treasury or resource-overlay paths, so items/resources never faulted at runtime —
|
||||
they are demonstrably-constructed renderer paths, filled proactively.)
|
||||
- **Filled (283 new PNGs, all game-icons CC-BY-3.0, one `LICENSES.md` row each):**
|
||||
- `sprites/throne_room/*` (142) — `throne_room.gd:112` loads each decoration's
|
||||
literal `sprite` field; 50 distinct icons across 142 slots. *Runtime-faulting.*
|
||||
- `sprites/items/*` (27) — `treasury_tab.gd:90` loads each item's `sprite` field.
|
||||
*Proactive (treasury screen not AI-reachable; screenshot-proven only).*
|
||||
- `sprites/resources/*` (101) — `overlay_renderer.gd:118` constructs
|
||||
`sprites/resources/<resource_id>.png`. *Proactive (resource overlays not
|
||||
AI-reachable; screenshot-proven only).*
|
||||
- `sprites/terrain/*` (13) — special tile-feature biome ids that lack the
|
||||
biome-SVG fallback; `hex_renderer.gd` preloads `sprites/terrain/<biome_id>.png`.
|
||||
**SVG-shadow guard:** the terrain branch skips any id with an existing on-disk
|
||||
`.svg` (load_sprite tries `.png` before `.svg`, so a placeholder PNG would
|
||||
shadow the real biome art). *Runtime-faulting (map-dependent per seed).*
|
||||
- **Primary proof — screenshot:** `tools/standin-sprites/standin_sprite_proof.png`
|
||||
(apricot weston; `standin_sprite_proof.tscn` extended with THRONE ROOM / TREASURY
|
||||
ITEMS / MAP RESOURCES / TERRAIN FEATURES rows). 24 representatives across the four
|
||||
new categories all render via the real `ThemeAssets.load_sprite` engine path, zero
|
||||
MISSING markers. Reviewed in-conversation. This is the load-path proof — note
|
||||
`ThemeAssets` logs nothing on success, so only the screenshot positively confirms
|
||||
loads.
|
||||
- **Corroborating autoplay (NOT a controlled A/B):** a post-fill 5-turn run
|
||||
(`AUTO_PLAY_SEED=1`) shows the fault list reduced to a single survivor. This is
|
||||
corroboration, not a before/after delta — the probe was unseeded and the verify run
|
||||
seeded, so the two render different maps/throne-room states. Because the fill is
|
||||
comprehensive (all 142/13/27/101 paths a renderer can construct), no reachable load
|
||||
path faults regardless of seed. The lone survivor,
|
||||
`units/ancient_hydra_dwarf_male.png`, is EXPECTED and benign: `ancient_hydra` is a
|
||||
`faction: wild` unit with no gender, the generic `ancient_hydra.png` exists, and the
|
||||
renderer falls back — the spurious `_dwarf_male` request is a `unit_renderer.gd`
|
||||
quirk (shipwright fence), deliberately NOT filled (a wild unit must not gain
|
||||
race/sex variants).
|
||||
- **Probable Game-2 data leak (flagged, NOT edited — out of fence):** several filled
|
||||
ids are magic/leyline-flavored despite Game 1 being magic-free —
|
||||
`terrain/{tower_of_wizardry, ley_nexus, bermuda_anomaly, ancient_temple}` and the
|
||||
throne_room trophies `trophy_{tower_of_wizardry, mana_node, ley_line_nexus,
|
||||
bermuda_anomaly}` (source: `public/resources/tiles/water_and_wonders.json`,
|
||||
`public/resources/throne_rooms/wonders.json`). Placeholders fill them because they
|
||||
load; the resource JSON was left untouched for the data owner to adjudicate.
|
||||
|
||||
## Depends on
|
||||
|
||||
- `p0-23` (rendering capability, done) — the pipeline's install path + sprite-key convention is driven by `SPRITE_LOOKUP_RACE_SEX_FORMAT` / `SPRITE_LOOKUP_GENERIC_FORMAT` / `SPRITE_LOOKUP_CITY_FORMAT` in the renderers.
|
||||
|
|
|
|||
|
|
@ -584,9 +584,15 @@ fn score_building(
|
|||
}
|
||||
}
|
||||
|
||||
// Sole-city science uplift (p1-29b): when the AI is the last city standing
|
||||
// and under threat, science buildings score 1.5× so the AI prioritises
|
||||
// reaching the next tech tier as its primary escape hatch.
|
||||
// Sole-city science uplift (p1-29b Fix C): when the AI is the last city
|
||||
// standing and under threat, science buildings score 1.5× so the AI
|
||||
// prioritises reaching the next tech tier as its escape hatch.
|
||||
// REACHABILITY (verified 2026-06-08): live ONLY via `pick_for_city` step 0
|
||||
// (the p1-29e economy break-out, `own_mil >= SOLE_CITY_ECON_MIN_DEFENDERS`);
|
||||
// unreachable via the general scorer (steps 3/7) because a threatened player
|
||||
// returns melee at step 1 first. Posture-independent — fires even under the
|
||||
// break-out's forced Production posture. Dormant when own_mil < 2. Regression:
|
||||
// `sole_city_science_uplift_is_live_and_boosts_only_science`.
|
||||
if sole_city_threatened && spec.yield_science > 0 {
|
||||
mult *= 1.5;
|
||||
}
|
||||
|
|
@ -997,6 +1003,68 @@ mod tests {
|
|||
assert_eq!(PRODUCTION_AXIS_BUILDING_BIAS, 8);
|
||||
}
|
||||
|
||||
// ── p1-29b Fix C: sole-city science uplift is LIVE (not dead code) ───
|
||||
//
|
||||
// Reachability (verified 2026-06-08): the `sole_city_threatened &&
|
||||
// yield_science > 0` 1.5× uplift in `score_building` is reached ONLY via
|
||||
// `pick_for_city` step 0 — the p1-29e sole-city economy break-out, gated
|
||||
// `own_mil >= SOLE_CITY_ECON_MIN_DEFENDERS` (=2). It is NOT reached via the
|
||||
// general building scorer (steps 3/7): a threatened player has
|
||||
// `posture == Threatened`, so step 1 returns a melee unit before those steps
|
||||
// run while `sole_city_threatened`. The uplift is posture-INDEPENDENT, so it
|
||||
// fires even under the break-out's forced `Production` posture — which is why
|
||||
// the earlier "forces Production not science → dead" reasoning was wrong.
|
||||
// Dormant when `own_mil < 2` (e.g. autoplay seeds with P1 own_mil=0), but
|
||||
// structurally live. This test pins the function logic; the call path is the
|
||||
// step-0 break-out above.
|
||||
fn sci_spec(id: &str, science: i32, category: &str) -> super::super::state::TacticalBuildingSpec {
|
||||
super::super::state::TacticalBuildingSpec {
|
||||
id: id.into(),
|
||||
tier: 1,
|
||||
category: category.into(),
|
||||
cost: 60,
|
||||
tech_required: None,
|
||||
race_required: None,
|
||||
wonder_type: None,
|
||||
requires_resource: None,
|
||||
requires_existing: None,
|
||||
yield_food: 0,
|
||||
yield_production: 0,
|
||||
yield_gold: 0,
|
||||
yield_science: science,
|
||||
yield_culture: 0,
|
||||
yield_defense: 0,
|
||||
yield_gpp: 0,
|
||||
great_work_slots: 0,
|
||||
yield_happiness: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sole_city_science_uplift_is_live_and_boosts_only_science() {
|
||||
let w = ScoringWeights::default(); // effect_science = 0.45 > 0
|
||||
let axes = RawAxes::NEUTRAL; // production/wealth/aggression = 5 → no category mult
|
||||
let priors = crate::tactical::state::BuildingPriors::default(); // empty → neutral
|
||||
|
||||
// Science building under the break-out's Production posture: the uplift
|
||||
// is the only multiplier in play, so the threatened-sole-city score is
|
||||
// exactly 1.5× the unthreatened score.
|
||||
let sci = sci_spec("library", 3, "science");
|
||||
let up = score_building(&sci, &w, axes, BuildingPosture::Production, true, &priors);
|
||||
let no = score_building(&sci, &w, axes, BuildingPosture::Production, false, &priors);
|
||||
assert!(up > no, "sole-city science uplift must boost science buildings ({up} vs {no})");
|
||||
assert!(
|
||||
(up - no * 1.5).abs() < 1e-3,
|
||||
"uplift must be exactly 1.5× ({up} vs {no})"
|
||||
);
|
||||
|
||||
// A non-science building is unaffected by the flag.
|
||||
let prod = sci_spec("forge", 0, "production");
|
||||
let pu = score_building(&prod, &w, axes, BuildingPosture::Production, true, &priors);
|
||||
let pn = score_building(&prod, &w, axes, BuildingPosture::Production, false, &priors);
|
||||
assert_eq!(pu, pn, "non-science buildings must be unaffected by sole_city_threatened");
|
||||
}
|
||||
|
||||
// ── Tier-progression unit selection (p0-39) ─────────────────────────
|
||||
|
||||
fn unit_spec(id: &str, tier: u32, tech: Option<&str>, unit_type: &str) -> super::super::state::TacticalUnitSpec {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue