fix(style): the style's generation prompt never reached the user - #47
Merged
Conversation
Picking a style stored `style_id` and lit a progress checkmark, and that was the whole effect. `prompt_template` and `negative_prompt` were seeded on every style row, declared on the `Style` type and shipped to the browser on every project page — GET /api/projects/[id] already joins `style:styles(*)` — and then read by nothing. Meanwhile the figures step told the user, in its own words, to "use an AI tool (Grok, Midjourney) to generate a styled version" without ever showing the prompt the style was written for. In Phase 1 the styled image is produced by hand and uploaded back, so that instruction is the actual handoff from app to human, and it was empty: every figure got whatever prompt its user recalled. Figures only merge into one scene when they were all generated the same way, so a prompt typed from memory per figure is a Ground Truth #3 failure, not a missing convenience. The figures step now shows the chosen style's prompt and negative prompt with copy buttons, and links to the style step when no style is chosen yet. The prompt is rendered exactly as stored — the seeded templates carry no `{placeholders}`, so there is no substitution to do and none is invented. Same shape as the orphaned /api/compositions route fixed in #25, one level down: there a route with no caller, here a column with no reader. Guarded narrowly rather than generically — a blanket "every modelled column is rendered" check would have to allowlist all of `Export`, `face_embedding` and `face_confidence`, which are deliberately unbuilt, so it would be noise. Per the never-twice rule this is the second sighting of the family: recorded as a pattern, automated only if it recurs. Verified: full root `verify` green (format + lint 0 errors/8 pre-existing warnings + tsc + 101 tests, up from 98). Production-parity `next build` green with CI's placeholder env. The new guard is mutation-verified — reverting the wiring fails all three of its assertions. Not verified: the panel rendered in a real browser against real data. Local `app/.env.selfhost.local` (git-ignored, untracked) points at hosted Supabase ref `ckpynkpsfnuqndplaapc`, which is NXDOMAIN from a public resolver, while production is built against self-hosted https://supabase.orangecat.ch and is healthy. Guest mode needs the service-role key, which this session does not have. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SZGjXtzLGk2q9zPUkA3vMf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Picking an art style stored
style_idand lit a progress checkmark. That was the entire effect.prompt_templateandnegative_promptare seeded on every style row, declared on theStyletype, and already shipped to the browser on every project page —GET /api/projects/[id]joinsstyle:styles(*). They were then read by nothing.Meanwhile the figures step told the user, in its own words:
…and never showed the prompt the style was written for. In Phase 1 the styled image is produced by hand and uploaded back, so that instruction is the handoff from app to human — and it was empty. Every figure got whatever prompt its user happened to recall.
Figures only merge into one scene when they were all generated the same way, so a per-figure prompt typed from memory is a Ground Truth #3 failure ("Style consistency across all figures… if any element feels pasted in, the illusion breaks"), not a missing convenience.
The fix
The figures step now shows the chosen style's prompt and negative prompt with copy buttons, and links to the style step when no style is chosen yet.
The prompt is rendered exactly as stored. The seeded templates carry no
{placeholders}despite the data-model doc saying they might, so there is no substitution to do and none is invented (YAGNI).No API, schema, or hook change — the data was already on the wire.
Why the guard is narrow
Same shape as the orphaned
/api/compositionsroute fixed in #25, one level down: there a route with no caller, here a column with no reader.A blanket "every modelled column is rendered somewhere" check would have to allowlist all of
Export,face_embeddingandface_confidence— deliberately unbuilt Phase 2/T2 surface — so it would be noise, not a gate. Per the never-twice rule this is the second sighting of the family: recorded as a pattern, automated only if it recurs.Verification
pnpm run verifygreen: format + lint (0 errors, 8 pre-existing warnings) + tsc + 101 tests, up from 98next buildgreen using CI's placeholder envNot verified: the panel rendered in a real browser against real data. Local
app/.env.selfhost.local(git-ignored, untracked) points at hosted Supabase refckpynkpsfnuqndplaapc, which is NXDOMAIN from a public resolver, while production is built against self-hostedhttps://supabase.orangecat.chand is healthy (GET /api/projects→{"success":true,...}). Guest mode needs the service-role key, which this session does not have.🤖 Generated with Claude Code
https://claude.ai/code/session_01SZGjXtzLGk2q9zPUkA3vMf