@mcptoolshop/style-dataset-lab 2.3.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +178 -0
- package/CODE_OF_CONDUCT.md +9 -0
- package/CONTRIBUTING.md +71 -0
- package/README.md +82 -9
- package/SECURITY.md +6 -5
- package/bin/sdlab.js +359 -11
- package/lib/adapters/comfyui-runner.js +170 -0
- package/lib/adapters/comfyui-workflows.js +163 -0
- package/lib/adapters/diffusers-lora.js +7 -2
- package/lib/adapters/generic-image-caption.js +6 -1
- package/lib/args.js +200 -13
- package/lib/batch-compiler.js +122 -0
- package/lib/batch-modes.js +111 -0
- package/lib/batch-runs.js +237 -0
- package/lib/batch-sheet-render.js +209 -0
- package/lib/brief-compiler.js +625 -0
- package/lib/brief-render.js +205 -0
- package/lib/card.js +11 -4
- package/lib/comfyui.js +100 -6
- package/lib/config.js +68 -3
- package/lib/critique-engine.js +413 -0
- package/lib/critique-render.js +171 -0
- package/lib/deprecation.js +1 -1
- package/lib/eval-pack.js +94 -4
- package/lib/eval-runs.js +42 -3
- package/lib/export.js +144 -34
- package/lib/generated-provenance.js +87 -0
- package/lib/implementation-packs.js +15 -1
- package/lib/log.js +41 -4
- package/lib/paths.js +72 -3
- package/lib/refine-briefs.js +336 -0
- package/lib/reingest-selected.js +184 -0
- package/lib/reingest.js +15 -1
- package/lib/run-summary.js +132 -0
- package/lib/runtime-runs.js +218 -0
- package/lib/selections.js +375 -0
- package/lib/snapshot.js +100 -7
- package/lib/split.js +175 -17
- package/lib/training-manifests.js +17 -3
- package/lib/training-packages.js +28 -14
- package/lib/workflow-profiles.js +161 -0
- package/package.json +13 -4
- package/scripts/batch-generate.js +176 -0
- package/scripts/batch-sheet.js +46 -0
- package/scripts/batch-show.js +86 -0
- package/scripts/brief-compile.js +134 -0
- package/scripts/brief-show.js +53 -0
- package/scripts/canon-bind.js +7 -9
- package/scripts/card.js +22 -25
- package/scripts/compare.js +38 -33
- package/scripts/critique-show.js +50 -0
- package/scripts/critique.js +70 -0
- package/scripts/curate.js +91 -46
- package/scripts/doctor.js +172 -6
- package/scripts/eligibility.js +17 -14
- package/scripts/eval-pack.js +13 -11
- package/scripts/eval-run.js +30 -29
- package/scripts/export.js +27 -31
- package/scripts/generate-controlnet.js +31 -17
- package/scripts/generate-identity.js +37 -20
- package/scripts/generate-ipadapter.js +22 -16
- package/scripts/generate.js +68 -13
- package/scripts/implementation-pack.js +19 -14
- package/scripts/init.js +137 -17
- package/scripts/painterly.js +8 -10
- package/scripts/refine.js +119 -0
- package/scripts/reingest-selected.js +78 -0
- package/scripts/reingest.js +37 -18
- package/scripts/run-generate.js +76 -0
- package/scripts/run-list.js +58 -0
- package/scripts/run-show.js +63 -0
- package/scripts/select.js +119 -0
- package/scripts/selection-show.js +88 -0
- package/scripts/snapshot.js +22 -24
- package/scripts/split.js +26 -31
- package/scripts/training-manifest.js +29 -24
- package/scripts/training-package.js +24 -20
- package/scripts/training-profile.js +13 -8
- package/scripts/workflow-list.js +61 -0
- package/scripts/workflow-show.js +97 -0
- package/templates/domains/architecture/inputs/prompts/example-wave.json +32 -0
- package/templates/domains/architecture/terminology.json +10 -8
- package/templates/domains/architecture/workflows/profiles/environment-moodboard.json +62 -0
- package/templates/domains/architecture/workflows/profiles/interior-variant-pack.json +60 -0
- package/templates/domains/character-design/inputs/prompts/example-wave.json +32 -0
- package/templates/domains/character-design/project.json +4 -2
- package/templates/domains/character-design/workflows/profiles/character-portrait-set.json +61 -0
- package/templates/domains/character-design/workflows/profiles/expression-sheet.json +59 -0
- package/templates/domains/creature-design/inputs/prompts/example-wave.json +32 -0
- package/templates/domains/creature-design/project.json +4 -2
- package/templates/domains/creature-design/terminology.json +1 -0
- package/templates/domains/creature-design/workflows/profiles/creature-portrait-set.json +61 -0
- package/templates/domains/creature-design/workflows/profiles/silhouette-exploration.json +58 -0
- package/templates/domains/game-art/inputs/prompts/example-wave.json +37 -0
- package/templates/domains/game-art/project.json +4 -2
- package/templates/domains/game-art/workflows/profiles/character-portrait-set.json +62 -0
- package/templates/domains/game-art/workflows/profiles/environment-moodboard.json +61 -0
- package/templates/domains/vehicle-mech/inputs/prompts/example-wave.json +37 -0
- package/templates/domains/vehicle-mech/project.json +4 -2
- package/templates/domains/vehicle-mech/workflows/profiles/variant-pack.json +60 -0
- package/templates/domains/vehicle-mech/workflows/profiles/vehicle-silhouette-pack.json +59 -0
- package/scripts/bulk-curate-wave2-5.js +0 -138
- package/scripts/bulk-curate-waves11-18.js +0 -413
- package/scripts/curate-wave25.js +0 -209
- package/scripts/migrate-records.js +0 -141
- package/templates/inputs/prompts/example-wave.json +0 -27
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Bulk curation for waves 11-18 (expanded categories).
|
|
4
|
-
*
|
|
5
|
-
* Strategy:
|
|
6
|
-
* - REJECT* subjects → rejected with specific explanations
|
|
7
|
-
* - All other subjects → approved with category-appropriate scores
|
|
8
|
-
* - Score profiles differ by category (ships, interiors, equipment, etc.)
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* node scripts/bulk-curate-waves11-18.js
|
|
12
|
-
* node scripts/bulk-curate-waves11-18.js --dry-run
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { readFileSync, writeFileSync, readdirSync, renameSync, existsSync, mkdirSync } from 'fs';
|
|
16
|
-
import { join, basename } from 'path';
|
|
17
|
-
|
|
18
|
-
const REPO_ROOT = new URL("..", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
|
|
19
|
-
const GAME = process.argv.find((a, i) => process.argv[i - 1] === '--game') || 'star-freight';
|
|
20
|
-
const GAME_ROOT = join(REPO_ROOT, 'projects', GAME);
|
|
21
|
-
|
|
22
|
-
const RECORDS_DIR = join(GAME_ROOT, 'records');
|
|
23
|
-
const CANDIDATES_DIR = join(GAME_ROOT, 'outputs', 'candidates');
|
|
24
|
-
const APPROVED_DIR = join(GAME_ROOT, 'outputs', 'approved');
|
|
25
|
-
const REJECTED_DIR = join(GAME_ROOT, 'outputs', 'rejected');
|
|
26
|
-
const DRY_RUN = process.argv.includes('--dry-run');
|
|
27
|
-
|
|
28
|
-
// Ensure output dirs exist
|
|
29
|
-
for (const dir of [APPROVED_DIR, REJECTED_DIR]) {
|
|
30
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ─── Rejection explanations ──────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
const REJECT_EXPLANATIONS = {
|
|
36
|
-
// Wave 11 rejects
|
|
37
|
-
REJECT11_star_destroyer: {
|
|
38
|
-
explanation: "Massive gleaming triangular warship — Imperial Star Destroyer aesthetic. Too clean, too military-imperial, wrong power scale for gritty space.",
|
|
39
|
-
failures: ["generic_scifi", "too_clean"],
|
|
40
|
-
scores: { silhouette_clarity: 0.9, palette_adherence: 0.2, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.4, clothing_logic: 0.3, composition: 0.8 }
|
|
41
|
-
},
|
|
42
|
-
REJECT11_luxury_yacht: {
|
|
43
|
-
explanation: "Sleek luxury spacecraft with chrome and panoramic windows. Rich person's toy, not working vehicle. Violates every grit principle.",
|
|
44
|
-
failures: ["too_clean", "costume_not_clothes"],
|
|
45
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.7 }
|
|
46
|
-
},
|
|
47
|
-
REJECT11_anime_ship: {
|
|
48
|
-
explanation: "Colorful exaggerated spacecraft with anime proportions and racing stripes. Wrong aesthetic entirely — cel-shaded toy, not working vessel.",
|
|
49
|
-
failures: ["anime_style", "wrong_palette"],
|
|
50
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.1, clothing_logic: 0.2, composition: 0.6 }
|
|
51
|
-
},
|
|
52
|
-
// Wave 12 rejects
|
|
53
|
-
REJECT12_star_trek_bridge: {
|
|
54
|
-
explanation: "Sleek bright bridge with holographic displays and pristine surfaces. Star Trek aesthetic — too clean, too advanced, too comfortable.",
|
|
55
|
-
failures: ["generic_scifi", "too_clean"],
|
|
56
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.2, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.2, composition: 0.7 }
|
|
57
|
-
},
|
|
58
|
-
REJECT12_luxury_cabin: {
|
|
59
|
-
explanation: "Spacious luxury cabin with king bed, silk sheets, panoramic viewport. Five-star hotel in space, not crew quarters.",
|
|
60
|
-
failures: ["too_clean", "costume_not_clothes"],
|
|
61
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.6 }
|
|
62
|
-
},
|
|
63
|
-
REJECT12_holodeck: {
|
|
64
|
-
explanation: "Empty room with glowing grid lines — holodeck concept. Pure Star Trek, no grounded reality, no lived-in quality.",
|
|
65
|
-
failures: ["generic_scifi"],
|
|
66
|
-
scores: { silhouette_clarity: 0.3, palette_adherence: 0.2, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.5 }
|
|
67
|
-
},
|
|
68
|
-
// Wave 13 rejects
|
|
69
|
-
REJECT13_clean_promenade: {
|
|
70
|
-
explanation: "Pristine shopping promenade with luxury storefronts and gleaming floors. High-end mall in space, not working station.",
|
|
71
|
-
failures: ["too_clean", "generic_scifi"],
|
|
72
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.7 }
|
|
73
|
-
},
|
|
74
|
-
REJECT13_resort_station: {
|
|
75
|
-
explanation: "Luxury resort with infinity pool and spa. Tourist brochure for space, not gritty lived-in station.",
|
|
76
|
-
failures: ["too_clean", "costume_not_clothes"],
|
|
77
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.6 }
|
|
78
|
-
},
|
|
79
|
-
REJECT13_anime_mall: {
|
|
80
|
-
explanation: "Colorful station with neon signs and cute mascots. Anime space mall aesthetic — wrong style entirely.",
|
|
81
|
-
failures: ["anime_style", "wrong_palette"],
|
|
82
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.1, clothing_logic: 0.1, composition: 0.5 }
|
|
83
|
-
},
|
|
84
|
-
// Wave 14 rejects
|
|
85
|
-
REJECT14_lightsaber: {
|
|
86
|
-
explanation: "Elegant glowing energy sword — lightsaber aesthetic. Star Wars iconic weapon, not grounded faction equipment.",
|
|
87
|
-
failures: ["generic_scifi"],
|
|
88
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.2, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.2, composition: 0.8 }
|
|
89
|
-
},
|
|
90
|
-
REJECT14_anime_weapon: {
|
|
91
|
-
explanation: "Oversized fantasy weapon with glowing runes — anime aesthetic. Impossible proportions, wrong genre.",
|
|
92
|
-
failures: ["anime_style", "generic_scifi"],
|
|
93
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.1, clothing_logic: 0.1, composition: 0.6 }
|
|
94
|
-
},
|
|
95
|
-
REJECT14_chrome_gun: {
|
|
96
|
-
explanation: "Perfectly polished chrome handgun with LED accents. Product advertisement, no wear or history, not a working weapon.",
|
|
97
|
-
failures: ["too_clean"],
|
|
98
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.2, material_fidelity: 0.3, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.4, clothing_logic: 0.2, composition: 0.8 }
|
|
99
|
-
},
|
|
100
|
-
// Wave 15 rejects
|
|
101
|
-
REJECT15_modern_ui: {
|
|
102
|
-
explanation: "Flat Material Design UI mockup. Contemporary smartphone aesthetic, not sci-fi, not painterly.",
|
|
103
|
-
failures: ["generic_scifi"],
|
|
104
|
-
scores: { silhouette_clarity: 0.4, palette_adherence: 0.2, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.1, clothing_logic: 0.1, composition: 0.5 }
|
|
105
|
-
},
|
|
106
|
-
REJECT15_neon_sign: {
|
|
107
|
-
explanation: "Vivid cyberpunk neon tubes. Too bright, too colorful, too urban — nightclub not spaceship.",
|
|
108
|
-
failures: ["wrong_palette", "generic_scifi"],
|
|
109
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.1, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.6 }
|
|
110
|
-
},
|
|
111
|
-
REJECT15_fantasy_runes: {
|
|
112
|
-
explanation: "Glowing magical runes on stone — Lord of the Rings aesthetic. Fantasy, not sci-fi. Wrong genre entirely.",
|
|
113
|
-
failures: ["generic_scifi"],
|
|
114
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.2, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.5 }
|
|
115
|
-
},
|
|
116
|
-
// Wave 16 rejects
|
|
117
|
-
REJECT16_bright_clean: {
|
|
118
|
-
explanation: "Brightly lit pristine white corridor — modern office aesthetic. Too clean, too comfortable, no grit.",
|
|
119
|
-
failures: ["too_clean"],
|
|
120
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.6 }
|
|
121
|
-
},
|
|
122
|
-
REJECT16_sunset_beauty: {
|
|
123
|
-
explanation: "Beautiful golden sunset through panoramic window. Tourist brochure lighting, romantic and scenic, not gritty or tense.",
|
|
124
|
-
failures: ["too_clean", "generic_scifi"],
|
|
125
|
-
scores: { silhouette_clarity: 0.4, palette_adherence: 0.2, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.5 }
|
|
126
|
-
},
|
|
127
|
-
// Wave 17 rejects
|
|
128
|
-
REJECT17_sleek_hovervehicle: {
|
|
129
|
-
explanation: "Chrome luxury hover transport — concept car aesthetic. No dirt, no wear, wrong power fantasy.",
|
|
130
|
-
failures: ["too_clean", "generic_scifi"],
|
|
131
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.7 }
|
|
132
|
-
},
|
|
133
|
-
REJECT17_military_tank: {
|
|
134
|
-
explanation: "Modern Earth military tank. Wrong genre — ground combat vehicle from present day, not lived-in sci-fi.",
|
|
135
|
-
failures: ["generic_scifi"],
|
|
136
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.3, material_fidelity: 0.4, faction_read: 0.0, wear_level: 0.5, style_consistency: 0.4, clothing_logic: 0.2, composition: 0.7 }
|
|
137
|
-
},
|
|
138
|
-
REJECT17_golden_crate: {
|
|
139
|
-
explanation: "Ornate golden treasure chest with jewels. Fantasy RPG loot, not cargo container. Wrong genre.",
|
|
140
|
-
failures: ["generic_scifi", "wrong_palette"],
|
|
141
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.0, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.7 }
|
|
142
|
-
},
|
|
143
|
-
// Wave 18 rejects
|
|
144
|
-
REJECT18_cute_pet: {
|
|
145
|
-
explanation: "Adorable fluffy alien pet with sparkly eyes. Kawaii mascot, not ship vermin or working animal. Too cute for gritty space.",
|
|
146
|
-
failures: ["anime_style", "generic_scifi"],
|
|
147
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.1, clothing_logic: 0.1, composition: 0.7 }
|
|
148
|
-
},
|
|
149
|
-
REJECT18_fantasy_feast: {
|
|
150
|
-
explanation: "Medieval fantasy banquet with golden platters and crystal goblets. King's feast, not crew rations. Wrong genre.",
|
|
151
|
-
failures: ["generic_scifi", "too_clean"],
|
|
152
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.6 }
|
|
153
|
-
},
|
|
154
|
-
REJECT18_gem_artifact: {
|
|
155
|
-
explanation: "Glowing magical gemstone in golden setting with runes. Fantasy RPG artifact, not sci-fi equipment. Wrong genre.",
|
|
156
|
-
failures: ["generic_scifi", "wrong_palette"],
|
|
157
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.0, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.7 }
|
|
158
|
-
},
|
|
159
|
-
// Wave 19 rejects
|
|
160
|
-
REJECT19_fantasy_castle: {
|
|
161
|
-
explanation: "Medieval fantasy castle with spires and buttresses. Wrong genre — Lord of the Rings, not lived-in sci-fi.",
|
|
162
|
-
failures: ["generic_scifi"],
|
|
163
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.2, material_fidelity: 0.3, faction_read: 0.0, wear_level: 0.3, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.7 }
|
|
164
|
-
},
|
|
165
|
-
REJECT19_modern_office: {
|
|
166
|
-
explanation: "Contemporary office interior with glass partitions and ergonomic chairs. Modern Earth, not sci-fi, not gritty.",
|
|
167
|
-
failures: ["generic_scifi", "too_clean"],
|
|
168
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.6 }
|
|
169
|
-
},
|
|
170
|
-
REJECT19_art_nouveau: {
|
|
171
|
-
explanation: "Art Nouveau building with flowing organic curves and decorative ironwork. Beautiful and delicate — too pretty for any gritty space faction.",
|
|
172
|
-
failures: ["too_clean", "costume_not_clothes"],
|
|
173
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.2, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.7 }
|
|
174
|
-
},
|
|
175
|
-
// Wave 20 rejects
|
|
176
|
-
REJECT20_luxury_item: {
|
|
177
|
-
explanation: "Chrome designer coffee machine with LED accents. Luxury kitchen showroom, not a working ship's galley.",
|
|
178
|
-
failures: ["too_clean", "generic_scifi"],
|
|
179
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.7 }
|
|
180
|
-
},
|
|
181
|
-
REJECT20_modern_gadget: {
|
|
182
|
-
explanation: "Pristine modern smartphone-like device. Contemporary Apple aesthetic — too clean, too now, wrong era.",
|
|
183
|
-
failures: ["too_clean", "generic_scifi"],
|
|
184
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.7 }
|
|
185
|
-
},
|
|
186
|
-
// Wave 21 rejects
|
|
187
|
-
REJECT21_paradise_planet: {
|
|
188
|
-
explanation: "Beautiful alien paradise with crystal waterfalls and twin suns. Tourist brochure, not gritty frontier.",
|
|
189
|
-
failures: ["too_clean", "generic_scifi"],
|
|
190
|
-
scores: { silhouette_clarity: 0.4, palette_adherence: 0.1, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.5 }
|
|
191
|
-
},
|
|
192
|
-
REJECT21_clean_colony: {
|
|
193
|
-
explanation: "Pristine futuristic colony with gleaming white domes. Utopian settlement — the brochure, not the reality.",
|
|
194
|
-
failures: ["too_clean"],
|
|
195
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.6 }
|
|
196
|
-
},
|
|
197
|
-
// Wave 22 rejects
|
|
198
|
-
REJECT22_clean_surface: {
|
|
199
|
-
explanation: "Factory-new brushed aluminum with even satin finish. No wear, no history, showroom quality.",
|
|
200
|
-
failures: ["too_clean"],
|
|
201
|
-
scores: { silhouette_clarity: 0.3, palette_adherence: 0.2, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.5 }
|
|
202
|
-
},
|
|
203
|
-
REJECT22_ornate_surface: {
|
|
204
|
-
explanation: "Gold-leafed surface with filigree and gemstone inlays. Luxury craftsmanship, wrong for any working environment.",
|
|
205
|
-
failures: ["too_clean", "wrong_palette"],
|
|
206
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.0, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.2, clothing_logic: 0.1, composition: 0.6 }
|
|
207
|
-
},
|
|
208
|
-
// Wave 23 rejects
|
|
209
|
-
REJECT23_rubber_suit_alien: {
|
|
210
|
-
explanation: "Humanoid alien that is clearly a human in makeup. Star Trek rubber-forehead aesthetic. Human body plan with decorative ridges. Not truly alien.",
|
|
211
|
-
failures: ["generic_scifi"],
|
|
212
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.3, material_fidelity: 0.3, faction_read: 0.1, wear_level: 0.3, style_consistency: 0.4, clothing_logic: 0.3, composition: 0.7 }
|
|
213
|
-
},
|
|
214
|
-
REJECT23_cute_alien: {
|
|
215
|
-
explanation: "Adorable big-eyed alien with pastel colors and baby proportions. Kawaii mascot designed to sell toys, not inhabit a gritty universe.",
|
|
216
|
-
failures: ["anime_style", "generic_scifi"],
|
|
217
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.1, clothing_logic: 0.1, composition: 0.7 }
|
|
218
|
-
},
|
|
219
|
-
REJECT24_catalog_interior: {
|
|
220
|
-
explanation: "IKEA-style staged living space with coordinated furniture and throw pillows. Contemporary lifestyle catalog, not a working ship or station.",
|
|
221
|
-
failures: ["too_clean", "generic_scifi"],
|
|
222
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.1, material_fidelity: 0.1, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.6 }
|
|
223
|
-
},
|
|
224
|
-
REJECT24_museum_clean: {
|
|
225
|
-
explanation: "Pristine museum exhibition with spotlighting and polished floors. Everything preserved and untouchable — the opposite of lived-in.",
|
|
226
|
-
failures: ["too_clean"],
|
|
227
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.2, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.1, composition: 0.6 }
|
|
228
|
-
},
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// ─── Category-specific score profiles ────────────────────────────────
|
|
232
|
-
|
|
233
|
-
const CATEGORY_SCORES = {
|
|
234
|
-
ship_ext: {
|
|
235
|
-
explanation: "Ship exterior with faction-appropriate hull design, wear, and construction.",
|
|
236
|
-
scores: { silhouette_clarity: 0.85, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.8, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.85 }
|
|
237
|
-
},
|
|
238
|
-
int: {
|
|
239
|
-
explanation: "Ship interior with lived-in detail, faction-specific design language, and atmospheric lighting.",
|
|
240
|
-
scores: { silhouette_clarity: 0.75, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.8, wear_level: 0.85, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.8 }
|
|
241
|
-
},
|
|
242
|
-
stn: {
|
|
243
|
-
explanation: "Station/port environment with gritty atmosphere, faction presence, and lived-in texture.",
|
|
244
|
-
scores: { silhouette_clarity: 0.75, palette_adherence: 0.8, material_fidelity: 0.8, faction_read: 0.75, wear_level: 0.85, style_consistency: 0.7, clothing_logic: 0.75, composition: 0.8 }
|
|
245
|
-
},
|
|
246
|
-
eqp: {
|
|
247
|
-
explanation: "Equipment/weapon with faction-specific design language, wear marks, and functional design.",
|
|
248
|
-
scores: { silhouette_clarity: 0.85, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.85, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.85, composition: 0.85 }
|
|
249
|
-
},
|
|
250
|
-
prop: {
|
|
251
|
-
explanation: "Environmental prop with faction identity or universal grit, showing use and history.",
|
|
252
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.75, material_fidelity: 0.8, faction_read: 0.7, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.75, composition: 0.85 }
|
|
253
|
-
},
|
|
254
|
-
sign: {
|
|
255
|
-
explanation: "Signage/UI/logo with faction-specific graphic design, appropriate weathering.",
|
|
256
|
-
scores: { silhouette_clarity: 0.85, palette_adherence: 0.85, material_fidelity: 0.8, faction_read: 0.85, wear_level: 0.75, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.85 }
|
|
257
|
-
},
|
|
258
|
-
mood: {
|
|
259
|
-
explanation: "Lighting/mood study with strong atmospheric character appropriate to gritty space.",
|
|
260
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.85, material_fidelity: 0.8, faction_read: 0.6, wear_level: 0.85, style_consistency: 0.75, clothing_logic: 0.7, composition: 0.8 }
|
|
261
|
-
},
|
|
262
|
-
cargo: {
|
|
263
|
-
explanation: "Cargo/container with faction-specific construction, labeling, and wear.",
|
|
264
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.75, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.85 }
|
|
265
|
-
},
|
|
266
|
-
ind: {
|
|
267
|
-
explanation: "Industrial scene with working-class space atmosphere, heavy equipment, grime.",
|
|
268
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.7, wear_level: 0.9, style_consistency: 0.7, clothing_logic: 0.75, composition: 0.8 }
|
|
269
|
-
},
|
|
270
|
-
veh: {
|
|
271
|
-
explanation: "Ground vehicle with utilitarian design, wear, and faction-appropriate construction.",
|
|
272
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.8, material_fidelity: 0.8, faction_read: 0.75, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.85 }
|
|
273
|
-
},
|
|
274
|
-
food: {
|
|
275
|
-
explanation: "Food/drink item showing faction food culture, appropriate presentation, lived-in quality.",
|
|
276
|
-
scores: { silhouette_clarity: 0.75, palette_adherence: 0.75, material_fidelity: 0.8, faction_read: 0.7, wear_level: 0.75, style_consistency: 0.7, clothing_logic: 0.7, composition: 0.85 }
|
|
277
|
-
},
|
|
278
|
-
artifact: {
|
|
279
|
-
explanation: "Cultural artifact with deep faction identity, appropriate materials and aging.",
|
|
280
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.85, material_fidelity: 0.85, faction_read: 0.9, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.85, composition: 0.85 }
|
|
281
|
-
},
|
|
282
|
-
creature: {
|
|
283
|
-
explanation: "Creature/animal fitting the gritty space universe — working animal, vermin, or alien fauna.",
|
|
284
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.75, material_fidelity: 0.8, faction_read: 0.6, wear_level: 0.7, style_consistency: 0.7, clothing_logic: 0.7, composition: 0.85 }
|
|
285
|
-
},
|
|
286
|
-
arch: {
|
|
287
|
-
explanation: "Faction architecture with species-appropriate ergonomics, material vocabulary, and lived-in detail.",
|
|
288
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.85, wear_level: 0.85, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.8 }
|
|
289
|
-
},
|
|
290
|
-
env: {
|
|
291
|
-
explanation: "Planetary/environmental scene with functional gritty atmosphere, faction presence or universal frontier quality.",
|
|
292
|
-
scores: { silhouette_clarity: 0.75, palette_adherence: 0.8, material_fidelity: 0.8, faction_read: 0.7, wear_level: 0.85, style_consistency: 0.7, clothing_logic: 0.75, composition: 0.8 }
|
|
293
|
-
},
|
|
294
|
-
surf: {
|
|
295
|
-
explanation: "Surface texture/damage study showing the visual vocabulary of wear that defines gritty space.",
|
|
296
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.8, material_fidelity: 0.9, faction_read: 0.7, wear_level: 0.9, style_consistency: 0.7, clothing_logic: 0.75, composition: 0.8 }
|
|
297
|
-
},
|
|
298
|
-
life: {
|
|
299
|
-
explanation: "Alien species cultural moment — daily life, ceremony, or cross-species interaction that reveals who they are as people.",
|
|
300
|
-
scores: { silhouette_clarity: 0.8, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.85, wear_level: 0.8, style_consistency: 0.7, clothing_logic: 0.8, composition: 0.8 }
|
|
301
|
-
},
|
|
302
|
-
lived: {
|
|
303
|
-
explanation: "Lived-in detail — domestic objects in industrial spaces, paper ephemera, mundane commercial spaces, weathering progression, fabric on machinery. The details that make a universe breathe.",
|
|
304
|
-
scores: { silhouette_clarity: 0.75, palette_adherence: 0.8, material_fidelity: 0.85, faction_read: 0.65, wear_level: 0.9, style_consistency: 0.7, clothing_logic: 0.75, composition: 0.8 }
|
|
305
|
-
},
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
function detectCategory(id) {
|
|
309
|
-
if (/^ship_ext_/.test(id)) return 'ship_ext';
|
|
310
|
-
if (/^int_/.test(id)) return 'int';
|
|
311
|
-
if (/^stn_/.test(id)) return 'stn';
|
|
312
|
-
if (/^eqp_/.test(id)) return 'eqp';
|
|
313
|
-
if (/^prop_/.test(id)) return 'prop';
|
|
314
|
-
if (/^sign_/.test(id)) return 'sign';
|
|
315
|
-
if (/^mood_/.test(id)) return 'mood';
|
|
316
|
-
if (/^cargo_/.test(id)) return 'cargo';
|
|
317
|
-
if (/^ind_/.test(id)) return 'ind';
|
|
318
|
-
if (/^veh_/.test(id)) return 'veh';
|
|
319
|
-
if (/^food_/.test(id)) return 'food';
|
|
320
|
-
if (/^artifact_/.test(id)) return 'artifact';
|
|
321
|
-
if (/^creature_/.test(id)) return 'creature';
|
|
322
|
-
if (/^arch_/.test(id)) return 'arch';
|
|
323
|
-
if (/^env_/.test(id)) return 'env';
|
|
324
|
-
if (/^surf_/.test(id)) return 'surf';
|
|
325
|
-
if (/^life_/.test(id)) return 'life';
|
|
326
|
-
if (/^lived_/.test(id)) return 'lived';
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ─── Main ────────────────────────────────────────────────────────────
|
|
331
|
-
|
|
332
|
-
const newRecordPattern = /^(ship_ext_|int_|stn_|eqp_|prop_|sign_|mood_|cargo_|ind_|veh_|food_|artifact_|creature_|arch_|env_|surf_|life_|lived_|REJECT1[1-9]|REJECT2[0-9])/;
|
|
333
|
-
const files = readdirSync(RECORDS_DIR)
|
|
334
|
-
.filter(f => f.endsWith('.json') && newRecordPattern.test(f));
|
|
335
|
-
|
|
336
|
-
let approved = 0, rejected = 0, skipped = 0;
|
|
337
|
-
|
|
338
|
-
for (const file of files) {
|
|
339
|
-
const path = join(RECORDS_DIR, file);
|
|
340
|
-
const record = JSON.parse(readFileSync(path, 'utf-8'));
|
|
341
|
-
|
|
342
|
-
// Skip already curated
|
|
343
|
-
if (record.judgment && record.judgment.status) {
|
|
344
|
-
skipped++;
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const id = record.id;
|
|
349
|
-
const baseId = id.replace(/_v[12]$/, '');
|
|
350
|
-
const isReject = /^REJECT/.test(id);
|
|
351
|
-
const imgFile = `${id}.png`;
|
|
352
|
-
const srcPath = join(CANDIDATES_DIR, imgFile);
|
|
353
|
-
|
|
354
|
-
if (isReject) {
|
|
355
|
-
// Find matching reject explanation
|
|
356
|
-
const rejectInfo = REJECT_EXPLANATIONS[baseId];
|
|
357
|
-
if (!rejectInfo) {
|
|
358
|
-
console.log(` SKIP ${id} — no reject explanation found for ${baseId}`);
|
|
359
|
-
skipped++;
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
record.judgment = {
|
|
364
|
-
status: 'rejected',
|
|
365
|
-
reviewer: 'bulk_curate_v2',
|
|
366
|
-
reviewed_at: new Date().toISOString(),
|
|
367
|
-
explanation: rejectInfo.explanation,
|
|
368
|
-
criteria_scores: { ...rejectInfo.scores },
|
|
369
|
-
failure_modes: [...rejectInfo.failures],
|
|
370
|
-
improvement_notes: null,
|
|
371
|
-
confidence: 0.95,
|
|
372
|
-
};
|
|
373
|
-
record.asset_path = `outputs/rejected/${imgFile}`;
|
|
374
|
-
|
|
375
|
-
if (!DRY_RUN) {
|
|
376
|
-
writeFileSync(path, JSON.stringify(record, null, 2) + '\n');
|
|
377
|
-
if (existsSync(srcPath)) {
|
|
378
|
-
renameSync(srcPath, join(REJECTED_DIR, imgFile));
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
rejected++;
|
|
382
|
-
} else {
|
|
383
|
-
// Approved — detect category for score profile
|
|
384
|
-
const category = detectCategory(id);
|
|
385
|
-
const profile = category ? CATEGORY_SCORES[category] : CATEGORY_SCORES.prop;
|
|
386
|
-
|
|
387
|
-
record.judgment = {
|
|
388
|
-
status: 'approved',
|
|
389
|
-
reviewer: 'bulk_curate_v2',
|
|
390
|
-
reviewed_at: new Date().toISOString(),
|
|
391
|
-
explanation: profile.explanation,
|
|
392
|
-
criteria_scores: { ...profile.scores },
|
|
393
|
-
failure_modes: [],
|
|
394
|
-
improvement_notes: null,
|
|
395
|
-
confidence: 0.75,
|
|
396
|
-
};
|
|
397
|
-
record.asset_path = `outputs/approved/${imgFile}`;
|
|
398
|
-
|
|
399
|
-
if (!DRY_RUN) {
|
|
400
|
-
writeFileSync(path, JSON.stringify(record, null, 2) + '\n');
|
|
401
|
-
if (existsSync(srcPath)) {
|
|
402
|
-
renameSync(srcPath, join(APPROVED_DIR, imgFile));
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
approved++;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
console.log(`\n═══ Bulk Curation ${DRY_RUN ? '(DRY RUN) ' : ''}Summary ═══`);
|
|
410
|
-
console.log(`Files scanned: ${files.length}`);
|
|
411
|
-
console.log(`Approved: ${approved}`);
|
|
412
|
-
console.log(`Rejected: ${rejected}`);
|
|
413
|
-
console.log(`Skipped (already curated): ${skipped}`);
|
package/scripts/curate-wave25.js
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Curation for wave 25 (expanded universe).
|
|
4
|
-
*
|
|
5
|
-
* Based on visual review of all 60 candidates:
|
|
6
|
-
* - REJECT25* subjects → rejected (anti-examples)
|
|
7
|
-
* - Strong hits → approved with high scores
|
|
8
|
-
* - Partial hits → approved with moderate scores + notes
|
|
9
|
-
* - Misses → rejected with explanations of what went wrong
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* node scripts/curate-wave25.js
|
|
13
|
-
* node scripts/curate-wave25.js --dry-run
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { readFileSync, writeFileSync, readdirSync, renameSync, existsSync, mkdirSync } from 'fs';
|
|
17
|
-
import { join, basename } from 'path';
|
|
18
|
-
|
|
19
|
-
const REPO_ROOT = new URL("..", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
|
|
20
|
-
const GAME = process.argv.find((a, i) => process.argv[i - 1] === '--game') || 'star-freight';
|
|
21
|
-
const GAME_ROOT = join(REPO_ROOT, 'projects', GAME);
|
|
22
|
-
|
|
23
|
-
const RECORDS_DIR = join(GAME_ROOT, 'records');
|
|
24
|
-
const CANDIDATES_DIR = join(GAME_ROOT, 'outputs', 'candidates');
|
|
25
|
-
const APPROVED_DIR = join(GAME_ROOT, 'outputs', 'approved');
|
|
26
|
-
const REJECTED_DIR = join(GAME_ROOT, 'outputs', 'rejected');
|
|
27
|
-
const DRY_RUN = process.argv.includes('--dry-run');
|
|
28
|
-
|
|
29
|
-
for (const dir of [APPROVED_DIR, REJECTED_DIR]) {
|
|
30
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ─── Scoring profiles ──────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
const STRONG_HIT = {
|
|
36
|
-
silhouette_clarity: 0.85, palette_adherence: 0.80, material_fidelity: 0.80,
|
|
37
|
-
faction_read: 0.75, wear_level: 0.85, style_consistency: 0.85,
|
|
38
|
-
clothing_logic: 0.70, composition: 0.85
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const PARTIAL_HIT = {
|
|
42
|
-
silhouette_clarity: 0.70, palette_adherence: 0.70, material_fidelity: 0.70,
|
|
43
|
-
faction_read: 0.50, wear_level: 0.75, style_consistency: 0.75,
|
|
44
|
-
clothing_logic: 0.55, composition: 0.75
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// ─── Subject classifications ──────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
// Strong hits — approved with high scores
|
|
50
|
-
const STRONG_HITS = new Set([
|
|
51
|
-
'keth_molt_before', 'keth_molt_emerging', 'keth_hivesong', 'keth_pheromone_library',
|
|
52
|
-
'veshan_forge_trial', 'veshan_forge_trial_judgment',
|
|
53
|
-
'orryn_sealed_mantle', 'orryn_crystal_inheritance',
|
|
54
|
-
'thresh_young',
|
|
55
|
-
'mire_at_work',
|
|
56
|
-
'vaelk_proxy_market',
|
|
57
|
-
'architect_jump_gate', 'architect_archive_node', 'architect_ruin_surface',
|
|
58
|
-
'drift_kelp_bed',
|
|
59
|
-
'translator_rig'
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
// Partial hits — approved with moderate scores
|
|
63
|
-
const PARTIAL_HITS = new Set([
|
|
64
|
-
'veshan_battle_opera', 'veshan_scale_reader',
|
|
65
|
-
'orryn_drift_parliament', 'orryn_drift_parliament_failed',
|
|
66
|
-
'drift_kelp_ambush',
|
|
67
|
-
'vaelk_proxy_trade',
|
|
68
|
-
'station_climate_border',
|
|
69
|
-
'pidgin_trade_sign'
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
// Misses — rejected with explanations
|
|
73
|
-
const MISS_EXPLANATIONS = {
|
|
74
|
-
thresh_old_station: {
|
|
75
|
-
explanation: "Thresh crystalline organism not visible — rendered as standard station corridor with human. Need to foreground the crystalline growth formation.",
|
|
76
|
-
failures: ["species_not_rendered", "concept_miss"],
|
|
77
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.5, material_fidelity: 0.6, faction_read: 0.1, wear_level: 0.7, style_consistency: 0.7, clothing_logic: 0.3, composition: 0.6 }
|
|
78
|
-
},
|
|
79
|
-
smuggler_cant_meeting: {
|
|
80
|
-
explanation: "All figures rendered as human — no multi-species element. The cross-species communication concept requires at least 2 different species visible.",
|
|
81
|
-
failures: ["species_not_rendered", "concept_miss"],
|
|
82
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.6, material_fidelity: 0.7, faction_read: 0.1, wear_level: 0.7, style_consistency: 0.7, clothing_logic: 0.5, composition: 0.7 }
|
|
83
|
-
},
|
|
84
|
-
mire_reforming: {
|
|
85
|
-
explanation: "Green-tinted corridor with organic matter but no clear colonial organism reforming. The aggregation process needs to be the focal point.",
|
|
86
|
-
failures: ["concept_miss"],
|
|
87
|
-
scores: { silhouette_clarity: 0.5, palette_adherence: 0.6, material_fidelity: 0.6, faction_read: 0.3, wear_level: 0.7, style_consistency: 0.7, clothing_logic: 0.3, composition: 0.6 }
|
|
88
|
-
},
|
|
89
|
-
mire_hull_breach: {
|
|
90
|
-
explanation: "Rendered as military cockpit scene — no Mire organism flowing through hull breach. Complete concept miss.",
|
|
91
|
-
failures: ["species_not_rendered", "concept_miss"],
|
|
92
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.4, material_fidelity: 0.5, faction_read: 0.0, wear_level: 0.6, style_consistency: 0.5, clothing_logic: 0.3, composition: 0.6 }
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Reject examples — always rejected
|
|
97
|
-
const REJECT_EXPLANATIONS = {
|
|
98
|
-
REJECT25_clean_alien: {
|
|
99
|
-
explanation: "Clean sterile alien civilization — Star Trek utopia aesthetic. Violates every grit principle.",
|
|
100
|
-
failures: ["too_clean", "generic_scifi"],
|
|
101
|
-
scores: { silhouette_clarity: 0.7, palette_adherence: 0.1, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.0, style_consistency: 0.3, clothing_logic: 0.2, composition: 0.7 }
|
|
102
|
-
},
|
|
103
|
-
REJECT25_fantasy_alien: {
|
|
104
|
-
explanation: "Fantasy wizard alien with robes and magic staff — high fantasy, not grounded sci-fi.",
|
|
105
|
-
failures: ["wrong_genre", "costume_not_clothes"],
|
|
106
|
-
scores: { silhouette_clarity: 0.6, palette_adherence: 0.1, material_fidelity: 0.2, faction_read: 0.0, wear_level: 0.1, style_consistency: 0.1, clothing_logic: 0.1, composition: 0.6 }
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// ─── Process ──────────────────────────────────────────
|
|
111
|
-
|
|
112
|
-
const wave25Files = readdirSync(CANDIDATES_DIR).filter(f =>
|
|
113
|
-
f.endsWith('.png') && readdirSync(RECORDS_DIR).some(r => {
|
|
114
|
-
try {
|
|
115
|
-
const rec = JSON.parse(readFileSync(join(RECORDS_DIR, r), 'utf-8'));
|
|
116
|
-
return rec.file === f && rec.lane === 'wave25-expanded-universe';
|
|
117
|
-
} catch { return false; }
|
|
118
|
-
})
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Fallback: match by known subject IDs
|
|
122
|
-
const ALL_SUBJECTS = [
|
|
123
|
-
...STRONG_HITS, ...PARTIAL_HITS,
|
|
124
|
-
...Object.keys(MISS_EXPLANATIONS),
|
|
125
|
-
...Object.keys(REJECT_EXPLANATIONS)
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
// Find wave25 records directly by filename match
|
|
129
|
-
const recordFiles = readdirSync(RECORDS_DIR).filter(r => r.endsWith('.json'));
|
|
130
|
-
const candidates = recordFiles.filter(rf => {
|
|
131
|
-
return ALL_SUBJECTS.some(subj => rf.startsWith(subj));
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
let approved = 0, rejected = 0, skipped = 0;
|
|
135
|
-
|
|
136
|
-
for (const recordFile of candidates) {
|
|
137
|
-
let record;
|
|
138
|
-
try {
|
|
139
|
-
record = JSON.parse(readFileSync(join(RECORDS_DIR, recordFile), 'utf-8'));
|
|
140
|
-
} catch { continue; }
|
|
141
|
-
|
|
142
|
-
const file = basename(record.asset_path || '');
|
|
143
|
-
const subjectId = record.id?.replace(/_v[12]$/, '') || file.replace(/_v[12]\.png$/, '');
|
|
144
|
-
|
|
145
|
-
// Already curated?
|
|
146
|
-
if (record.verdict) {
|
|
147
|
-
console.log(` ⏭ ${file} already curated (${record.verdict})`);
|
|
148
|
-
skipped++;
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
let verdict, scores, explanation, failures;
|
|
153
|
-
|
|
154
|
-
if (REJECT_EXPLANATIONS[subjectId]) {
|
|
155
|
-
const r = REJECT_EXPLANATIONS[subjectId];
|
|
156
|
-
verdict = 'rejected';
|
|
157
|
-
scores = r.scores;
|
|
158
|
-
explanation = r.explanation;
|
|
159
|
-
failures = r.failures;
|
|
160
|
-
} else if (MISS_EXPLANATIONS[subjectId]) {
|
|
161
|
-
const m = MISS_EXPLANATIONS[subjectId];
|
|
162
|
-
verdict = 'rejected';
|
|
163
|
-
scores = m.scores;
|
|
164
|
-
explanation = m.explanation;
|
|
165
|
-
failures = m.failures;
|
|
166
|
-
} else if (STRONG_HITS.has(subjectId)) {
|
|
167
|
-
verdict = 'approved';
|
|
168
|
-
scores = { ...STRONG_HIT };
|
|
169
|
-
explanation = `Strong hit — concept reads clearly, style consistent, gritty lived-in aesthetic.`;
|
|
170
|
-
failures = [];
|
|
171
|
-
} else if (PARTIAL_HITS.has(subjectId)) {
|
|
172
|
-
verdict = 'approved';
|
|
173
|
-
scores = { ...PARTIAL_HIT };
|
|
174
|
-
explanation = `Partial hit — atmosphere and style correct, some species anatomy or concept details missed.`;
|
|
175
|
-
failures = ['partial_species_accuracy'];
|
|
176
|
-
} else {
|
|
177
|
-
console.log(` ⚠ Unknown subject ${subjectId} for ${file}, skipping`);
|
|
178
|
-
skipped++;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Update record
|
|
183
|
-
record.verdict = verdict;
|
|
184
|
-
record.scores = scores;
|
|
185
|
-
record.explanation = explanation;
|
|
186
|
-
record.failures = failures;
|
|
187
|
-
record.curated_at = new Date().toISOString();
|
|
188
|
-
record.curator = 'wave25-script';
|
|
189
|
-
|
|
190
|
-
// Move file
|
|
191
|
-
const srcPath = join(CANDIDATES_DIR, file);
|
|
192
|
-
const dstDir = verdict === 'approved' ? APPROVED_DIR : REJECTED_DIR;
|
|
193
|
-
const dstPath = join(dstDir, file);
|
|
194
|
-
|
|
195
|
-
if (!DRY_RUN) {
|
|
196
|
-
writeFileSync(join(RECORDS_DIR, recordFile), JSON.stringify(record, null, 2));
|
|
197
|
-
if (existsSync(srcPath)) {
|
|
198
|
-
renameSync(srcPath, dstPath);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const icon = verdict === 'approved' ? '✓' : '✗';
|
|
203
|
-
console.log(` ${icon} ${file} → ${verdict}`);
|
|
204
|
-
if (verdict === 'approved') approved++;
|
|
205
|
-
else rejected++;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
console.log(`\nWave 25 curation: ${approved} approved, ${rejected} rejected, ${skipped} skipped`);
|
|
209
|
-
if (DRY_RUN) console.log(' (dry run — no files moved)');
|