docs(sprite-generation): 📝 clarify workflow details in PIPELINE.md for new and existing features

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-28 21:31:40 -07:00
parent ebebb8095d
commit 5887655774

View file

@ -2,156 +2,179 @@
## Overview
A single long-running process (`cli.py run`) continuously moves sprites from `needed``installed` with minimal human intervention. The human's only job is picking winners in the Review GUI.
A single long-running process (`cli.py run`) generates sprites one at a time, scores each immediately, and cycles through the roster until all pass. The human's job is reviewing passing sprites in the Theater GUI and approving winners.
```
┌─────────────────────────────────────────┐
│ ORCHESTRATOR │
│ (cli.py run — daemon) │
│ │
┌────────┐ auto │ ┌──────────┐ auto ┌──────────┐ │
│ SCAN │─────────────▶│ │ GENERATE │──────▶│ RANK │ │
│ │ │ │ model- │ │ (Sonnet) │ │
│ JSON │ │ │ boss GPU │ │ vision │ │
│ → DB │ │ │ 4 var/ea │ │ auto │ │
└────────┘ │ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ │ ┌──────────────┘ │
│ │ │ score < threshold?
│ │ │ yes → back to GENERATE │
│ │ │ no → move to REVIEW │
│ ▼ ▼ │
│ ┌──────────────┐ │
│ │ REVIEW QUEUE │◀── Sonnet-ranked │
│ │ (ready for │ sprites waiting │
│ │ human pick) │ for approval │
│ └──────┬───────┘ │
└─────────┼──────────────────────────────┘
┌────────▼────────┐
│ REVIEW GUI │ ◀── human picks
│ (theater) │ best variant
│ click approve │
└────────┬────────┘
┌────────▼────────┐
│ INSTALL │ auto on approve
│ chroma key rm │
│ resize │
│ → game assets │
│ → manifest DB │
└─────────────────┘
game JSON ──scan──▶ spritegen.db (sprites table — what to generate)
┌──────┴──────┐
│ ORCHESTRATOR │ cli.py run
│ (daemon) │
└──────┬──────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌──────────┐
│GENERATE │ │ RANK │ │ STATUS │
│model-boss│ │ Sonnet │ │ CHECK │
│1 sprite │ │ vision │ │ │
│4 variants│ │ 7 dims │ │ pass? │
└────┬────┘ └────┬────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
raw/*.png spritegen.db status=review (pass)
variant.notes status=needed (fail, retry)
variant.rating max 5 attempts → review anyway
┌──────┴──────┐
│ THEATER GUI │ localhost:5850
│ human picks │
│ approve/skip│
└──────┬──────┘
┌──────┴──────┐
│ INSTALL │ cli.py approve <id>
│ chroma key │
│ resize 256² │
│ → game dir │
└─────────────┘
```
## Orchestrator Loop (`cli.py run`)
```python
while True:
# 1. Pick sprites that need work
needed = get_sprites(status="needed", limit=BATCH_SIZE)
# 2. Generate variants (4 per sprite, with pose reference)
for sprite in needed:
generate_batch([sprite.id], variants_per=4, pose_ref=POSE_REF)
# sprite auto-transitions: needed → review
# 3. Rank newly completed sprites
in_review = get_sprites(status="review", unranked=True)
for sprite in in_review:
result = rank_and_filter(sprite.id)
if result.needs_regen:
# Not enough good variants → back to needed
reset_sprite(sprite.id) # will be re-generated next loop
# 4. Sleep between batches (let GPU breathe)
sleep(30)
```
## State Machine
## Data Model (spritegen.db)
```
needed ──generate──▶ review ──rank──▶ review (ranked)
▲ │
│ │
└── needs_regen ◀── score < threshold
score ≥ threshold
review (ready)
human approve
approved
auto process
+ install
installed
sprites (51) One row per sprite to generate
id TEXT PK "units/spearmen_dwarves_m"
category TEXT "units"
entity_id TEXT "spearmen_dwarves_m"
status TEXT needed → review → approved → installed
prompt TEXT Current prompt template (mutable)
negative_prompt TEXT Current negative (mutable)
install_path TEXT Game asset destination path
gen_width/height Generation resolution (1024×1024)
target_width/height Final sprite size (256×256)
│ 1:N
variants (431+) One row per generated image
id INTEGER PK
sprite_id TEXT FK → sprites
seed INTEGER Reproducible seed
job_status TEXT submitted → completed
raw_path TEXT raw/{sprite_id}_{variant_id}.png
processed_path TEXT variants/{...}.png (after chroma key)
is_approved INTEGER 0 or 1
rating INTEGER 1-5 (from Sonnet), -1 = rejected/skipped
notes TEXT JSON scores {"composition": 0.85, ...}
── immutable generation record ──
model TEXT "juggernaut-xi-v11"
prompt_used TEXT Exact prompt sent to model
negative_used TEXT Exact negative sent
guidance_scale REAL 9.0
steps INTEGER 25
sprite_dimensions (41) Quality/race/gender permutations
sprite_id FK → sprites
dimension_type TEXT "quality"
dimension_value TEXT "q1"
prompt_modifier TEXT Added to base prompt
generation_runs (617) Batch tracking
total_jobs / completed / failed
```
## Key Behaviors
## Orchestrator Loop
### Auto-regeneration
When Sonnet ranks a sprite and fewer than 3 variants pass the confidence threshold, the sprite resets to `needed` and gets 4 more variants on the next loop. Old variants are kept (accumulative). Eventually enough good variants accumulate.
```
cli.py run --category units --variants 4
```
### Pose Reference
All unit generation uses an approved southwest-facing sprite as img2img reference (strength 0.6). This ensures consistent facing direction across the roster. The reference sprite is configured once and used for all unit generation.
Each loop iteration:
1. Pick ONE sprite with `status=needed` (skip if hit 5 regen attempts)
2. Generate 4 variants via model-boss (`MAX_CONCURRENT=1`, sequential)
3. Rank ALL unscored variants for that sprite via Sonnet vision (7 dimensions)
4. If ≥3 variants pass 70% threshold → `status=review`
5. If <3 pass and attempts < 5 keep as `needed` (retry next loop)
6. If attempts = 5 → force to `status=review` (human picks from best available)
7. Sleep 5s, next sprite
### Rate Limiting
- Generator submits requests with retry + exponential backoff (10s, 20s, 40s...)
- model-boss queues internally (1 concurrent diffusion request)
- Orchestrator batches work in small groups (4-8 sprites) with sleep between batches
## Scoring (7 dimensions, 70% threshold)
### Review GUI
- Sprite Theater shows all generated sprites sorted by recency
- Each card shows #ID, entity name, category, Sonnet scores
- Click to open detail → approve winner
- Approve triggers: chroma key removal → resize → install to game assets → manifest DB update
| Dimension | What it checks |
|-----------|---------------|
| facing_direction | Character oriented toward lower-left (southwest) |
| composition | Single character, clean framing, no clutter |
| unit_identity | Correct unit type with appropriate equipment |
| race_accuracy | Correct racial proportions (dwarf = short/stocky) |
| gender_accuracy | Correct gender presentation |
| pose_quality | Full body visible, natural stance |
| background_compliance | Clean green background, no base/tile/ground |
| art_style | 2D hand-painted fantasy game art, not photorealistic |
Confidence = average of all 8 dimensions. Variant passes at ≥70%.
## CLI Commands
```bash
# Full pipeline daemon (scan + generate + rank loop)
./run tools spritegen run
# Full pipeline (generate → rank → regen loop + GUI server)
python3 cli.py run --category units --variants 4
# Individual steps (for debugging)
./run tools spritegen scan --demo
./run tools spritegen generate --category units --variants 4 --pose-ref raw/reference.png
./run tools spritegen rank
./run tools spritegen approve <variant_id>
./run tools spritegen status
# GUI server only
python3 cli.py start --port 5850
# Review GUI
./run tools spritegen start # http://localhost:5850
# Scan game data → populate sprite registry
python3 cli.py --demo scan
# Test prompts without touching DB (rapid iteration)
python3 cli.py test-prompt --entity spearmen --race dwarves --gender male --seeds 42 123 777
# Rebuild all stored prompts from current templates
python3 cli.py refresh-prompts --category units --clear-scores
# Manual operations
python3 cli.py generate --sprite units/spearmen_dwarves_m --variants 8
python3 cli.py rank --sprite units/spearmen_dwarves_m
python3 cli.py approve 129
python3 cli.py status
python3 cli.py reset --sprite units/spearmen_dwarves_m
```
## File Flow
```
game JSON data
game JSON data (demo-data/units/*.json)
sprites.db (pipeline state)
scan
spritegen.db ──── sprites table (what to generate)
├──generate──▶ raw/{sprite_id}_{variant_id}.png (1024×1024, green bg)
│ │
│ rank (Sonnet)
│ │
│ ▼
│ sprites.db (variant.rating, variant.notes)
│ │
│ human approve
│ │
│ ▼
├──process──▶ variants/{sprite_id}_{variant_id}.png (256×256, transparent)
│ │
│ install
│ │
│ ▼
├──install──▶ games/age-of-four/assets/sprites/units/{id}_{race}_{g}.png
▼ generate (model-boss → juggernaut-xi-v11)
raw/{sprite_id}_{variant_id}.png ──── 1024×1024, green bg
└──manifest─▶ games/age-of-four/data/sprites.db (game runtime manifest)
▼ rank (Sonnet vision, 7 dimensions)
spritegen.db ──── variants table (scores in notes JSON)
▼ human approve (Theater GUI or cli.py approve)
spritegen.db ──── variant.is_approved = 1
▼ process (chroma key removal + resize)
variants/{sprite_id}_{variant_id}.png ──── 256×256, transparent
▼ install
games/age-of-dwarves/assets/sprites/units/{name}.png
```
## Configuration
```json
// sprite-config.json
{
"model": "juggernaut-xi-v11",
"api_base": "http://localhost:8210",
"defaults": {
"steps": 25,
"guidance_scale": 9.0
}
}
```
Prompt templates live in `engine/prompts.py`. The `compose_prompt()` function builds the full prompt from: style prefix + race/gender + entity description + style tail.