@@ -56,6 +56,16 @@ const LIST_FIELD_SHAPES = {
5656 handGestures : '{ "name": "<gesture>", "description": "<the hand pose>" }' ,
5757} ;
5858
59+ // Non-visual character-framework fields (CWQE Phase 10, #2175). They live in the
60+ // shared STRING_FIELDS / LIST_FIELDS lists (the text/vision expand flows share
61+ // one source of truth) but a vision model CANNOT infer a character's Ghost, Lie,
62+ // Want, Need, or secrets from an image — those are narrative, not renderable. So
63+ // the vision prompt excludes them (they'd only invite hallucinated backstory,
64+ // and `secrets` has no LIST_FIELD_SHAPES row shape to emit). arcType / sliders
65+ // are already absent from both shared lists (merged specially in the text flow),
66+ // so they never reach the vision prompt either.
67+ const NON_VISUAL_FRAMEWORK_FIELDS = new Set ( [ 'ghost' , 'wound' , 'lie' , 'want' , 'need' , 'secrets' ] ) ;
68+
5969/**
6070 * Build the vision prompt. The model is told to return ONE JSON object keyed by
6171 * the structured character fields, populating only those it can infer from the
@@ -74,12 +84,16 @@ export function buildVisionExpandPrompt({ name, context, imageCount = 1, blankFi
7484 // Narrow the ask to the fields that are still blank when we have that list;
7585 // otherwise offer the full set. Populated fields are no-clobber server-side
7686 // regardless, so this is purely to focus the model.
87+ // Non-visual framework fields (ghost/lie/…/secrets) are never asked of a
88+ // vision model — filter them out of both the full-set and blank-narrowed asks.
89+ const visualStringFields = STRING_FIELDS . filter ( ( f ) => ! NON_VISUAL_FRAMEWORK_FIELDS . has ( f ) ) ;
90+ const visualListFields = LIST_FIELDS . filter ( ( f ) => ! NON_VISUAL_FRAMEWORK_FIELDS . has ( f ) ) ;
7791 const stringTargets = blankFields . length
78- ? STRING_FIELDS . filter ( ( f ) => blankFields . includes ( f ) )
79- : STRING_FIELDS ;
92+ ? visualStringFields . filter ( ( f ) => blankFields . includes ( f ) )
93+ : visualStringFields ;
8094 const listTargets = blankFields . length
81- ? LIST_FIELDS . filter ( ( f ) => blankFields . includes ( f ) )
82- : LIST_FIELDS ;
95+ ? visualListFields . filter ( ( f ) => blankFields . includes ( f ) )
96+ : visualListFields ;
8397
8498 const stringLines = stringTargets . map ( ( f ) => ` "${ f } ": "<string>"` ) . join ( ',\n' ) ;
8599 const listLines = listTargets
0 commit comments